强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

PostGIS 完全指南 / 第 2 章:安装与数据导入

第 2 章:安装与数据导入

2.1 安装方式概览

PostGIS 支持多种安装方式,选择取决于你的操作系统和部署需求。

安装方式适用场景难度
包管理器 (apt/yum)Linux 开发/测试环境
Docker快速启动、跨平台
源码编译需要特定版本或自定义编译选项⭐⭐⭐
EnterpriseDB 安装器Windows 桌面开发
云数据库 (RDS)生产环境

2.2 Ubuntu/Debian 安装

使用官方 PostgreSQL 仓库

# 1. 安装 PostgreSQL 仓库
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  > /etc/apt/sources.list.d/pgdg.list'

# 2. 导入仓库签名密钥
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | \
  sudo apt-key add -

# 3. 更新包列表
sudo apt update

# 4. 安装 PostgreSQL 16 和 PostGIS 3.4
sudo apt install postgresql-16 postgresql-16-postgis-3

# 5. 启动服务
sudo systemctl enable postgresql
sudo systemctl start postgresql

启用扩展

# 切换到 postgres 用户
sudo -u postgres psql

# 在数据库中启用 PostGIS
CREATE EXTENSION postgis;
CREATE EXTENSION postgis_topology;
-- 可选:栅格支持
CREATE EXTENSION postgis_raster;
-- 可选:地址标准化
CREATE EXTENSION address_standardizer;

验证安装

-- 查看 PostGIS 版本
SELECT PostGIS_Full_Version();

预期输出包含 GEOS、PROJ、GDAL 等依赖库的版本信息。


2.3 CentOS/RHEL 安装

# 1. 安装 EPEL 和 PostgreSQL 仓库
sudo dnf install -y epel-release
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-9-x86_64/pgdg-redhat-repo-latest.noarch.rpm

# 2. 安装 PostgreSQL 和 PostGIS
sudo dnf install -y postgresql16-server postgresql16-postgis3

# 3. 初始化数据库
sudo /usr/pgsql-16/bin/postgresql-16-setup initdb

# 4. 启动服务
sudo systemctl enable postgresql-16
sudo systemctl start postgresql-16

# 5. 配置认证(修改 pg_hba.conf)
sudo sed -i 's/ident/md5/g' /var/lib/pgsql/16/data/pg_hba.conf
sudo systemctl restart postgresql-16

2.4 macOS 安装

# 使用 Homebrew
brew install postgresql@16
brew install postgis

# 启动 PostgreSQL
brew services start postgresql@16

# 创建数据库并启用 PostGIS
createdb gisdb
psql -d gisdb -c "CREATE EXTENSION postgis;"

2.5 Docker 部署

基础启动

# 使用官方 PostGIS 镜像
docker run -d \
  --name postgis \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -e POSTGRES_DB=gisdb \
  -p 5432:5432 \
  postgis/postgis:16-3.4

带数据持久化的启动

# 创建数据卷
docker volume create pgdata

docker run -d \
  --name postgis \
  -e POSTGRES_PASSWORD=mysecretpassword \
  -e POSTGRES_DB=gisdb \
  -v pgdata:/var/lib/postgresql/data \
  -p 5432:5432 \
  postgis/postgis:16-3.4

docker-compose.yml

version: '3.8'

services:
  postgis:
    image: postgis/postgis:16-3.4
    container_name: postgis
    environment:
      POSTGRES_USER: gisadmin
      POSTGRES_PASSWORD: ${PG_PASSWORD:-changeme}
      POSTGRES_DB: gisdb
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
      - ./init:/docker-entrypoint-initdb.d  # 初始化脚本
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U gisadmin -d gisdb"]
      interval: 10s
      timeout: 5s
      retries: 5
    restart: unless-stopped

volumes:
  pgdata:

init/01-extensions.sql 中预装扩展:

CREATE EXTENSION IF NOT EXISTS postgis;
CREATE EXTENSION IF NOT EXISTS postgis_topology;
CREATE EXTENSION IF NOT EXISTS postgis_raster;
# 启动
docker compose up -d

# 查看日志
docker compose logs -f postgis

# 连接测试
docker exec -it postgis psql -U gisadmin -d gisdb -c "SELECT PostGIS_Version();"

2.6 创建数据库与扩展

数据库初始化脚本

-- 创建专用空间数据库
CREATE DATABASE gisdb
    WITH ENCODING 'UTF8'
    LC_COLLATE 'zh_CN.UTF-8'
    LC_CTYPE 'zh_CN.UTF-8'
    TEMPLATE template0;

-- 连接到 gisdb 后执行
\c gisdb

-- 安装核心扩展
CREATE EXTENSION IF NOT EXISTS postgis;           -- 核心空间功能
CREATE EXTENSION IF NOT EXISTS postgis_topology;  -- 拓扑支持
CREATE EXTENSION IF NOT EXISTS postgis_raster;    -- 栅格支持
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;     -- 模糊匹配(用于地名匹配)
CREATE EXTENSION IF NOT EXISTS postgis_tiger_geocoder; -- TIGER 地理编码
CREATE EXTENSION IF NOT EXISTS address_standardizer;   -- 地址标准化

-- 验证已安装的扩展
SELECT name, default_version, installed_version, comment
FROM pg_available_extensions
WHERE installed_version IS NOT NULL
ORDER BY name;

升级 PostGIS

-- 查看可用升级路径
SELECT version();

-- 升级扩展(例如从 3.3 升级到 3.4)
ALTER EXTENSION postgis UPDATE;
ALTER EXTENSION postgis_topology UPDATE;

-- 验证升级结果
SELECT PostGIS_Full_Version();

注意: 升级前务必备份数据库!某些版本升级可能需要运行 postgis_extensions_upgrade() 函数。


2.7 数据导入:Shapefile

Shapefile 是 GIS 领域最常用的数据交换格式。PostGIS 提供了 shp2pgsql 工具。

基本导入流程

# 1. 下载示例数据(全球国家边界)
wget https://naciscdn.org/naturalearth/110m/cultural/ne_110m_admin_0_countries.zip
unzip ne_110m_admin_0_countries.zip -d /tmp/world

# 2. 预览 SQL(不执行)
shp2pgsql -s 4326 -W UTF-8 /tmp/world/ne_110m_admin_0_countries.shp public.countries

# 3. 导入到数据库
shp2pgsql -s 4326 -W UTF-8 -I /tmp/world/ne_110m_admin_0_countries.shp public.countries \
  | psql -d gisdb -U postgres

shp2pgsql 参数说明

参数说明示例
-s指定 SRID-s 4326 (WGS84)
-W指定字符编码-W UTF-8
-I创建空间索引-I
-d先删除已存在的表-d
-a追加到已存在的表-a
-t几何类型强制 2D-t 2D
-D使用 dump 格式(更快)-D
-G使用 Geography 类型-G

批量导入脚本

#!/bin/bash
# batch_import.sh - 批量导入目录下所有 Shapefile

DB_NAME="gisdb"
DB_USER="postgres"
SRID="4326"
ENCODING="UTF-8"
SHAPEFILE_DIR="/data/shapefiles"

for shp in "$SHAPEFILE_DIR"/*.shp; do
    TABLE_NAME=$(basename "$shp" .shp | tr '[:upper:]' '[:lower:]' | tr '-' '_')
    echo "Importing $shp -> $TABLE_NAME ..."
    shp2pgsql -s "$SRID" -W "$ENCODING" -I -D "$shp" "public.$TABLE_NAME" \
      | psql -d "$DB_NAME" -U "$DB_USER" -q
    echo "Done: $TABLE_NAME"
done

echo "All shapefiles imported."

2.8 数据导入:CSV 坐标数据

很多业务数据以 CSV 格式存储经纬度坐标,需要将其转换为空间数据。

创建表并导入

-- 创建原始数据表
CREATE TABLE raw_pois (
    id SERIAL PRIMARY KEY,
    name VARCHAR(200),
    category VARCHAR(50),
    latitude DOUBLE PRECISION,
    longitude DOUBLE PRECISION,
    address TEXT
);

-- 使用 COPY 导入 CSV
COPY raw_pois (name, category, latitude, longitude, address)
FROM '/data/pois.csv'
WITH (FORMAT csv, HEADER true, ENCODING 'UTF-8');

转换为空间表

-- 创建空间表
CREATE TABLE pois AS
SELECT
    id,
    name,
    category,
    address,
    ST_SetSRID(ST_MakePoint(longitude, latitude), 4326) AS geom
FROM raw_pois;

-- 添加主键和空间索引
ALTER TABLE pois ADD PRIMARY KEY (id);
CREATE INDEX idx_pois_geom ON pois USING GIST (geom);

-- 验证数据
SELECT name, category, ST_AsText(geom) FROM pois LIMIT 5;

注意: ST_MakePoint 的参数顺序是 (经度, 纬度),即 (X, Y),不要搞反!


2.9 数据导入:GeoJSON

-- 使用 ogr2ogr 导入 GeoJSON(需安装 GDAL)
-- 命令行方式
ogr2ogr -f "PostgreSQL" \
  PG:"dbname=gisdb user=postgres" \
  /data/regions.geojson \
  -nln regions \
  -overwrite

-- 或在 SQL 中使用 ST_GeomFromGeoJSON
CREATE TABLE geojson_features (
    id SERIAL PRIMARY KEY,
    properties JSONB,
    geom GEOMETRY(Geometry, 4326)
);

-- 手动插入 GeoJSON
INSERT INTO geojson_features (properties, geom) VALUES
(
    '{"name": "测试区域", "type": "polygon"}',
    ST_GeomFromGeoJSON('{
        "type": "Polygon",
        "coordinates": [[[116.38, 39.90], [116.42, 39.90],
                         [116.42, 39.93], [116.38, 39.93],
                         [116.38, 39.90]]]
    }')
);

2.10 数据导出

导出为 Shapefile

# 导出到 Shapefile
pgsql2shp -f /output/countries.shp -h localhost -u postgres gisdb \
  "SELECT name, iso_a3, geom FROM countries"

# 导出带 SRID 的数据
pgsql2shp -f /output/cities.shp -h localhost -u postgres gisdb \
  "SELECT name, population, geom FROM cities"

导出为 GeoJSON

-- 单条记录导出
SELECT ST_AsGeoJSON(geom) AS geojson
FROM cities
WHERE name = '北京';

-- 导出完整 FeatureCollection
SELECT jsonb_build_object(
    'type', 'FeatureCollection',
    'features', jsonb_agg(feature)
) AS geojson
FROM (
    SELECT jsonb_build_object(
        'type', 'Feature',
        'id', id,
        'geometry', ST_AsGeoJSON(geom)::jsonb,
        'properties', jsonb_build_object(
            'name', name,
            'province', province,
            'population', population
        )
    ) AS feature
    FROM cities
) subquery;

使用 ogr2ogr 导出

# 导出为 GeoJSON
ogr2ogr -f "GeoJSON" /output/cities.geojson \
  PG:"dbname=gisdb" -sql "SELECT name, geom FROM cities"

# 导出为 GeoPackage(推荐,支持多图层)
ogr2ogr -f "GPKG" /output/gis_data.gpkg \
  PG:"dbname=gisdb" -sql "SELECT * FROM cities"

2.11 大数据量导入优化

使用 COPY 而非 INSERT

-- 慢:逐条 INSERT
INSERT INTO points VALUES (1, ST_GeomFromText('POINT(0 0)'));
INSERT INTO points VALUES (2, ST_GeomFromText('POINT(1 1)'));

-- 快:批量 COPY(先导入文本,后创建几何)
-- 步骤 1: 导入到临时表
CREATE TEMP TABLE tmp_points (
    id INTEGER,
    x DOUBLE PRECISION,
    y DOUBLE PRECISION
);

COPY tmp_points FROM '/data/points.csv' WITH (FORMAT csv, HEADER true);

-- 步骤 2: 批量转换并插入空间表
INSERT INTO points (id, geom)
SELECT id, ST_SetSRID(ST_MakePoint(x, y), 4326)
FROM tmp_points;

DROP TABLE tmp_points;

大文件导入性能对比

方法100 万条记录耗时说明
逐条 INSERT~120 秒最慢,不推荐
COPY + 后处理~8 秒推荐方案
shp2pgsql -D~5 秒dump 格式更快
ogr2ogr~6 秒多格式通用

导入后优化

-- 1. 创建空间索引
CREATE INDEX idx_table_geom ON table_name USING GIST (geom);

-- 2. 更新表统计信息
ANALYZE table_name;

-- 3. 验证数据
SELECT Count(*) AS total,
       ST_IsValid(geom) AS is_valid,
       Count(*) FILTER (WHERE NOT ST_IsValid(geom)) AS invalid_count
FROM table_name
GROUP BY ST_IsValid(geom);

-- 4. 修复无效几何
UPDATE table_name
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);

2.12 常见安装问题排查

问题原因解决方案
could not open extension control file扩展未安装检查 `apt list –installed
ERROR: function postgis_version() does not exist扩展未启用CREATE EXTENSION postgis;
could not load library依赖库缺失ldd /usr/lib/postgresql/16/lib/postgis-3.so
permission denied用户权限不足使用 SUPERUSER 角色或 GRANT
encoding mismatch编码不一致使用 -W UTF-8 参数
-- 诊断脚本
SELECT version();                          -- PostgreSQL 版本
SELECT PostGIS_Full_Version();             -- PostGIS 完整版本
SELECT name, setting FROM pg_settings
WHERE name IN ('shared_preload_libraries', 'max_connections');

2.13 本章小结

要点说明
安装方式apt/yum 包管理器、Docker、源码编译
Docker 镜像postgis/postgis:16-3.4,推荐生产使用
扩展安装CREATE EXTENSION postgis;
Shapefile 导入shp2pgsql -s 4326 -I
CSV 坐标导入ST_MakePoint(lng, lat),注意参数顺序
大数据量优化COPY + 后处理,批量创建索引

扩展阅读