GeoJSONcn
GeoJSONcn / 技术专栏 / GeoJSON 转 GeoPac…

GeoJSON 转 GeoPackage?把全国省市区县乡镇边界装进一个 SQLite 离线包

发布于 2026-08-25 · GeoJSONcn 技术团队 · 阅读约 10 分钟

> 写稿用正文(下)。

做全国省市区县乡镇街道村geojson数据下载的整理时,我一开始也是老老实实一个省一个 .geojson 往上传。等省级下钻到乡镇,才发现问题:一份全国乡镇边界拆开就有几十个文件,浏览器里点来点去全靠一个个 fetch,边缘缓存一多,依赖关系乱成一锅粥。后来我把思路换成「先转 GeoPackage,再按需切片发布」,整个站点的静态文件数量和更新成本都降了一大截。

GeoPackage(.gpkg)本质就是一个 SQLite 文件,表结构里塞了空间字段和扩展元数据。它到底好在哪,为什么适合做行政区划数据的离线分发?这篇就讲讲我的取舍、一张表对比,以及一段能把 GeoJSON 转成 .gpkg 的落地代码。

为什么用 GeoPackage 装行政区划,而不是抱着 .geojson 不放

先说结论:GeoJSON 是交换格式,GeoPackage 是离线存储格式。两者不是替代关系,是链路里的一前一后。

维度GeoJSONGeoPackage (.gpkg)
本质纯 JSON 文本,一个 FeatureCollectionSQLite 单文件,含空间表 + 几何插件
多图层一个文件一个图层,要拆一堆一个文件可放省市区县乡镇村多组图层
属性查询得自己遍历 featuresSQL WHERE 随手筛,还能建空间索引
读大文件全部 JSON.parse 进内存流式/分页读,范围查询只取需要的
编辑整文件重写SQL 事务,改一条不影响其他
工具链浏览器/前端友好desktop 端(QGIS/ArcGIS/POSTGIS)友好

对「全国村级geojson数据下载」这类低频、量大、以离线分析为主的场景,GeoPackage 的单文件 + SQL 能力优势非常明显。对浏览器在线预览,反过来还是切片后的轻量 GeoJSON 更划算。

用 Python 把 GeoJSON 批量转成 GeoPackage

GeoPackage 官方推荐用 GDAL/OGR,但如果你只想在本地把一套省市区县边界转进一个 .gpkg,用 fiona + shapely 就够了。下面这段把多份 GeoJSON 合并进同一个 .gpkg 的不同图层:

import fiona

GPKG='/data/adcode/全国边界.gpkg'
layers=[
  ('province','省','E:/geo/省/*.geojson'),
  ('city','市','E:/geo/市/*.geojson'),
  ('district','区县','E:/geo/区县/*.geojson'),
]

for layer_name,layer_title,pattern in layers:
    rows=[]
    for path in util.glob.iglob(pattern):
        with fiona.open(path,'r') as src:
            # 用中段 id 做图层外键,避免跨层属性重复
            for f in src:
                f['properties']['_layer']=layer_name
                rows.append(f)
    # 'GPKG' 驱动 + 同一文件多图层,force 覆盖旧层
    with fiona.open(GPKG,'w',driver='GPKG',
                    schema=rows[0]['schema'],crs=rows[0]['crs']) as dst:
        for f in rows: dst.write(f)

几个踩过的坑提醒你:

GeoPackage 拿回去怎么用,前端又能白嫖什么

后端我每天把清洗好的边区转一次 .gpkg 归档,前端喂给浏览器的仍是按需切片的轻量 GeoJSON——两者并行,单一真相源只在生成脚本里。QGIS 打开 .gpkg 直接点开图层树,不用再拼一堆文件;要查某个乡级单位,SQL 面板一句 SELECT * FROM town WHERE adcode LIKE '510722%' 就出来了,这在纯 GeoJSON 里得手写一堆遍历。

如果你也想让浏览器能读 .gpkg,别急着上大库——@ngageoint/geopackage 这个纯 JS 包就能读空间表,配合 Leaflet 的 L.geoJSON 把拿回来的 FeatureCollection 画出来。离线场景(船舶、野外、内网)特别吃这一套:一个 .gpkg 拷进去,整套边界都在手上,不依赖任何 CDN。

顺带一提,把 .gpkg 归档文件丢进对象存储做版本化,配合之前讲的 SHA-256 校验和,更新边界就是「换一个文件」的事,边缘节点按文件名缓存也不会串旧版。想让省市区县边界数据下载后能长期离线使用,这个组合值得一试。

小结

GeoJSON 负责在浏览器里「活」,GeoPackage 负责在本地「存」。两步走:构建期用 fiona 把省市区县乡镇村各级 GeoJSON 合并进一个 .gpkg,发布期切片成前端要的轻量文件。这样静态站的文件数可控、更新原子化,桌面端拿到 .gpkg 又能直接开分析。

> 本文属于 GeoJSONcn 技术专栏,GeoJSON 数据在线预览暂免费,导出(含 .gpkg 转换服务)按次计费 ¥1.99/次。站点为静态生成 + 边缘加速架构,覆盖全国省市区县乡镇街道村各级边界数据下载。