简介:面向PostgreSQL 14的64位环境,这份PostGIS 3.5.0完整扩展安装包,用于解决普通数据库难以存储、查询与分析地理空间数据的问题,适合GIS开发者、数据工程师和系统管理员快速搭建空间数据能力。作为PostgreSQL最流行的开源空间扩展,PostGIS支持点、线、面等几何类型,并提供空间索引、坐标转换、拓扑关系判断等函数,可用于地图服务、位置分析、城市规划与环境监测等场景。压缩包约130.92MB,共含1335个文件,主体为951个SQL脚本、79个动态库、75个CSV数据、50个TIF栅格文件及35个GFS文件;SQL脚本负责创建扩展对象与函数,动态库提供核心运行能力,CSV与TIF多用于示例数据和坐标参考,目录清晰便于按需部署。包内附依赖组件、坐标系定义及示例数据,并集成部分点云与路径分析模块,可让开发者在PostgreSQL 14中直接启用PostGIS,省去源码编译和依赖排查过程;同时加上示例资源,便于理解从创建空间数据库到执行空间查询的常用流程。目前已有95人学习下载,适合需要高效构建GIS后台服务的团队作为基础环境包使用。
1. 拿到 postgis-bundle-pg14-3.5.0x64.zip,先别急着解压
这个文件名已经把大部分信息写在脸上了:postgis-bundle是 Windows 下不用安装程序、解压即用的 PostGIS 完整发行包,pg14表示它内置的是 PostgreSQL 14 的运行时,3.5.0对应 PostGIS 扩展版本,x64说明只能用在 64 位 Windows 上。很多人第一次拿到这种 zip 包会默认它跟普通绿色软件一样,解压、双击、完事,但 PostGIS 的 bundle 恰恰不是这么回事——它里面塞了数据库服务、空间扩展脚本、GDAL 依赖和一整套命令行工具,缺了环境变量配置和扩展初始化,psql 连上去也只是一台普通得不能再普通的 PostgreSQL,空间函数一个都调不出来。
这篇文章就是围绕这个 zip 包展开的落地笔记。适合三类人:正在 Windows 上做 GIS / LBS 开发、想省掉 Linux 虚拟机折腾的工程师;被各种“安装失败”“扩展不存在”错误卡住的新手;以及想搞清楚 bundle 和官方安装器到底差在哪、要不要换成 zip 方案的熟手。接下来我会按“包内部结构 → 安装配置 → 踩坑记录 → 数据实操 → 升级进阶”的顺序,把能抄作业的命令和参数都摆在明面上。
2. bundle 包的目录结构:先搞清楚三个组件分别干什么
2.1 bin、lib、share/extension 三层各管什么
把postgis-bundle-pg14-3.5.0x64.zip解压后,第一眼看到的是一堆目录,最核心的是bin、lib、share三个。bin里面放着所有可执行程序,包括postgres服务进程、psql客户端、pg_ctl服务控制工具,还有后面导入导出 shp 数据时要用的shp2pgsql和pgsql2shp。平时做空间数据迁移,绝大多数时间都在跟这六个命令打交道。
lib目录里是 PostgreSQL 运行时的动态链接库,注意这里不只是数据库本身的库,还有 PostGIS 依赖的libgdal、libproj、libgeos等空间计算底层库。bundle 之所以体积偏大,很大一部分原因就是把这些原生依赖全部塞进了这一个包,换来了“解压即用”的便利,代价是你没法单独升级其中某一个库——要升只能等新版本 bundle。
share/extension是容易被忽略但最关键的一层。PostGIS 不是一个独立的可执行程序,而是一堆 SQL 脚本和 C 语言扩展文件组成的数据库插件。postgis.control文件用来声明扩展名和默认版本,postgis--3.5.0.sql这个几百 KB 的脚本会在你执行CREATE EXTENSION postgis时被 PostgreSQL 自动加载,把几千个空间函数、类型和操作符注入到数据库里。很多人在这一步翻车,报“无法加载扩展”,八成是 PostgreSQL 的SHAREDIR路径没指向这个 bundle 的share目录。
2.2 为什么 Windows 下 PostGIS 用 zip 而不是 exe 安装包
在 Windows 上安装 PostgreSQL 本身有原生安装程序,但 PostGIS 却长期没有官方 exe 安装器,这是很多新手不理解的地方。常见做法是社区把 PostgreSQL 运行时、PostGIS 扩展、GDAL 依赖打包成 zip,让用户自己手动做两件事:注册环境变量、初始化数据目录。
这个选型是刻意的。PostGIS 的版本迭代很快,3.5.0 的脚本要依赖特定版本的 GEOS 和 GDAL 才能编译出正确的空间结果。如果做成 exe 安装器,就必须把 PostgreSQL 服务注册、扩展脚本拷贝、依赖库版本检测全部写进安装逻辑,出错的概率反而更高。zip 包把选择权交给使用者:你可以先试跑、再决定要不要注册成 Windows 服务,甚至可以同时解压多个版本做对比测试。缺点也很明显——没有安装向导,所有配置都得自己动手,这就是本文章第三章要解决的问题。
2.3 用表格看懂 bundle 里的关键可执行文件
| 文件 | 用途 | 常见调用场景 |
|---|---|---|
initdb | 初始化数据库数据目录 | 首次安装时指定编码和数据目录 |
pg_ctl | 启停 PostgreSQL 服务 | pg_ctl start/pg_ctl register |
psql | 命令行客户端 | 执行 SQL、验证扩展版本 |
shp2pgsql | 把 ESRI Shapefile 转成 SQL | 导入 shp 空间数据 |
pgsql2shp | 把空间查询结果导出成 shp | 数据交换、备份 |
createdb | 创建新数据库 | 建库后加载 PostGIS 扩展 |
熟悉这张表的重点不是背命令,而是理解 bundle 里“没有安装向导、只有工具链”这个事实。后面所有操作,本质上都是在手工执行安装向导原本要做的事情。
3. 从解压到跑通空间扩展:Windows 下的完整配置流程
3.1 解压位置与 PATH 环境变量:先立规矩
我习惯把 bundle 解压到C:\pgsql,这个路径没有空格,能避开 PostgreSQL 在 Windows 下对带空格路径的一堆怨念。如果你放到C:\Program Files\pgsql,后面配置 GDAL 数据路径时大概率会踩坑,这一点在第 4 章会展开讲。
解压后第一件事是设置环境变量,把bin目录加进 PATH,同时新建一个PGSHARE环境变量指向share目录。Windows 上可以用系统设置里的“环境变量”界面,也可以直接用 PowerShell 执行:
[Environment]::SetEnvironmentVariable("Path", $env:Path + ";C:\pgsql\bin", "User") [Environment]::SetEnvironmentVariable("PGSHARE", "C:\pgsql\share", "User")这段命令把bin目录追加到用户级 PATH,并定义了PGSHARE。PostgreSQL 在启动时会根据PGSHARE查找extension目录下的脚本文件,如果这个变量缺失,后面执行CREATE EXTENSION postgis就会直接报错。设置完成后,重开一个终端窗口让环境变量生效。
3.2 初始化数据目录与首次启动
环境变量就位后,下一步是初始化数据库的数据目录。bundle 自带的 PostgreSQL 14 和官方安装版的行为一致,必须先用initdb生成基础系统数据库,否则连启动都做不到。
initdb -D C:\pgsql\data -E UTF8 --locale=C -U postgres这里-D指定数据目录,-E UTF8强制数据库编码为 UTF-8,--locale=C是为了避免 Windows 的中文区域设置把排序规则搞出乱子,-U postgres设置超级用户名为postgres。如果你用默认的 locale,之后对中文进行ORDER BY或LOWER()时会有意想不到的行为,我见过某项目因为这个问题导致中文排序结果跟线上 Linux 环境不一致,排查了很久。
初始化完成后启动服务:
pg_ctl -D C:\pgsql\data -l C:\pgsql\pg.log start-l参数把日志写到C:\pgsql\pg.log,启动过程如果报错,这个日志是第一个排查入口。确认启动成功后,先创建业务数据库,再用psql加载扩展:
createdb -U postgres gisdb psql -U postgres -d gisdb -c "CREATE EXTENSION postgis;" psql -U postgres -d gisdb -c "SELECT postgis_full_version();"上面postgis_full_version()会返回 PostGIS 的完整版本号以及依赖库(GEOS、GDAL、PROJ)的版本信息。如果能看到类似POSTGIS="3.5.0" [EXTENSION]的输出,说明扩展加载成功。需要注意的是,CREATE EXTENSION postgis只加载基础功能,如果你需要栅格数据支持,还要手动创建postgis_raster扩展,PostGIS 3.x 以后栅格功能不再默认包含。
3.3 注册成 Windows 服务:让数据库开机自启
开发机可以每次手动pg_ctl start,但部署到服务器上就不礼貌了。用pg_ctl register把 PostgreSQL 注册成 Windows 服务是最省心的方式:
pg_ctl register -N "PostgreSQL14_GIS" -D C:\pgsql\data -U "NT AUTHORITY\NetworkService" -P "password"这里-N指定服务名,-D指向数据目录,-U是 Windows 运行身份,-P是数据库超级用户的密码(不是 Windows 密码)。注册后用net start PostgreSQL14_GIS启动服务。有个细节:如果数据库超级用户的密码是空,注册时服务会因为无法通过身份验证而启动失败。而initdb阶段设了-U postgres但没设密码的话,必须先psql -c "ALTER USER postgres PASSWORD 'yourpass';"把密码补上,再注册服务,否则会看到一个非常折磨人的“服务启动后又自动停止”事件。
这条路径走完,bundle 才从“一堆文件”变成“一个可用的 GIS 数据库服务”。很多人栽在只解压不配置上,以为 zip 里的 postgres.exe 双击就能开机自启,实际上 PostgreSQL 在 Windows 上从设计上就不支持直接双击跑服务。
4. 安装与使用避坑:PostGIS bundle 最常踩的 5 个坑
4.1 路径带空格导致 GDAL 数据加载失败
某同事把 bundle 解压到C:\Program Files\PostGIS Bundle,执行CREATE EXTENSION postgis_raster;时直接报错,提示找不到gdal-data目录下的proj.db。问题出在 GDAL 库初始化时按相对路径找资源文件,而路径里有空格导致解析被截断。解决办法是解压到C:\pgsql这类无空格路径,或者在postgis.ini里显式指定GDAL_DATA为绝对路径。
[postgis] GDAL_DATA=C:\pgsql\share\contrib\postgis-3.5\gdal-data PROJ_LIB=C:\pgsql\share\contrib\postgis-3.5\proj注意postgis.ini文件一般放在bin目录下,bundle 在启动时会去读它,如果改完还是报错,先确认文件编码是 ANSI 而不是带 BOM 的 UTF-8,Windows 上这类玄学问题经常是编码引起的。
4.2 多套 PostgreSQL 环境导致 psql 连错版本
本机原来装了 PostgreSQL 16 的服务,PATH 里已有旧的psql路径,把 bundle 的bin目录追加到 PATH 之后,执行psql -U postgres连上的可能是旧版的 5432 端口服务。此时查SELECT version()会看到 PostgreSQL 16,而CREATE EXTENSION postgis大概率失败,因为旧实例的share目录里根本没有 PostGIS 的脚本文件。
遇到这种情况,先不要急着怀疑 zip 包坏了。执行which psql或where psql看实际调用路径,再检查pg_ctl status -D C:\pgsql\data确认 bundle 实例是否真的在跑。我一般会把 bundle 的 bin 路径放在 PATH 最前面,并且在新终端里操作,避免沿用旧终端的环境变量快照。
4.3 服务注册成功后却反复停止:密码与权限问题
某项目部署时,用NT AUTHORITY\NetworkService注册服务,启动后服务状态从“正在运行”变成“已停止”,事件查看器里只有一句模糊的错误。折腾了半天发现是数据目录C:\pgsql\data的访问权限没放开,NetworkService 账户没有读取权限,导致postgres.exe一启动就崩溃。
解决办法:右键数据目录 → 属性 → 安全 → 添加NetworkService账户读写权限。另一种做法是注册成-U postgres用当前登录用户身份运行,但这台机器一旦注销登录,服务又起不来。所以上线前的最后一件事,一定要用net stop和net start完整走一遍服务的停止、启动流程,不要只验证手动启动。
4.4 中文数据乱码:客户端编码与数据库编码不一致
数据库用-E UTF8初始化后,向表里插入中文地名,查出来是一堆问号。问题不在数据本身,而在 psql 客户端的客户端编码设置。Windows 的默认终端代码页是 GBK,psql 把 GBK 编码的内容直接传给了 UTF-8 数据库。
解决方法是执行SET client_encoding TO 'UTF8';或者在 psql 启动时加-E参数。更彻底的方案是在环境变量里设置PGCLIENTENCODING=UTF8。另外要注意,shp2pgsql 导入数据时如果 shp 文件里有中文属性,必须加-W UTF8参数指定源文件编码,否则会出现属性乱码而几何数据正常的诡异情况。
4.5 空间索引建了却不生效:缺 ST_Intersects 条件或没 ANALYZE
空间索引在 PostgreSQL 里是通过 GIST 索引实现的,创建语法是CREATE INDEX idx_poi_geom ON poi USING GIST (geom);。但建完索引后发现查询依然全表扫描,速度没有提升。
最常见的原因是查询语句没用空间操作符。PostGIS 的 GIST 索引只对&&(bbox 相交)、ST_Intersects、ST_DWithin这类函数生效,如果你用ST_Area或者ST_Distance < 100这种写法,优化器根本不会走空间索引。另一个原因是数据量太小,优化器认为全表扫描比索引更快。执行ANALYZE poi;更新统计信息后,再配合EXPLAIN ANALYZE看执行计划,逐步排除掉这两种情况。
5. 让数据真正跑起来:空间函数验证与 shp 导入导出
5.1 用一张测试表验证空间函数链路
扩展加载成功只是开始,真正证明环境可用的是能跑通一套“创建空间表 → 插入几何 → 空间查询”的完整链路。下面这一段 SQL 可以直接拷到 psql 里运行:
CREATE TABLE IF NOT EXISTS test_points ( id serial PRIMARY KEY, name text, geom geometry(Point, 4326) ); INSERT INTO test_points (name, geom) VALUES ('站点A', ST_GeomFromText('POINT(116.39 39.90)', 4326)), ('站点B', ST_GeomFromText('POINT(116.41 39.91)', 4326)); CREATE INDEX idx_test_points_geom ON test_points USING GIST (geom); SELECT name, ST_Distance(geom::geography, 'POINT(116.40 39.905)'::geography) AS dist_meters FROM test_points ORDER BY dist_meters;这段 SQL 做了四件事:建表时指定几何类型为geometry(Point, 4326),即二维点加 WGS84 坐标系;插入数据时用ST_GeomFromText把 WKT 字符串转成几何对象;建 GIST 空间索引;最后用ST_Distance计算每个点到目标点的球面距离,单位为米。注意我把几何对象用::geography转成了地理类型,这样距离计算才按椭球模型走,结果更符合地图上的直观感受,否则 4326 坐标系下算出来的距离是度,不是米。
如果这段 SQL 能顺利跑完,说明几何类型、空间索引、空间函数三大件全部正常。
5.2 shp2pgsql:导入一个真实 shp 图层
拿到一个已有的 shp 文件时,shp2pgsql是必用工具。它的工作流程是把 shp 文件解析成 SQL 文件,再把 SQL 通过管道喂给 psql:
shp2pgsql -s 3857 -I -W UTF8 poi.shp public.poi | psql -U postgres -d gisdb参数含义:-s 3857指定 shp 文件自带的坐标系为 Web 墨卡托,如果你的 shp 是别的坐标系,要改成对应的 EPSG 编号,比如 CGCS2000 是4490,WGS84 是4326;-I表示导入后自动建 GIST 空间索引;-W UTF8指定 shp 属性表里的字符串编码,如果源文件是 GBK 的,把 UTF8 改成 GBK 即可。
有一个常见的翻车场景:shp 的坐标系是4490(CGCS2000),但底图数据是3857,导入后直接把两个图层叠在一起发现位置偏了几公里。这不是导入命令的问题,而是坐标系没统一。最稳妥的做法是导入后用ST_Transform做一次坐标转换,统一到3857再使用:
ALTER TABLE public.poi ALTER COLUMN geom TYPE geometry(MultiPolygon, 3857) USING ST_Transform(geom, 3857);5.3 pgsql2shp:把查询结果导出成 shp
导出方向同样常用,比如把某个时间范围内的轨迹点导出给外业人员看。pgsql2shp的用法跟 shp2pgsql 正好相反,它直接连数据库执行查询并生成 shp 文件:
pgsql2shp -f C:\gis_data\export_poi -u postgres -P password gisdb "SELECT id, name, geom FROM public.poi WHERE ST_Within(geom, ST_MakeEnvelope(116.0, 39.5, 117.0, 41.0, 3857))"这里-f指定输出文件路径,-u和-P是账号密码,最后一个参数是数据库名和查询 SQL。需要注意的是,导出格式的字段宽度固定为 DBF 规范,中文文本超出长度会被截断,写 SQL 时用LEFT(name, 20)提前控制长度就能避免乱码和截断问题。
6. 版本升级与日常维护:把 bundle 用熟的进阶技巧
PostGIS 的版本迭代节奏不算慢,3.5.0 之后还会有小版本更新。Windows 下升级 postgis 扩展本身不需要重装整个 bundle:拿到新版 zip 后,解压到一个新目录,把新目录的share\extension覆盖到旧目录,然后执行ALTER EXTENSION postgis UPDATE;。这里最关键的坑是,升级前用pg_dump -Fc做一次全库备份,因为扩展升级脚本一旦执行到一半报错,数据库可能处于“已更新但不可用”的中间状态,没有备份几乎只能从头初始化。
日常用得多的另一个技巧是持续关注postgis_full_version()的输出。它除了显示 PostGIS 版本,还会报告 GEOS 和 PROJ 的版本,如果发现 GEOS 版本过低,某些空间计算函数的精度会明显下降,这时候才需要整体升级 bundle。我个人的习惯是每半年检查一次这个输出,不是看数字新不新,而是确认底层依赖没有偏离太远。
PostGIS 3.x 之后,栅格功能默认不再随主扩展安装。需要用到ST_AsTIFF、ST_Value做栅格计算的项目,在创建扩展时要单独执行CREATE EXTENSION postgis_raster;,并且要设置postgis.enable_outdb_rasters参数为on,否则你只能读本地栅格,读不到外部引用栅格。完整命令如下:
psql -U postgres -d gisdb \ -c "CREATE EXTENSION IF NOT EXISTS postgis_raster;" \ -c "SET postgis.enable_outdb_rasters = on;"最初用 bundle 包做某图像处理 Demo 的时候,我犯过一个很低级的错误:把整个 zip 当作“绿色软件”,解压后只运行了postgres.exe,然后对着 psql 连接失败的错误日志发了一下午的呆。后来把环境变量、数据初始化、扩展加载这三件事理清楚,全流程跑通只需要十分钟。Windows 上做 GIS 开发本身不算主流选择,但一旦摸清 bundle 的脾气,它反而是上手最快、最容易复现的一条规定路线。希望这篇文章能帮你少走一段弯路,把时间花在数据本身,而不是折腾环境上。
本文还有配套的精品资源,点击获取