地理编码服务器

在之前的教程中,我们讲解了如何 设置开放街道地图瓦片服务器。本教程将向您展示如何设置 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

请替换 PostgreSQL 为合适的版本。

为避免数据导入过程中出现分词器错误,请同时安装 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) 中获取。

退出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

清除旧的 PHP 模块包:

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

启用正确的多进程模块(MPM,建议 HTTP/2 使用事件模式):

sudo a2enmod mpm_event
sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php8.2-fpm

为什么这种方法有效:

离线和在线地理编码

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 处理的。

下一步

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