PostGIS 与 pgRouting:打造强大的地理空间分析容器

背景

在现代地理信息系统 (GIS) 和基于位置的服务中,PostGIS 和 pgRouting 是两个不可或缺的开源工具。PostGIS 为 PostgreSQL 数据库提供了强大的空间数据处理能力,而 pgRouting 则在此基础上增加了高级的路径规划和网络分析功能。通过 Docker 将它们容器化,可以极大地简化部署和管理过程。本文将介绍 PostGIS 和 pgRouting 的基本概念、用途,并提供一个 Dockerfile 示例来构建一个包含这两个扩展的 PostgreSQL 镜像。

一、. PostGIS 是什么,pgRouting 又是什么?

PostGIS

PostGIS 是 PostgreSQL 对象关系数据库的一个扩展,它允许 PostgreSQL 数据库管理和查询地理对象。简而言之,PostGIS 为 PostgreSQL 增加了对空间数据类型(如点、线、面、栅格等)的支持,以及一系列用于空间分析和处理的空间函数。这使得 PostgreSQL 成为了一个功能完备的空间数据库,能够存储、索引和查询大规模的地理空间数据。

主要特性包括:

  • 支持多种矢量和栅格数据类型。
  • 符合 Open Geospatial Consortium (OGC) 的 SQL 实现标准。
  • 提供丰富的空间操作和分析函数(例如,距离计算、缓冲区分析、几何对象关系判断等)。
  • 强大的空间索引能力,优化查询性能。

pgRouting

pgRouting 是 PostGIS/PostgreSQL 的另一个扩展,它专注于网络分析和路径规划。基于 PostGIS 存储的地理网络数据(如道路网、河流网络等),pgRouting 可以执行复杂的路由算法,例如:

  • Dijkstra 算法: 查找单源最短路径。
  • A* 算法: 一种启发式搜索算法,用于更高效地查找最短路径。
  • K-Shortest Path (KSP): 查找 K 条最短路径。
  • Traveling Salesperson Problem (TSP): 旅行商问题求解。
  • Driving Distance: 计算可达区域。
    pgRouting 使得开发者可以在数据库层面直接进行路径计算和网络分析,而无需将数据导出到外部工具。

二. 它们的用途

PostGIS 的用途

PostGIS 的应用场景非常广泛,主要包括:

  • 空间数据存储与管理: 作为企业级地理数据库,集中存储和管理各类地理空间数据。
  • Web 地图服务: 为在线地图应用(如 Leaflet, OpenLayers, Mapbox GL JS)提供后端数据支持。
  • 地理信息系统 (GIS) 分析: 进行复杂的空间分析,如环境影响评估、城市扩张研究、资源分布分析等。
  • 位置服务 (LBS): 开发基于用户位置的应用,如“附近的餐馆”、“导航服务”等。
  • 城市规划与基础设施管理: 辅助规划道路、管线、公共设施等。
  • 自然资源管理: 监测森林、水资源、土地利用变化等。

pgRouting 的用途

pgRouting 主要用于解决与网络和路径相关的问题:

  • 导航系统: 为汽车、行人和自行车提供最优路径规划。
  • 物流与配送: 优化车辆的配送路线,提高效率,降低成本。
  • 交通网络分析: 分析交通流量、瓶颈路段、可达性等。
  • 应急响应: 规划消防车、救护车等紧急车辆的最佳出动路径。
  • 公共交通规划: 分析公交线路覆盖范围和效率。
  • 旅游路线推荐: 根据用户偏好和景点网络生成推荐游览路线。

三.如何封装 Dockerfile

编写Dockerfile 文件

以下是一个用于构建包含 PostGIS 和 pgRouting 的 PostgreSQL 镜像的 Dockerfile。这个 Dockerfile 基于 postgres:15-alpine3.18 镜像,并安装了 PostGIS 3.4.2 和 pgRouting 3.7.1

CNB代码仓库https://cnb.cool/srebro/docker-images/-/tree/main/Middleware/postgres

参考https://github.com/postgis/docker-postgis/blob/master/15-3.4/alpine/Dockerfilehttps://github.com/postgis/docker-postgis/issues/399

#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "make update"! PLEASE DO NOT EDIT IT DIRECTLY.
#

ARG BASE_IMAGE=postgres:15-alpine3.18

FROM ${BASE_IMAGE}

LABEL maintainer="PostGIS Project - https://postgis.net" \
      org.opencontainers.image.description="PostGIS 3.4.2 spatial database extension with PostgreSQL 15 Alpine" \
      org.opencontainers.image.source="https://github.com/postgis/docker-postgis"

ENV POSTGIS_VERSION 3.4.2
ENV POSTGIS_SHA256 17aa8760a5c4fcb9a1fdc750c1c9aca0198a35dd1e320628064c43f178eefed2
ENV PGROUTING_VERSION 3.7.1


RUN set -eux \
    # 使用国内镜像源
    # && sed -i 's/dl-cdn.alpinelinux.org/mirrors.nju.edu.cn/g' /etc/apk/repositories \
    && sed -i 's@dl-cdn.alpinelinux.org@mirrors.cloud.tencent.com@g' /etc/apk/repositories \
    # 更新并安装依赖
    && apk update && apk upgrade \
    && apk add --no-cache \
        boost-dev \
        cmake \
        ca-certificates \
        openssl \
        tar \
        wget \
        gdal-dev \
        geos-dev \
        proj-dev \
        proj-util \
        sfcgal-dev \
        autoconf \
        automake \
        cunit-dev \
        file \
        g++ \
        gcc \
        gettext-dev \
        git \
        json-c-dev \
        libtool \
        libxml2-dev \
        make \
        pcre2-dev \
        perl \
        protobuf-c-dev \
    && apk add --no-cache --virtual .build-deps \
        $DOCKER_PG_LLVM_DEPS \
    # 下载pgrouting源代码
    && wget -O pgrouting.tar.gz "https://cnb.cool/srebro/docker-images/-/releases/download/V1.0.0/pgrouting-${PGROUTING_VERSION}.tar.gz" \
    && echo "Extracting pgrouting" \
    && tar -xvzf pgrouting.tar.gz \
    && cd pgrouting-* \
    && mkdir build \
    && cd build \
    && cmake -DCMAKE_CXX_FLAGS="-include cstdint" .. \
    && make -j4 \
    && make install \
    && cd ../.. \
    && rm -rf pgrouting* \
    # 下载和校验 PostGIS 源代码
    && wget -O postgis.tar.gz "https://cnb.cool/srebro/docker-images/-/releases/download/V1.0.0/postgis-${POSTGIS_VERSION}.tar.gz" \
    && echo "${POSTGIS_SHA256} *postgis.tar.gz" | sha256sum -c - \
    && mkdir -p /usr/src/postgis \
    && tar --extract --file postgis.tar.gz --directory /usr/src/postgis --strip-components 1 \
    && rm postgis.tar.gz \
    # 构建和安装 PostGIS
    && cd /usr/src/postgis \
    && gettextize \
    && ./autogen.sh \
    && ./configure --enable-lto \
    && make -j$(nproc) \
    && make install \
    # 刷新 proj 数据并执行回归测试
    && projsync --system-directory --file ch_swisstopo_CHENyx06_ETRS \
    && projsync --system-directory --file us_noaa_eshpgn \
    && projsync --system-directory --file us_noaa_prvi \
    && projsync --system-directory --file us_noaa_wmhpgn \
    && mkdir /tempdb \
    && chown -R postgres:postgres /tempdb \
    && su postgres -c 'pg_ctl -D /tempdb init' \
    && su postgres -c 'pg_ctl -D /tempdb start -l /tmp/logfile -o "-F"' \
    && cd regress \
    && make -j$(nproc) check RUNTESTFLAGS=--extension PGUSER=postgres \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS postgis;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS postgis_raster;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS postgis_sfcgal;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS address_standardizer;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS address_standardizer_data_us;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;"' \
    && su postgres -c 'psql -c "CREATE EXTENSION IF NOT EXISTS pgrouting;"' \
    && su postgres -c 'pg_ctl -D /tempdb --mode=immediate stop' \
    && rm -rf /tempdb /tmp/logfile \
    # 安装运行时依赖
    && apk add --no-cache \
        gdal \
        geos \
        proj \
        sfcgal \
        json-c \
        libstdc++ \
        pcre2 \
        protobuf-c \
        ca-certificates \
    # 清理构建依赖
    && apk del .build-deps \
    && rm -rf /usr/src/postgis /var/cache/apk/*

COPY ./initdb-postgis.sh /docker-entrypoint-initdb.d/10_postgis.sh
COPY ./update-postgis.sh /usr/local/bin

构建镜像

docker build -t XXXX -f Dockerfile .

或者使用我构建好的镜像: docker.cnb.cool/srebro/docker-images/postgis:15.5-3.4

四.使用docker-compose 运行容器

1、创建目录
mkdir -p /home/application/Database/postgres/{data,init}


2、创建docker-compose.yaml 文件,见 docker-compose.yaml 文件
vim /home/application/Database/postgres/docker-compose.yml 

version: "3"

services:
  postgres:
    container_name: postgres
    image: docker.cnb.cool/srebro/docker-images/postgis:15.5-3.4
    networks:
      - srebro  
    environment:
      POSTGRES_HOST_AUTH_METHOD: md5
      POSTGRES_PASSWORD: xxxxxxx
      #ALLOW_IP_RANGE: 0.0.0.0/0
    volumes:
      - /home/application/Database/postgres/data:/var/lib/postgresql/data
      - /home/application/Database/postgres/init:/docker-entrypoint-initdb.d/  ##可初始化SQL文件
    command: -c max_connections=2000
    ports:
      - "5432:5432"
    restart: always

networks:
  srebro:
    external: true


4、运行docker-compose创建容器
docker-compose up -d

之后,你就可以连接到这个 PostgreSQL 实例,并开始使用 PostGIS 和 pgRouting 的强大功能了。

image-20250509233853145

希望这篇文章对你有所帮助!