带有网页界面的地理编码服务器

在之前的教程中,我们讲解了如何 设置开放街道地图瓦片服务器。本教程将向您展示如何设置带有网页界面的地理编码服务器。Nominatim 为开放街道地图提供搜索功能,因此,如果访问者在搜索框中输入地址,系统将返回该地址的经纬度位置。如果您希望同时设置在线和离线地理编码服务, 请参阅另一篇教程

1. 从源代码构建 Nominatim

安装构建 Nominatim 所需的依赖包。

sudo apt-get update
sudo apt install build-essential cmake g++ libboost-dev libboost-system-dev libboost-filesystem-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev php php-pgsql php-intl php-cgi phpunit php-codesniffer python3-setuptools python3-dev python3-pip python3-psycopg2 python3-tidylib python3-behave python3-pytest pylint git clang-tidy postgresql-server-dev-13

该安装程序已在 Debian 11 和 Ubuntu 20.04 上测试通过。其他系统可能需要进行一些小的修改。

创建名为 nominatim 的用户。(无需为此用户创建密码。)

sudo useradd -d /srv/nominatim -s /bin/bash -m nominatim

授予您自己的用户帐户权限。请将用户名替换为您的真实用户名。

sudo setfacl -R -m u:username:rwx /srv/nominatim/

切换到 /srv/nominatim/ 目录。

cd /srv/nominatim/

从官方网站下载 Nominatim。

wget https://nominatim.org/release/Nominatim-3.5.2.tar.bz2

解压缩。

tar xvf Nominatim-3.5.2.tar.bz2

创建构建目录。

mkdir build

切换到该目录并配置构建环境。

cd build
cmake /srv/nominatim/Nominatim-3.5.2

编译源代码。

make

2. 配置 Nominatim

Nominatim 的默认配置文件是 /srv/nominatim/build/settings/settings.php。我们可以创建一个 local.php 文件,并在其中添加我们的修改。

sudo vi /srv/nominatim/build/settings/local.php

在文件中添加以下几行。
<?php
 @define('CONST_Website_BaseURL', '/nominatim/');
 @define('CONST_Default_Lat', 32);
 @define('CONST_Default_Lon', 117);
 @define('CONST_Default_Zoom', 7);
 @define('CONST_Map_Tile_URL', 'https://t.umd.me.uk/osm/{z}/{x}/{y}.png');

?>
        
上述配置定义了 您还可以查看 `/srv/nominatim/build/settings/settings.php` 文件,并根据需要添加自定义设置。例如,如果您要导入大型数据集(例如欧洲、北美、全球等),建议启用节点位置的扁平化存储,这样节点坐标将存储在一个简单的文件中,而不是数据库中,从而节省导入时间和磁盘空间。

@define('CONST_Osm2pgsql_Flatnode_File', '/srv/nominatim/flatnode.file');

3. 导入 OSM 数据库

下载维基百科重要性转储文件,这将提高 Nomiatim 搜索结果的质量。

cd /srv/nominatim/Nominatim-3.5.2/data
wget https://www.nominatim.org/data/wikimedia-importance.sql.gz

下载美国和英国邮政编码数据。

wget https://www.nominatim.org/data/us_postcode_data.sql.gz
wget https://www.nominatim.org/data/gb_postcode_data.sql.gz

下载国家代码数据文件。

wget -O country_osm_grid.sql.gz https://www.nominatim.org/data/country_grid.sql.gz

然后您需要下载一个 OSM 文件并将其导入 PostgreSQL。您可以访问 http://download.geofabrik.de 下载所需的提取文件。您也可以使用在瓦片服务器设置过程中用到的 PBF 文件。

在 PostgreSQL 中创建 www-data 用户,以便网络服务器对数据库具有只读访问权限。

sudo -u postgres -i createuser www-data

授予 postgres 用户权限。

sudo setfacl -R -m u:postgres:rwx /srv/nominatim/

切换到 postgres 用户。

sudo -u postgres -i

运行以下命令将 OSM 提取数据导入 PostgreSQL。

cd /srv/nominatim/build/
/srv/nominatim/build/utils/setup.php --osm-file /srv/nominatim/build/anhui-latest.osm.pbf --all 2>&1 | tee setup.log

它将在 PostgreSQL 中自动创建 nominatim 数据库,并使用 osm2pgsql 导入 OSM 文件。

示例输出:

处理:节点(25848k,241.6k/秒)路径(2960k,18.85k/秒)关系(22670,1133.50/秒)解析时间:284秒
节点统计:总计(25848659),最大值(13039901645),耗时107秒
路径统计:总计(2960045),最大(1418977325),耗时157秒
关系统计:总计(25417),最大值(19412309),用时20秒
停止表:planet_osm_nodes
表 planet_osm_nodes 已停止,耗时 0 秒。
停止表:planet_osm_ways
表 planet_osm_ways 已停止,耗时 0 秒
停止表:planet_osm_rels
在表 planet_osm_rels 上建立索引
表 planet_osm_rels 已停止,耗时 1 秒
Osm2pgsql 总耗时 287 秒。
节点缓存:已存储:25848659(100.00%),存储效率:53.25%(密集块:1170,稀疏节点:19478552),命中率:100.00%
2025-08-03 22:27:21 == 创建函数
2025-08-03 22:27:22 == 创建表
2025-08-03 22:27:23 == 创建函数
2025-08-03 22:27:23 == 创建表
2025-08-03 22:27:23 == 创建分区表
2025-08-03 22:27:34 == 创建分区函数
2025-08-03 22:27:35 == 正在导入维基百科文章和重定向
2025-08-03 22:35:43 == 删除旧数据 …………………….
2025-08-03 22:35:47 == 正在加载单词列表
总数
-------------
162480
(1 行)
总数
-------------
26438
(1 行)
2025-08-03 22:36:47 == 加载数据
2025-08-03 22:41:43 == 重新分析数据库
最新数据导入自 2025-07-31T18:35:09Z。
2025-08-03 22:43:50 == 计算邮政编码
        

数据库导入完成后,索引过程将开始。总共有 30 个排名。

完成后,运行以下命令进行验证。

/srv/nominatim/build/utils/check_import_finished.php

数据库已创建 ... 成功
nominatim.so 模块已安装 ... 成功
位置表已安装 ... 成功
索引状态已安装 ... 成功
创建搜索索引
正在检查索引 idx_word_word_id ... 正常
正在检查索引 idx_place_addressline_address_place_id ... 正常
正在检查索引 idx_placex_rank_search ... 正常
正在检查索引 idx_placex_rank_address ... 正常
正在检查索引 idx_placex_pendingsector ... 正常
正在检查索引 idx_placex_parent_place_id ... 正常
正在检查索引 idx_placex_geometry_reverse_lookuppoint ... 正常
正在检查索引 idx_placex_geometry_reverse_lookuppolygon ... 正常
正在检查索引 idx_placex_geometry_reverse_placenode ... 正常
正在检查索引 idx_location_area_country_place_id ... 正常
正在检查索引 idx_osmline_parent_place_id ... 正常
正在检查索引 idx_osmline_parent_osm_id ... 正常
正在检查索引 idx_place_osm_unique ... 正常
正在检查索引 idx_postcode_id ... 正常
正在检查索引 idx_postcode_postcode ... 正常
正在检查索引 idx_search_name_nameaddress_vector ... 正常
正在检查索引 idx_search_name_name_vector ... 正常
正在检查索引 idx_search_name_centroid ... 正常
正在检查搜索索引是否有效 ... 正常
        

退出postgres用户。

exit

4. 搭建网络服务器

编辑瓦片服务器配置文件。

sudo vi /etc/apache2/sites-enabled/tiles_site-le-ssl.conf

在 VirtualHost 标签之间添加以下几行代码。
        <Directory "/srv/nominatim/build/website">
          Options FollowSymLinks MultiViews
          AddType application/json   .php
          DirectoryIndex search.php
          Require all granted
        </Directory>
        alias /nominatim /srv/nominatim/build/website
        

保存并关闭文件。然后重新加载Apache。

sudo systemctl reload apache2

现在访问 https://tile.yourdomain.com/nominatim,您将看到您的 Nomiatim 实例。

5. 为滑动地图添加搜索功能

我假设你的滑动地图是用 Leaflet JavaScript 库显示的。要为地图添加搜索功能,你需要使用 Leaflet 地理编码插件. 我会向您展示如何使用 Leaflet 地理编码控件。其实非常简单。

假设您使用以下 HTML 代码来显示您的滑动地图。

    <html>
    <head>
    <meta charset="UTF-8">
    <title>我的开放街道地图</title>
    <link rel="stylesheet" type="text/css" href="html/leaflet.css"/>
    <script src="html/leaflet.js"></script>
    <style>
       html{height:100%;}
       body{height:100%;}
       
       #map{width:100%;height:100%}
    </style>
    </head>

    <body>
      <div id="map"></div>
      <script>
        var map = L.map('map').setView([32,117],7);
        L.tileLayer('http://tile.your-domain.com/osm/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map);
      </script>
    </body>
    </html>
        

现在,您需要在 HTML 头部添加以下两行代码才能使用 Leaflet 开放街道地图地理编码插件。首先,从 cdnjs (https://cdnjs.com/libraries/perliedman-leaflet-control-geocoder/2.4.0) 下载地理编码 的 CSS 和 JavaScript 文件到您的网站文件夹。然后,将这两个文件添加到 <head> 部分。

<link rel="stylesheet" href="Control.Geocoder.min.css" />
<script src="Control.Geocoder.min.js"></script>

然后将以下函数添加到 <script>...</script> 代码中,以便将搜索功能添加到您的地图中。

L.Control.geocoder().addTo(map);

最终的HTML代码如下所示:

    <html>
        <head>
            <meta charset="UTF-8">
            <title>My first osm</title>
            <link rel="stylesheet" type="text/css" href="html/leaflet.css"/>  
            <link rel="stylesheet" href="Control.OSMGeocoder.css " />
            <script src="html/leaflet.js"></script>
            <script src="Control.OSMGeocoder.js"></script>
            <style>
               html{height:100%;}
               body{height:100%;}
               #map{width:100%;height:100%;}
            </style>
        </head>

        <body>
            <div id="map"></div>
            <script>
               var map = L.map('map').setView([32,117],7);
               L.tileLayer('https://tile.yourdomain.com/osm/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map); 
               L.Control.geocoder().addTo(map);
            </script>
       </body>
    </html>
        

保存并关闭文件。然后在浏览器中重新加载地图,您应该会在右上角看到一个搜索按钮。

默认情况下,Leaflet 地理编码控件 使用公共的 https://nominatim.openstreetmap.org 地理编码服务。要使其使用您自己的 Nominatim 地理编码服务,请删除以下行:

L.Control.geocoder().addTo(map);

请添加以下几行代码。将 URL 替换为您的 Nominatim 地理编码服务的 URL。请注意,不要省略末尾的斜杠。

var geocoder = L.Control.Geocoder.nominatim({serviceUrl:'https://tile.yourdomain.com/nominatim/'});
var control = L.Control.geocoder({
placeholder: 'Search here...',
geocoder: geocoder,
position: 'topright'
}).addTo(map);

您还可以添加以下代码进行反向地理编码。当访客点击地图上的某个点时,就会显示该地址的名称。

map.on('click', function(e) {
    var lat = e.latlng.lat;
    var lon = e.latlng.lng;
    var lonmap = lon;       /* leaflet 中的横坐标可能大于 180,因此需要缩小 */
    while (lonmap > 180) {
        lonmap -= 360;
    }
while (lonmap < -180) { lonmap += 360; } var nominatim1 = 'https://tile.yourdomain.com/nominatim/reverse?format=json&lat='; var xhttp = new XMLHttpRequest(); xhttp.addEventListener("load", function(event) { var myobj = JSON.parse(event.target.response); if (myobj && myobj.address) { var address = myobj.display_name; L.marker([lat, lon]).addTo(map) .bindPopup(address) .openPopup(); } else { console.log('地址未找到。'); } }); // 设置我们的请求 xhttp.open("GET", nominatim1 + lat + '&lon=' + lonmap); xhttp.send(); console.log(lat.toFixed(6) + ", " + lon.toFixed(6)); });

保存并关闭文件。然后在浏览器中重新加载地图。

6. 集成路径规划和搜索功能

我假设您的滑动地图是用 Leaflet JavaScript 库显示的,并且您已将 Nominatim 地理编码服务添加到您的滑动地图中。

要将路径规划与搜索功能集成,我们可以使用名为 Leaflet Routing Machine 的插件。首先,将 Leaflet routing machine 的 JavaScript 和 CSS 文件添加到您的滑动地图中。请注意,它们应该放在 Leaflet 主 JavaScript 文件之后。

下载 leaflet-routing-machine.css、 leaflet-routing-machine.min.js、 leaflet.routing.icons.png 和 routing-icon.png 到 dist 文件夹。

sudo mkdir /var/www/html/dist
cd /var/www/html/dist
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.css
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet-routing-machine.js
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/leaflet.routing.icons.png
sudo wget https://unpkg.com/leaflet-routing-machine@latest/dist/routing-icon.png

        <html>
          <head>
             ....
             ....
             <link rel="stylesheet" href="dist/leaflet-routing-machine.css" />
             <script src="dist/leaflet-routing-machine.js"></script>
         </head>
         <body>
         ....
         ....
         </body>
        </html>
        
        

接下来,将以下几行代码添加到 <script>...</script> HTML 正文中的代码片段中。

L.Routing.control({
serviceUrl: 'https://osrm.your-domain.com/route/v1',
geocoder: L.Control.Geocoder.nominatim({serviceUrl:'https://tile.your-domain.com/nominatim/'}),
reverseWaypoints: true,
routeWhileDragging: true
}).addTo(map);

像这样:
        <html>
          <head>
             ....
             ....
             <link rel="stylesheet" href="dist/leaflet-routing-machine.css" />
             <script src="dist/leaflet-routing-machine.js"></script>
         </head>
         <body>
           <div id="map"></div>
             <script>
             ....
             ....
             L.Routing.control({
                serviceUrl: 'https://osrm.your-domain.com/route/v1',
                geocoder: L.Control.Geocoder.nominatim({serviceUrl:'https://tile.your-domain.com/nominatim/'}),
                reverseWaypoints: true,
                routeWhileDragging: true
             }).addTo(map);
             </script>

         </body>
        </html>
        

保存并关闭文件。 然后在浏览器中重新加载地图,你应该会在右上角看到一个控制面板,您可以在其中输入起点地址和终点地址。

您可以拖动地图上的航点, OSRM 将自动重新计算路线。

下一步

希望这一系列教程能帮助您搭建开放街道地图瓦片、路径规划和地理编码服务器。您可能还需要为您的域名搭建一个邮件服务器。