石家庄GIS数据怎么转GeoJSON?Shapely与Fiona实战技巧(附:代码示例)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

如果你正在处理“石家庄GIS数据怎么转GeoJSON?Shapely与Fiona实战技巧(附:代码示例)”这个问题,通常不是简单把后缀改成 .geojson 就结束了。真正容易出错的地方在于坐标系、中文字段编码、几何有效性、属性类型以及最终是否适合 WebGIS 加载。

本文以石家庄常见的 GIS 矢量数据为例,演示如何用 Python 中的 Fiona 读取 Shapefile,用 Shapely 处理几何对象,并输出标准 GeoJSON。适合 GIS 学生、初级 GIS 工程师、空间数据分析人员和 WebGIS 开发者参考。

石家庄GIS数据转GeoJSON Shapely与Fiona实战流程
石家庄 GIS 数据转 GeoJSON 的推荐处理流程:先检查坐标系和编码,再处理几何,最后输出并验证。

引言:为什么石家庄GIS数据转GeoJSON经常出问题

很多石家庄本地项目会拿到行政区、道路、兴趣点、管网或用地边界等 Shapefile 数据。把这些数据转成 GeoJSON 后,常用于 Leaflet、OpenLayers、Mapbox GL JS 或 Cesium 等 WebGIS 前端展示。

但在实际工作中,经常会遇到这些问题:

  • GeoJSON 加载后位置偏到海上或其他城市。
  • 中文字段变成乱码。
  • Polygon 面数据显示缺口、重叠或无法渲染。
  • 属性字段里的日期、数值、空值导致导出失败。
  • GeoJSON 文件过大,前端打开卡顿。

这些问题背后往往不是 GeoJSON 本身的问题,而是数据坐标系、编码、几何质量和转换流程没有处理好。下面我们用 Fiona 与 Shapely 建立一个相对稳妥的转换流程。

背景:Shapefile、Fiona、Shapely和GeoJSON分别负责什么

在开始写代码之前,需要先把几个概念分清楚。

  • Shapefile:传统 GIS 矢量数据格式,通常由 .shp.shx.dbf.prj 等多个文件组成。
  • GeoJSON:基于 JSON 的矢量地理数据格式,常用于 WebGIS 前端加载。
  • Fiona:Python 中常用的 GIS 矢量数据读写库,适合读取 Shapefile、GeoPackage、GeoJSON 等格式。
  • Shapely:Python 中处理几何对象的库,可用于判断几何是否有效、修复几何、计算范围、简化线面等。
  • 坐标系:决定数据坐标如何落到地球上的规则。WebGIS 中的 GeoJSON 通常建议使用 WGS84 经纬度坐标,即 EPSG:4326。

一个稳妥的转换思路是:用 Fiona 读取原始数据,用 PyProj 进行坐标转换,用 Shapely 检查和修复几何,再用 Fiona 或标准 JSON 写出 GeoJSON。

原理:石家庄GIS数据转GeoJSON前必须先检查坐标系

石家庄位于河北省中南部,WGS84 经纬度大致在东经 113 到 115 度、北纬 37 到 39 度附近。如果你导出的 GeoJSON 坐标不是类似 [114.5, 38.0] 这样的经纬度,而是 [500000, 4200000][38400000, 4200000] 这类大数值,就说明它很可能仍然是投影坐标。

常见情况包括:

  • 数据本身是 CGCS2000 高斯克吕格投影。
  • 数据是西安80或北京54坐标系。
  • 数据缺少 .prj 文件,无法自动识别坐标系。
  • 数据已经是 WGS84,但被错误地再次转换。

GeoJSON 规范本身并不强制所有坐标都必须是 EPSG:4326,但大多数 WebGIS 工具默认按经纬度坐标理解 GeoJSON。因此,石家庄 GIS 数据转 GeoJSON 时,建议明确输出为 EPSG:4326,除非你的前端地图专门处理了其他坐标系。

判断是否需要转换坐标系的简单方法:打开几条坐标看数值范围。如果是 110 到 120、35 到 45 这样的经纬度范围,通常接近 WGS84;如果是几十万、几百万或带分带号的大数值,则通常是投影坐标。

步骤:使用Fiona和Shapely把石家庄Shapefile转GeoJSON

步骤1:准备Python环境

建议使用独立虚拟环境,避免与系统 Python 或 ArcGIS Pro 自带 Python 混用。可以使用 conda 安装,GIS 相关依赖会更省心。

conda create -n gis_geojson python=3.11
conda activate gis_geojson
conda install -c conda-forge fiona shapely pyproj

如果使用 pip,也可以安装:

pip install fiona shapely pyproj

如果在 Windows 上安装 Fiona 报错,优先建议使用 conda-forge,因为 Fiona 依赖 GDAL,直接 pip 安装有时会遇到本地编译或动态库问题。

步骤2:检查Shapefile文件是否完整

假设你的石家庄行政区数据路径为:

D:/gisdata/shijiazhuang/admin_boundary.shp

同目录下至少应有以下文件:

  • admin_boundary.shp:几何数据。
  • admin_boundary.shx:几何索引。
  • admin_boundary.dbf:属性表。
  • admin_boundary.prj:坐标系信息,强烈建议保留。

如果缺少 .prj 文件,程序可能无法知道原始坐标系。此时不要盲目转换,应先向数据提供方确认坐标系,或在 QGIS、ArcGIS Pro 中根据已知底图位置进行判断。

步骤3:读取Shapefile并查看坐标系

import fiona

input_shp = r"D:/gisdata/shijiazhuang/admin_boundary.shp"

with fiona.open(input_shp, "r", encoding="utf-8") as src:
    print("驱动:", src.driver)
    print("坐标系:", src.crs)
    print("要素数量:", len(src))
    print("字段结构:", src.schema)

如果中文字段乱码,可以把 encoding="utf-8" 改为 encoding="gbk" 试一下。很多历史 Shapefile 的 .dbf 属性表使用 GBK 或 CP936 编码。

with fiona.open(input_shp, "r", encoding="gbk") as src:
    print(src.schema)

步骤4:将坐标转换为EPSG:4326

下面的代码会读取原始 Shapefile,自动获取源坐标系,并转换到 WGS84 经纬度坐标。这里使用 pyproj.Transformer 做坐标转换,用 shapely.ops.transform 应用到几何对象。

import fiona
from fiona.crs import CRS
from shapely.geometry import shape, mapping
from shapely.ops import transform
from pyproj import Transformer

input_shp = r"D:/gisdata/shijiazhuang/admin_boundary.shp"
output_geojson = r"D:/gisdata/shijiazhuang/admin_boundary_wgs84.geojson"

target_crs = CRS.from_epsg(4326)

with fiona.open(input_shp, "r", encoding="gbk") as src:
    source_crs = src.crs

    if not source_crs:
        raise ValueError("原始数据缺少坐标系信息,请先确认 .prj 或手动指定源坐标系。")

    transformer = Transformer.from_crs(source_crs, target_crs, always_xy=True)

    output_schema = src.schema.copy()

    with fiona.open(
        output_geojson,
        "w",
        driver="GeoJSON",
        crs=target_crs,
        schema=output_schema,
        encoding="utf-8"
    ) as dst:

        for feature in src:
            geom = shape(feature["geometry"])

            geom_wgs84 = transform(transformer.transform, geom)

            new_feature = {
                "type": "Feature",
                "geometry": mapping(geom_wgs84),
                "properties": dict(feature["properties"])
            }

            dst.write(new_feature)

print("转换完成:", output_geojson)

这段代码适合大多数“有正确 .prj 文件”的石家庄 GIS 数据转 GeoJSON 场景。如果输出后能在 QGIS 或 WebGIS 中落到石家庄附近,说明坐标转换基本正确。

步骤5:用Shapely检查和修复无效几何

有些面数据在 Shapefile 中看起来正常,但转换为 GeoJSON 后前端渲染失败,原因可能是 Polygon 自相交、环方向异常或几何为空。可以在转换时增加几何检查。

import fiona
from fiona.crs import CRS
from shapely.geometry import shape, mapping
from shapely.ops import transform
from pyproj import Transformer

input_shp = r"D:/gisdata/shijiazhuang/admin_boundary.shp"
output_geojson = r"D:/gisdata/shijiazhuang/admin_boundary_fixed.geojson"

target_crs = CRS.from_epsg(4326)

with fiona.open(input_shp, "r", encoding="gbk") as src:
    source_crs = src.crs

    if not source_crs:
        raise ValueError("原始数据缺少坐标系信息,请先确认源坐标系。")

    transformer = Transformer.from_crs(source_crs, target_crs, always_xy=True)
    output_schema = src.schema.copy()

    invalid_count = 0
    empty_count = 0

    with fiona.open(
        output_geojson,
        "w",
        driver="GeoJSON",
        crs=target_crs,
        schema=output_schema,
        encoding="utf-8"
    ) as dst:

        for feature in src:
            geom = shape(feature["geometry"])

            if geom.is_empty:
                empty_count += 1
                continue

            if not geom.is_valid:
                invalid_count += 1
                geom = geom.buffer(0)

            if geom.is_empty:
                empty_count += 1
                continue

            geom_wgs84 = transform(transformer.transform, geom)

            new_feature = {
                "type": "Feature",
                "geometry": mapping(geom_wgs84),
                "properties": dict(feature["properties"])
            }

            dst.write(new_feature)

print("转换完成:", output_geojson)
print("修复的无效几何数量:", invalid_count)
print("跳过的空几何数量:", empty_count)

buffer(0) 是 Shapely 中常见的简单几何修复方法,对部分自相交面有效。但它不是万能工具,复杂拓扑错误仍建议在 QGIS 的“修复几何”工具或 ArcGIS Pro 的 Repair Geometry 工具中进一步处理。

步骤6:处理GeoJSON属性中的特殊类型

GeoJSON 属性最终会变成 JSON。JSON 对字段类型比较敏感,日期、Decimal、小数精度、空值和特殊对象都可能造成问题。Fiona 通常会帮你处理大部分常见类型,但为了更稳妥,可以对属性做一次清洗。

from datetime import date, datetime
from decimal import Decimal

def clean_properties(properties):
    result = {}

    for key, value in dict(properties).items():
        if isinstance(value, (datetime, date)):
            result[key] = value.isoformat()
        elif isinstance(value, Decimal):
            result[key] = float(value)
        elif value is None:
            result[key] = None
        else:
            result[key] = value

    return result

在写出 Feature 时使用:

new_feature = {
    "type": "Feature",
    "geometry": mapping(geom_wgs84),
    "properties": clean_properties(feature["properties"])
}

如果你的属性字段包含很长的说明文本、图片路径、附件字段或内部管理字段,可以在导出前删除不需要的字段,减小 GeoJSON 文件体积。

步骤7:在QGIS中验证GeoJSON结果

代码运行完成后,不建议直接交给前端使用。至少做一次 GIS 软件验证:

  1. 打开 QGIS。
  2. 加载一个在线底图,例如 OpenStreetMap。
  3. 把输出的 admin_boundary_wgs84.geojson 拖入 QGIS。
  4. 检查是否落在石家庄市范围附近。
  5. 随机点选几个要素,检查中文属性是否正常。
  6. 放大到边界细节,检查面是否破碎、线是否断裂。

如果位置正确、属性正常、几何显示完整,说明这次石家庄 GIS 数据转 GeoJSON 的结果基本可用。

常见坑:石家庄GIS数据转GeoJSON时最容易忽略的问题

1. 把投影坐标直接当成经纬度

这是最常见的问题。比如原始数据是 CGCS2000 高斯克吕格投影,坐标值可能是几十万和几百万。如果不转成 EPSG:4326,WebGIS 前端会把这些大数当作经纬度,结果自然无法显示到正确位置。

2. 不确认.prj文件是否正确

有些数据虽然带 .prj,但并不一定正确。历史数据经过多次复制、裁剪和导出后,可能出现几何坐标和 .prj 描述不一致的情况。遇到位置偏移明显的数据,应回到数据源确认坐标系。

3. 中文字段乱码

石家庄本地项目数据常见字段如“区县名”“道路名”“地块编号”“用地性质”等。如果 .dbf 编码是 GBK,而读取时按 UTF-8 处理,就会出现乱码。Fiona 读取 Shapefile 时可以尝试 encoding="gbk"

4. 面数据无效导致前端不显示

WebGIS 前端渲染 GeoJSON 时,对几何质量比较敏感。自相交 Polygon、空几何、重复节点、极小碎面都可能导致显示异常。Shapely 可以做初步检查,但复杂数据最好配合 QGIS 或 ArcGIS Pro 做拓扑清理。

5. GeoJSON文件太大

GeoJSON 是文本格式,体积通常比 Shapefile 或 GeoPackage 更大。如果石家庄道路、建筑物或地块数据特别细,前端一次性加载会很慢。可以考虑简化几何、裁剪范围、删除冗余字段,或改用矢量瓦片。

方法比较:Fiona和Shapely转换GeoJSON与其他工具怎么选

方法 适合场景 优点 注意事项
Fiona + Shapely + PyProj 需要批处理、自动化、几何检查和属性清洗 流程可控,适合写脚本和项目集成 需要理解坐标系和 Python 环境
QGIS另存为GeoJSON 少量数据、人工转换、需要可视化检查 操作直观,适合初学者 批量处理和规则化清洗不如代码灵活
ogr2ogr命令行 格式转换、坐标转换、批处理 速度快,适合自动化脚本 命令参数需要准确,错误时不易排查
GeoPandas 需要同时做表格分析和空间处理 语法接近 Pandas,适合数据分析 大文件内存占用较高
ArcGIS Pro导出 已有 ArcGIS 工作流和地理数据库数据 界面成熟,适合企业数据管理 自动化通常需要 ArcPy 环境和许可

如果只是偶尔把一个 Shapefile 转成 GeoJSON,用 QGIS 最快。如果你要定期处理石家庄多个区县、多个专题图层,推荐使用 Fiona 与 Shapely 写成脚本,这样更容易复用和检查。

检查清单:交付GeoJSON前建议逐项确认

  • 原始 Shapefile 是否包含 .shp.shx.dbf.prj 文件。
  • 是否确认源坐标系,不要只凭文件名猜测。
  • 输出 GeoJSON 是否转换为 EPSG:4326。
  • 坐标数值是否落在石家庄合理经纬度范围附近。
  • 中文字段是否正常显示。
  • 是否检查并处理空几何、无效几何。
  • 属性字段是否包含不能 JSON 序列化的类型。
  • 是否删除了前端不需要的冗余字段。
  • GeoJSON 文件大小是否适合浏览器直接加载。
  • 是否在 QGIS 或 WebGIS 页面中做过最终验证。

FAQ:石家庄GIS数据转GeoJSON常见问题

石家庄GIS数据转GeoJSON一定要转成WGS84吗?

如果用于 Leaflet、OpenLayers 等常见 WebGIS 前端,建议输出 EPSG:4326 的 GeoJSON。虽然某些前端框架可以处理其他投影,但需要额外配置坐标转换。为了通用性和交付便利,WGS84 是最稳妥的选择。

Shapely可以直接读写Shapefile和GeoJSON吗?

Shapely 主要负责几何计算和几何处理,不负责完整的 GIS 文件读写。读取 Shapefile、写出 GeoJSON 通常交给 Fiona,Shapely 负责检查、修复、简化和转换几何对象。

Fiona读取中文字段乱码怎么办?

优先尝试在 fiona.open 中指定编码,例如 encoding="gbk"encoding="utf-8"。如果仍然乱码,说明原始 .dbf 编码可能不标准,建议先用 QGIS 或其他工具确认属性表编码。

为什么GeoJSON在QGIS里正常,但网页上加载很慢?

QGIS 是桌面 GIS 软件,对大文件处理能力更强;浏览器加载 GeoJSON 时,需要下载、解析 JSON、创建图形对象并渲染。若 GeoJSON 过大,网页就会卡顿。可以通过简化几何、裁剪范围、删除字段或使用矢量瓦片解决。

buffer(0)修复几何安全吗?

buffer(0) 可以修复一部分自相交面,但可能改变几何形状或拆分为多部件。对于规划红线、权属边界、管线等高精度数据,不建议只依赖它自动修复,最好人工抽查或使用专业拓扑检查流程。

没有.prj文件还能转换GeoJSON吗?

可以,但前提是你必须知道原始数据的真实坐标系。如果不知道源坐标系,任何转换都可能是错的。建议先联系数据提供方,或用已知控制点、底图叠加和坐标数值范围进行判断。

结论:用代码转换GeoJSON的关键不是写出文件,而是保证位置和质量正确

石家庄 GIS 数据转 GeoJSON 的核心流程可以概括为:确认原始坐标系,读取 Shapefile,修复或过滤问题几何,转换到 EPSG:4326,清洗属性字段,最后用 QGIS 或 WebGIS 验证结果。

Fiona 与 Shapely 的组合适合需要批量处理和可重复交付的场景。Fiona 负责稳定读写 GIS 文件,Shapely 负责几何质量控制,PyProj 负责坐标转换。只要把坐标系、编码和几何有效性这三件事处理好,大多数石家庄 GIS 数据转 GeoJSON 的问题都能被定位并解决。