地理编码服务器
在之前的教程中,我们讲解了如何 设置开放街道地图瓦片服务器。本教程将向您展示如何设置 Nominatim 地理编码服务器。Nominatim 为开放街道地图提供搜索功能,因此,如果访问者在搜索框中输入地址,系统将返回该地址的经纬度位置。如果您想一步完成 Nominatim 地理编码服务和网页界面的设置,请参阅“带有网页界面的地理编码服务”指南。
1. 从源代码构建 Nominatim
安装构建 Nominatim 所需的依赖包。
sudo apt-get update
sudo apt install build-essential cmake g++ libboost-dev libboost-system-dev libboost-filesystem-dev liblua5.4-dev libexpat1-dev zlib1g-dev libbz2-dev libpq-dev libproj-dev php php-pgsql php-intl php-cgi phpunit php-codesniffer nlohmann-json3-dev python3-setuptools python3-dev python3-pip python3-psycopg2 python3-tidylib python3-behave python3-pytest php-cli python-babel-localedata python3-babel python3-datrie python3-jinja2 python3-markupsafe python3-psutil pylint git pandoc python3-argparse-manpage clang-tidy postgresql-server-dev-15
为避免数据导入过程中出现分词器错误,请同时安装 python3-icu:
sudo apt install python3-icu
创建名为 nominatim 的用户。(无需为此用户创建密码。)
sudo useradd -d /srv/nominatim -s /bin/bash -m nominatim
授予您自己的用户帐户权限。请将用户名替换为您的真实用户名。
sudo setfacl -R -m u:用户名:rwx /srv/nominatim/
切换到 /srv/nominatim/ 目录。
cd /srv/nominatim/
从官方网站下载 Nominatim。
wget https://nominatim.org/release/Nominatim-4.3.1.tar.bz2
如果您的服务器只有 IPv6,您可以将其下载到本地,然后再上传到服务器。
解压缩。
tar xvf Nominatim-4.3.1.tar.bz2
创建构建目录。mkdir build
切换到该目录并配置构建环境。cd build
cmake /srv/nominatim/Nominatim-4.3.1
-- C 编译器标识为 GNU 12.2.0
-- CXX 编译器标识为 GNU 12.2.0
-- 检测 C 编译器 ABI 信息
-- 检测 C 编译器 ABI 信息 - 完成
-- 检查 C 编译器是否可用:/usr/bin/cc - 已跳过
-- 检测 C 编译特性
-- 检测 C 编译特性 - 完成
-- 检测 CXX 编译器 ABI 信息
-- 检测 CXX 编译器 ABI 信息 - 完成
-- 检查 C++ 编译器是否可用:/usr/bin/c++ - 已跳过
-- 检测 CXX 编译特性
-- 检测 CXX 编译特性 - 完成
-- 找到 Git:/usr/bin/git(版本“2.39.5”)
-- 正在构建 osm2pgsql 1.9.2
-- 找到 ZLIB:/usr/lib/x86_64-linux-gnu/libz.so(版本“1.2.13”)
……
编译源代码。
make
[ 55%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/taginfo.cpp.o
[ 57%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/tagtransform-c.cpp.o
[ 59%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/tagtransform.cpp.o
[ 61%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/tile.cpp.o
[ 62%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/thread-pool.cpp.o
[ 64%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/util.cpp.o
[ 66%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/wildcmp.cpp.o
[ 67%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/wkb.cpp.o
[ 69%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-index.cpp.o
[ 71%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-table.cpp.o
[ 72%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-table-column.cpp.o
[ 74%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-lua-expire-output.cpp.o
[ 76%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-lua-geom.cpp.o
[ 77%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-lua-index.cpp.o
[ 79%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-lua-table.cpp.o
[ 81%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/flex-write.cpp.o
[ 83%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/geom-transform.cpp.o
[ 84%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/lua-setup.cpp.o
[ 86%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/lua-utils.cpp.o
[ 88%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/output-flex.cpp.o
[ 89%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/tagtransform-lua.cpp.o
[ 91%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/lua-init.cpp.o
[ 93%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/version.cpp.o
[ 94%] 正在构建 CXX 对象 osm2pgsql/src/CMakeFiles/osm2pgsql_lib.dir/reprojection-generic-proj6.cpp.o
[ 96%] Linking CXX static library libosm2pgsql.a
[ 96%] 目标 osm2pgsql_lib 已构建
[ 98%] 正在构建 CXX 对象 osm2pgsql/CMakeFiles/osm2pgsql.dir/src/osm2pgsql.cpp.o
[100%] 正在链接 CXX 可执行文件 osm2pgsql
[100%] 已构建目标 osm2pgsql
xtao@tile:/srv/nominatim/build$
安装 Nominatim。
sudo make install
xtao@tile:/srv/nominatim/build$ sudo make install
[sudo] xtao 的密码:
[ 96%] 目标 osm2pgsql_lib 已构建
[100%] 已构建目标 osm2pgsql
安装项目……
-- 安装配置:""
-- 安装: /usr/local/bin/nominatim
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/version.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/db_search_builder.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/legacy_tokenizer.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/query.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/db_search_fields.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/query_analyzer_factory.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/icu_tokenizer.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/geocoder.py
-- 安装: /usr/local/lib/nominatim/lib-python/nominatim/api/search/token_assignment.py
......
2. 导入 OSM 数据库
下载维基百科重要性转储文件,这将提高 Nomiatim 搜索结果的质量。cd /srv/nominatim/build/
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
再次提醒,下载链接需要 IPv4 地址。如有需要,您可以先将文件下载到本地,然后再上传到服务器。
安装所需的Python模块。
sudo apt install python3-dotenv python3-sqlalchemy python3-asyncpg
在 PostgreSQL 中创建 www-data 用户,以便网络服务器对数据库具有只读访问权限。
sudo -u postgres -i createuser www-data
授予 postgres 用户权限。sudo setfacl -R -m u:postgres:rwx /srv/nominatim/
现在您可以启动 screen 命令,因为接下来的进程可能需要很长时间。切换到 postgres 用户。
sudo -u postgres -i
运行以下命令将 OSM 提取数据导入 PostgreSQL。cd /srv/nominatim/build/
/usr/local/bin/nominatim import -j 6 --osm-file /path/to/the/map.osm.pbf 2>&1 | tee setup.log
它将在 PostgreSQL 中自动创建 nominatim 数据库,并使用 osm2pgsql 导入 OSM 文件。
示例输出:
使用项目目录: /srv/nominatim/build
创建数据库
设置国家表格
导入 OSM 数据文件
osm2pgsql 版本 1.9.2
数据库版本:15.14 (Debian 15.14-1.pgdg12+1)
PostGIS 版本: 3.6
将属性存储到表 public.osm2pgsql_properties 中。
读取输入文件耗时 133 秒(2 分 13 秒)
处理了 19208552 个节点,耗时 64 秒(1 分 4 秒)——速度为 300k/秒
处理了 2269941 条路径,耗时 64 秒(1 分 4 秒)——速度为 35k/秒
5 秒内处理了 38665 个关系 – 8k/秒
没有标记路径(跳过第 2 阶段)。
按几何形状对表“place”进行聚类…
表“place”上无需创建索引。
在表“place”上创建id索引…
分析表格“位置”……
表“planet_osm_nodes”的后处理已完成,耗时0秒。
在表“planet_osm_ways”上建立索引
表“planet_osm_ways”的后处理已完成,耗时29秒。
在表“planet_osm_rels”上建立索引
表“planet_osm_rels”的后处理已完成,耗时0秒。
对表“place”的所有后处理在 29 秒内完成。
将属性存储到表 public.osm2pgsql_properties 中。
osm2pgsql 总共耗时 194 秒(3 分 14 秒)。
导入维基百科重要性数据
导入次要重要性栅格数据
次要重要性文件未导入。将回退到默认排名。
创建函数(第一遍)
创建表
创建函数(第二遍)
创建表触发器
创建分区表
创建函数(第三遍)
初始化表
将数据加载到 placex 表中…………………………
数据加载过程可能需要长达 10 分钟,因此您可以按 Ctrl + A,然后按 d 键退出屏幕。屏幕可能会显示:
[已从 31226.pts-0.tile 分离]
您可以使用以下命令恢复之前的屏幕进程:screen -r 编号
例如,我的屏幕版本号是 31226,所以恢复屏幕的命令是:screen -r 31226
数据库导入完成后,索引过程将开始。总共有 30 个排名。
80 已完成 19894 个任务,平均速度为每秒 246.582 个任务 – 排名第 26,预计完成时间(秒):874.75
……
11 已完成 5720 个任务,耗时 11 分钟,平均速度为每秒 480.525 个任务 – 排名第 30,预计完成时间(秒):1017.74
……
78 已完成 45920 个任务,耗时 78 秒,平均速度 594.411 个/秒 - 排名 30 预计剩余时间(秒):754.10
……
736 已完成 5174 个任务,平均速度 7.029 个/秒 – 排名 0,预计剩余时间(秒):1973.56
……
92 完成 19488/19488,耗时 92 秒,平均速度 210.816 次/秒 – 完成,排名 0
开始插值线(location_property_osmline)(使用批处理大小 20)
……
开始使用 6 个线程对邮政编码进行索引
起始邮政编码(location_postcode)(使用批次大小 20)
……
0 已完成 706/706 个邮政编码(location_postcode),速度为每秒 962.839 个 - 邮政编码已完成
后处理表
创建默认国家/地区名称的搜索索引。
重新计算字数
在 /srv/nominatim/build/website 设置网站
完成后,运行以下命令完成索引并进行验证。
nominatim index
nominatim admin --check-database
postgres@tile:/srv/nominatim/build$ nominatim admin --check-database
2025-12-13 23:10:16: 使用项目目录: /srv/nominatim/build
2025-12-13 23:10:16: 正在检查数据库
正在检查数据库连接…… 正常
正在检查 placex 表…… 正常
正在检查 placex 内容…… 正常
正在检查分词器是否正常工作…… 正常
正在检查维基百科/维基数据…… 正常
正在检查索引状态…… 正常
正在检查数据库索引是否完整…… 正常
正在检查所有数据库索引是否有效…… 正常
正在检查 TIGER 外部数据表…… 不适用
postgres@tile:/srv/nominatim/build$
您可能会发现以下 nominatim 命令很有用。详细信息可从手册 (man nominatim) 中获取。
- nominatim freeze: 将数据库设置为只读
- nominatim replication: 使用在线复制服务更新数据库。
- nominatim add-data: 从文件或在线资源添加附加数据。
- nominatim index: 重新索引所有新增和修改的数据。
- nominatim admin: 分析和维护数据库。
退出postgres用户。
exit
3. 搭建网络服务器
编辑瓦片服务器配置文件。
sudo vi /etc/apache2/sites-enabled/tileserver_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
授予 www-data 用户权限。
sudo setfacl -R -m u:www-data:rx /srv/nominatim/
sudo chown www-data:www-data /var/www/html/ -R
丢弃动态更新所需的数据
Nominatim 数据库中大约一半的数据实际上并未用于 API 服务。这些数据仅用于动态更新开放街道地图数据。对于许多应用场景而言,这些动态更新并非必需。您可以使用以下命令删除动态部分:nominatim freeze
请注意,您仍然需要为初始导入预留足够的磁盘空间。因此,如果您计划稍后迁移数据库或重复使用该空间,则此选项尤其值得考虑。如何为滑动地图添加搜索功能
我假设你的滑动地图是用 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([38.9,-77.3],8);
L.tileLayer('http://tile.your-domain.com/osm/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map);
</script>
</body>
</html>
现在,您需要在 HTML 头部添加以下两行代码才能使用 Leaflet 开放街道地图地理编码插件。首先,从 GitHub (https://github.com/k4r573n/leaflet-control-osm-geocoder) 下载 OSMGeocoder 的 CSS 和 JavaScript 文件到您的网站文件夹。然后,将这两个文件添加到 <head> 部分。您还需要将 geocoder.png 文件下载到 images 文件夹中。
<link rel="stylesheet" href="Control.OSMGeocoder.css" />
<script src="Control.OSMGeocoder.js"></script>
然后将以下函数添加到 <script>...</script> 代码中,以便将搜索功能添加到您的地图中。
var osmGeocoder = new L.Control.OSMGeocoder();
map.addControl(osmGeocoder);
最终的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([38.9,-77.3],8);
L.tileLayer('https://tile.yourdomain.com/osm/{z}/{x}/{y}.png',{maxZoom:18}).addTo(map);
var osmGeocoder = new L.Control.OSMGeocoder();
map.addControl(osmGeocoder);
</script>
</body>
</html>
保存并关闭文件。然后在浏览器中重新加载地图,您应该会在右上角看到一个搜索按钮。
默认情况下,Leaflet 开放街道地图地理编码控件 使用公共的 https://nominatim.openstreetmap.org 地理编码服务。要使其使用您自己的 Nominatim 地理编码服务,请修改 Control.OSMGeocoder.js 中的第 178 行:
var url = protocol + "//nominatim.openstreetmap.org/search" + L.Util.getParamString(params),
请将 URL 替换为您的 Nominatim 地理编码服务的链接。
var url = protocol + "//tile.yourdomain.com/nominatim/search" + L.Util.getParamString(params),
您还可以添加以下代码进行反向地理编码。当访客点击地图上的某个点时,就会显示该地址的名称。
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));
});
保存并关闭文件。然后在浏览器中重新加载地图。
如何更新 Nominatim 数据库
启动屏幕。切换到 postgres 用户。
sudo -u postgres -i
删除 nominatim 数据库。dropdb nominatim;
下载维基百科重要性数据文件。cd /srv/nominatim/build/
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
下载地图:wget http://download.geofabrik.de/north-america/us/maryland-latest.osm.pbf
运行以下命令将 OSM 提取数据导入 PostgreSQL。/usr/local/bin/nominatim import -j 6 --osm-file /path/to/the/map.osm.pbf 2>&1 | tee setup.log
完成后,运行以下命令完成索引并进行验证。nominatim index
nominatim admin --check-database
您还可以彻底清理数据库以释放空间:
vacuumdb -fz nominatim
退出 postgres 用户。exit
启用 HTTP2
安装过程中,我们安装了 mod_php 模块,该模块与 HTTP2 不兼容。因此,您必须先禁用该模块,才能使 HTTP2 正常工作。sudo a2dismod php8.2
sudo a2dismod mpm_prefork
sudo apt-get purge libapache2-mod-php8.2 # 或者直接执行 'sudo apt-get purge libapache2-mod-php*'
安装 PHP-FPM:sudo apt update
sudo apt install php8.2-fpm
sudo a2enmod mpm_event
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.2-fpm
- mpm_event:使用异步、非阻塞进程,使其在现代网络服务中非常高效。
- PHP-FPM(FastCGI 进程管理器):一个独立的 PHP 进程管理器,它与 Apache(通过 mod_proxy_fcgi)通信,使 Apache 能够继续使用 mpm_event 并高效地处理 PHP 请求,而无需 mod_php 的开销。
- mod_php(避免使用):将 PHP 直接绑定到 Apache 进程中,强制使用 mpm_prefork,这会导致可扩展性较差且占用更多内存。
离线和在线地理编码
Nominatim 4.3 同时支持在线和 离线地理编码。您可以直接在 Nominatim 服务器上获取离线地理编码结果:sudo -u postgres -i
nominatim search --query "LeFrak"
2026-01-28 20:33:40: 使用项目目录:/var/lib/postgresql
[
{
"地点 ID": 504952,
“licence”:“数据 © OpenStreetMap 贡献者,ODbL 1.0。http://osm.org/copyright”
"osm 类别": "way",
"osm ID": 314901003,
"纬度": "38.98366755",
"经度": "-76.94365744815555",
“类别”: “建筑物”,
"类型": "大学",
"地点等级": 30,
“重要性”:9.99999999995449e-06,
"地址类别": "建筑物",
"名字": "LeFrak Hall",
"显示名字": "LeFrak Hall, 7251, Preinkert Drive, Lord Calvert Manor, Calvert Hills, College Park,
美国马里兰州乔治王子县,邮编20742”
"外包矩形": [
“38.9835104,”
“38.9838799,
“-76.9442060”
“-76.9431145”
]
}
]
您可以使用 curl 进行在线查询:
curl "https://tile.umd.me.uk/nominatim/search?q=LeFrak"
除了名称字段外,它返回的结果几乎相同,因为 Nominatim 4.3 中的在线查询是由 PHP 而不是 Python 处理的。