示例数据(中国国界 + 省级行政区 GeoJSON,本文全程用这套数据演示): 链接:夸克网盘分享 提取码:Vysu (网盘失效请评论区留言,我看到会补)
前置阅读:先把环境装好
这篇是纯实操流程,不再重复讲安装。如果你还没装 PostgreSQL / PostGIS / QGIS,先看这两篇保姆级教程,跟着做一遍再回来:
-
PostgreSQL + PostGIS + pgRouting 安装 :PostgreSQL以及PostGIS和pgRouting插件安装保姆级教程(2026年最新Windows版)
-
QGIS 安装与底图加载 :QGIS安装教程(Windows版):从下载安装到底图加载一篇讲透
本文假设你已经跑通了:本机 PostgreSQL 服务能启动、PostGIS 扩展能创建、QGIS 能加载 XYZ 底图。
做时空数据、监测类项目的朋友应该都遇到过这种场景:手里拿到一份 GeoJSON------可能是前端同事给的行政区边界、可能是无人机航测出的图斑、也可能是从 OSM 下载的路网------需要把它塞进 PostGIS 里做后续的空间查询、叠加分析或者瓦片服务发布。
命令行党会直接用 ogr2ogr 或者 shp2pgsql,但如果数据本身坐标系不干净、字段类型要调整、几何还有自相交需要修一修,纯命令行反而绕远。最省心的路径其实是 QGIS:图形化连接数据库、拖拽加载数据、右键导出入库,中间还能顺手做数据检查和坐标重投影。整个链路十几分钟就能跑通,比翻文档拼 SQL 快得多。
这篇把完整流程拆开写一遍,重点是几个新手容易踩坑的地方------SRID 选错、中文字段乱码、几何类型混杂、索引没建------都一并说清楚。示例数据用开头网盘里的中国国界和省界 GeoJSON,跟着做一遍就能复现。

一、准备工作:建库、启用扩展、看清数据
1.1 新建一个专用的 GIS 数据库
装完 PostgreSQL/PostGIS 之后,默认只有 postgres、template0、template1 三个库,别直接把业务数据塞 postgres 库里,规范的做法是给 GIS 数据单开一个:
java
-- 用 postgres 超级用户执行
CREATE DATABASE test_learn;
\c gis_demo
CREATE EXTENSION IF NOT EXISTS postgis;
-- 验证
SELECT PostGIS_Full_Version();
正常会返回类似 POSTGIS="3.4.1 xxxxx" [EXTENSION] PGSQL="150" 的版本串。扩展必须在目标库里手动 CREATE,很多新手以为装完 PostGIS 就全局可用,结果 QGIS 连上库却建不了空间表,卡在这里半天。
如果打算用 pgAdmin 图形化操作,也可以:Databases → 右键 → Create → Database,然后连上这个新库,在 Query Tool 里跑上面的 CREATE EXTENSION。

1.2 先看清 GeoJSON 长啥样
把网盘里的国界和省界 GeoJSON 下载下来,用 VSCode 或者在线 geojson.io 打开,确认三件事:
-
顶层结构 :必须是
FeatureCollection,不能是单个Feature或Geometry。QGIS 也能读单 Feature,但入库时容易只写一条记录,坑得很隐蔽。 -
几何类型是否统一 :省界数据一般是
MultiPolygon(因为一个省可能包含多个岛屿飞地),国界可能是Polygon或MultiPolygon。混着来的话 PostGIS 会强制建成GEOMETRY通用类型,后续查询效率差、也不能建针对性的空间索引。 -
坐标系:GeoJSON 规范(RFC 7946)默认要求 WGS84(EPSG:4326),但国内数据经常被人塞成 GCJ-02 火星坐标或者 CGCS2000,肉眼看不出来,导到底图上偏移几百米才发现。网盘里这两份数据是标准 WGS84,直接可用;你手上如果拿到别的数据,得留个心眼,第 3 节讲怎么处理。
二、QGIS 连接 PostGIS 数据库
打开 QGIS,左侧 Browser 面板(浏览器)找到 PostGIS 节点,右键 → 新建连接。
弹窗里几个关键字段:
| 字段 | 值 | 说明 |
|---|---|---|
| 名称 | test_learn | 自己起,QGIS 里显示的别名 |
| 主机 | localhost | 远程库填 IP 或域名 |
| 端口 | 5432 | PostgreSQL 默认端口 |
| 数据库 | test_learn | 上面 CREATE DATABASE 建的那个 |
| 用户名 | postgres | 或者专门的 GIS 用户 |
| 密码 | ** | 勾选"保存密码"省得每次输 |
关键的两个勾选项:
-
✅ 允许无密码保存:配合项目文件切换时不用反复输密码。
-
❌ 只列出拥有权限的图层 :新手建议取消勾选,否则你自己刚导入的表可能因为权限过滤看不到,还以为导入失败了。
点"测试连接",弹窗显示连接成功就 OK。这时候 Browser 面板 PostGIS 下会展开你的连接,能看到 public schema 里已有的空间表(如果之前有的话)以及 spatial_ref_sys 这张系统表。

常见连接问题
Q: 测试连接报 "FATAL: password authentication failed"
检查 pg_hba.conf(PostgreSQL data 目录下),把本地连接方式从 scram-sha-256 改成 md5 或者临时改成 trust 测试,改完记得重启 PostgreSQL 服务。生产环境别用 trust。
Q: 报 "connection refused"
十有八九是 PostgreSQL 服务没启动,或者端口被占。Windows 服务里找 postgresql-x64-15 手动启动,或者 netstat -ano | findstr 5432 看端口监听情况。
Q: 远程库连不上,本地能连
默认 PostgreSQL 只监听 localhost,需要改 postgresql.conf 里的 listen_addresses = '*',同时在 pg_hba.conf 加一条 host all all 0.0.0.0/0 md5(生产环境把 0.0.0.0/0 换成具体网段),改完重启服务,防火墙放行 5432。
三、加载 GeoJSON 到 QGIS
三种方式任选一种:
-
直接拖拽:从资源管理器把 .geojson 文件拖到 QGIS 画布,最省事。
-
菜单加载:Layer → Add Layer → Add Vector Layer,数据源类型选"文件",浏览选中 GeoJSON。
-
Browser 面板:如果文件在 QGIS 已注册的目录里,直接双击。
把网盘里的国界和省界两份数据都加载进来,配上底图看一眼,中国轮廓清晰、各省分界连续,说明数据本身没问题。
加载成功后左侧 Layers 面板会出现图层名,画布上显示矢量要素。这时候先别急着导出,做两件检查工作:
3.1 检查坐标系
右键图层 → 点击属性。看 CRS 那一行,坐标系那一栏会显示 EPSG 编码。

如果显示 EPSG:4326 - WGS 84,正常,跳到下一步。
如果显示 EPSG:3857 或者其他投影坐标系,说明数据已经是投影过的,需要判断入库用哪个 CRS。经验法则:
-
展示、瓦片服务、Web 端:入 EPSG:3857(Web Mercator)或者 4326 都行,看后端约定。
-
面积/长度计算、缓冲区分析:入投影坐标系,比如国内用 CGCS2000 3-degree Gauss-Kruger 带(EPSG:4513-4533 系列),单位是米,算出来才是真米。
-
不确定就统一 4326 :PostGIS 里后续可以用
ST_Transform现算。
如果需要改坐标系:右键图层 → 导出 → 要素另存为,在 CRS 下拉里选目标坐标系,导出成一个新的临时 GeoJSON 或者 Shapefile,再把这个中间产物入库。也可以在导入 PostGIS 那一步直接指定目标 CRS(下一节讲)。
踩坑提醒:国内项目常见的偏移问题,多数是 GeoJSON 存的是 GCJ-02 但声明成 WGS84,QGIS 认不出来,直接入 WGS84 底图会偏 100-500 米。这种情况得先用 coordtransform 之类的库做 GCJ-02 → WGS84 转换,QGIS 本身没有内置这个转换(因为它不是官方 EPSG 定义)。
3.2 检查几何有效性
Processing Toolbox(Ctrl+Alt+T 打开)里搜 Check Validity,运行后会给出一份无效几何清单。常见的自相交、环方向错误,可以用 Fix Geometries 工具一键修复,输出一个新的临时图层,用这个修复版去入库。
国界和省界这类公开数据一般几何都很干净,这步可以跳过;但如果是从 CAD 转过来的、或者前端同事用 d3-geo 手撸出来的 GeoJSON,最好过一遍。判断标准:入库后跑 ST_Intersects 之类的分析报错 GEOSIntersects: TopologyException,八成就是几何无效,回来补做。
四、导出到 PostGIS:两种方式对比
方式一:数据库管理器
点击上方的工具栏中的数据库,然后点击数据库管理器,选择要导入的数据库,然后点击导入图层或文件。


下面是导入的选项说明

点 OK,QGIS 底部状态栏会显示进度。省界数据大约三十几条记录、几万个顶点,几秒就导完;国界数据顶点更多一些,十几秒也够了。完成后 Browser 面板刷新,能在 test_learn → public 下看到 china_province 和 china_border 两张新表,双击就能加载回画布验证。

方式二:DB Manager 插件(适合批量、需要写 SQL 前后处理)
菜单 Database → DB Manager,左侧树展开 PostGIS → test_learn,选中目标 schema,上方工具栏点 Import Layer/File 图标。
弹窗比 Export 简洁一些,多了几个选项:
-
Source SRID / Target SRID:可以显式指定,做坐标系转换更可控。
-
Create single-part geometries instead of multi-part:同上。
-
Replace all selected columns with strings:所有属性字段强制转 text,防止数值类型推断错误。
DB Manager 还有个杀手锏是内置 SQL 窗口,可以导入前先在源图层跑一段 SQL 做过滤:
java
SELECT * FROM china_province
WHERE name LIKE '%河南%'
AND ST_IsValid(geom)
然后再导入。这种"边导边筛"的能力,右键 Export 做不到。如果只想把某几个省单独入一张表做重点分析,走这条路。
两种方式怎么选
-
单个图层、字段类型清晰、直接入库:Export。
-
多个图层批量、需要 SQL 预处理、需要精细控制 SRID:DB Manager。
-
需要写入非 public schema 且 schema 尚未创建 :先在 DB Manager 或者 psql 里
CREATE SCHEMA xxx;,再回来导入,Export 不能自动建 schema。
五、验证与后续处理
5.1 psql 里查一下
java
-- 表结构
\d public.china_province
-- 记录数
SELECT COUNT(*) FROM china_province;
-- 几何类型和 SRID 确认
SELECT GeometryType(geom), ST_SRID(geom) FROM china_province LIMIT 1;
-- 空间范围(应该覆盖中国全域,经度 73-135,纬度 3-54 左右)
SELECT ST_Extent(geom) FROM china_province;
-- 索引情况
SELECT indexname, indexdef FROM pg_indexes
WHERE tablename = 'china_province';
-- 抽样看几条属性
SELECT name, ST_Area(geom::geography)/1e6 AS area_km2
FROM china_province
ORDER BY area_km2 DESC
LIMIT 5;
应该能看到 geom geometry(MultiPolygon,4326) 这样的字段类型,索引里应该有一条 gist (geom)。最后那条面积查询会返回新疆、西藏、内蒙古这几个大区,能对上就说明数据、坐标系、投影都没问题。

5.2 QGIS 里加载回来看看
Browser 面板找到刚导入的表,双击加载。如果画布上什么都不显示,先点工具栏的 Zoom Full(放大镜图标),大概率是视图没定位到数据范围。
右键图层 → Properties → Source,看 Features count 是否和 psql 查出来的记录数一致。
再顺手做个直观验证:把 QGIS 底图切到天地图或者 OSM,看看 PostGIS 里的省界是不是完美贴合底图轮廓。有偏移的话就是坐标系问题,回头查第三节。
5.3 补建索引(如果导入时漏了)
java
CREATE INDEX idx_china_province_geom
ON china_province USING GIST (geom);
-- 更新统计信息,让查询规划器走对索引
ANALYZE china_province;
省界这种规模的数据其实无所谓,但如果你导的是百万级路网或者上亿点云,建议再补一个:
java
-- 空间聚类,让物理存储顺序和空间位置贴近,范围查询提速明显
CLUSTER china_province USING idx_china_province_geom;
CLUSTER 是一次性操作,后续新增数据不会自动重排,定期维护的话可以配 pg_cron 定时跑。
六、常见坑与排查清单
坑 1:中文字段乱码
现象:psql 查出来 name 字段是 ??? 或者一串拉丁字母乱码,比如 河南省 变成 ?ê??。
原因:GeoJSON 是 UTF-8,但导入时 Encoding 选了 System(Windows 下就是 GBK)。
修复:Export 对话框 Encoding 明确选 UTF-8,重新导一次。已经入库的可以:
java
ALTER TABLE china_province
ALTER COLUMN name TYPE text
USING convert_from(convert_to(name, 'GBK'), 'UTF8');
不过说实话,重新导比转换靠谱。
坑 2:几何类型不匹配
现象:QGIS 加载入库表时报 "WKB type MultiPolygon not supported by this layer"。
原因:Export 时选了 "Force single-part",把 MultiPolygon 拆成了 Polygon,但图层元数据还写着 Multi。
修复:删表重导,或者 QGIS 里 Layer → Add Layer 时手动指定几何类型。
坑 3:SRID 0 或者 -1
现象:SELECT ST_SRID(geom) 返回 0。
原因:GeoJSON 文件里没写 crs 字段,QGIS 也没识别出来,导入时 CRS 下拉框被留成"未知"。
修复:入库时明确选 EPSG:4326,或者事后 UPDATE:
java
UPDATE china_province
SET geom = ST_SetSRID(geom, 4326);
注意 ST_SetSRID 只改标签不做坐标变换,如果原始坐标本来就是经纬度那没问题;如果是投影坐标被误标成 4326,得用 ST_Transform 重算。
坑 4:远程库导入极慢
现象:本机导几万条秒完,远程库卡半小时。
原因:QGIS 是逐条 INSERT,网络往返放大延迟。
修复:Export 对话框下方有个 Use bulk insert 或者切换到 DB Manager 用 COPY 模式,速度能提一到两个数量级。或者干脆把 GeoJSON 传到服务器本地,用 ogr2ogr 命令行导,那是最快的。
坑 5:权限不足
现象:连得上库,但导入时报 permission denied for schema public。
原因:PostgreSQL 15 之后,public schema 默认不再给普通用户 CREATE 权限。
修复:用超级用户执行:
java
GRANT CREATE, USAGE ON SCHEMA public TO gis_user;
-- 或者更规范地建独立 schema
CREATE SCHEMA gis_data AUTHORIZATION gis_user;
七、写在最后
用 QGIS 把 GeoJSON 塞进 PostGIS 这条路,胜在可视化、可回溯、中间能改 ,特别适合数据本身不太干净、需要边看边调的场景。等流程稳定了、数据结构固化了,再切换到 ogr2ogr 或者 Python 脚本批处理也不迟。
如果这篇文章对你有用,点个赞、收藏一下,评论区聊聊你入库时遇到的奇葩数据格式。