带有网页界面的地理编码服务器
在之前的教程中,我们讲解了如何 设置开放街道地图瓦片服务器。本教程将向您展示如何设置带有网页界面的地理编码服务器。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
创建名为 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');
?>
上述配置定义了
- Nominatim 实例相对于您的瓦片服务器的路径。
- 默认经纬度和缩放级别。
- 您的瓦片服务器链接。默认情况下,Nominatim 使用公共的 https://tile.openstreetmap.org 瓦片服务器。这里我使用的是我自己的瓦片服务器。
@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 将自动重新计算路线。