GeoDjango如何加载多CRS的.shp文件并转为WGS84的GeoJSON?
针对你的需求——在Django中将不同CRS的SHP文件转换为WGS84(EPSG:4326)的GeoJSON,再给Leaflet展示,我最推荐GDAL/OGR相关的方案,这是空间数据处理领域的标准工具链,CRS转换支持全面且可靠。下面逐个分析你提到的三个方案:
1. ogr2ogr(最直接高效,优先推荐)
ogr2ogr是GDAL工具集里的核心转换工具,对各种CRS的支持非常完善,能一键完成SHP到GeoJSON的格式转换+CRS转WGS84,完全不需要自己处理坐标计算。你有两种使用方式:
方式一:直接在Django中调用命令行(简单快速)
在Django视图里通过subprocess调用ogr2ogr命令,生成GeoJSON后读取内容返回给前端:
import subprocess import os from django.http import HttpResponse def shp_to_geojson(request, shp_path): # 临时输出GeoJSON路径(可以用Django的临时文件管理) temp_geojson = "/tmp/output.geojson" # 执行ogr2ogr命令:转换为GeoJSON,目标CRS设为WGS84 subprocess.run([ "ogr2ogr", "-f", "GeoJSON", "-t_srs", "EPSG:4326", temp_geojson, shp_path ], check=True) # 读取生成的GeoJSON内容 with open(temp_geojson, "r") as f: geojson_str = f.read() # 删除临时文件(可选) os.remove(temp_geojson) return HttpResponse(geojson_str, content_type="application/json")
方式二:使用GDAL的Python绑定(osgeo.ogr),更集成化
如果不想调用外部命令,可以直接用Python的GDAL库(GeoDjango默认已经依赖这个),在代码里完成转换:
from osgeo import ogr, osr import json from django.http import HttpResponse def shp_to_geojson(request, shp_path): # 打开SHP文件 driver = ogr.GetDriverByName("ESRI Shapefile") data_source = driver.Open(shp_path, 0) # 0表示只读 layer = data_source.GetLayer() # 设置源CRS和目标CRS(WGS84) source_crs = layer.GetSpatialRef() target_crs = osr.SpatialReference() target_crs.ImportFromEPSG(4326) transform = osr.CoordinateTransformation(source_crs, target_crs) # 构建GeoJSON结构 geojson = { "type": "FeatureCollection", "features": [] } # 遍历所有要素 for feature in layer: # 转换几何坐标 geom = feature.GetGeometryRef() geom.Transform(transform) # 获取属性 properties = {} for i in range(feature.GetFieldCount()): field_name = feature.GetFieldDefnRef(i).GetName() properties[field_name] = feature.GetField(i) # 添加到FeatureCollection geojson["features"].append({ "type": "Feature", "geometry": json.loads(geom.ExportToJson()), "properties": properties }) return HttpResponse(json.dumps(geojson), content_type="application/json")
2. GeoDjango的DataSource
其实GeoDjango的DataSource就是对GDAL/OGR的封装,和上面的osgeo.ogr方案本质一样,但更贴合GeoDjango的开发习惯。你可以用它来读取SHP,然后结合Django的空间工具完成CRS转换:
from django.contrib.gis.gdal import DataSource, SpatialReference import json from django.http import HttpResponse def shp_to_geojson(request, shp_path): ds = DataSource(shp_path) layer = ds[0] # 设置目标CRS为WGS84 target_srs = SpatialReference(4326) geojson = { "type": "FeatureCollection", "features": [] } for feat in layer: # 转换几何 geom = feat.geom.transform(target_srs, clone=True) # 获取属性 props = dict(feat.fields.items()) # 添加到GeoJSON geojson["features"].append({ "type": "Feature", "geometry": json.loads(geom.json), "properties": props }) return HttpResponse(json.dumps(geojson), content_type="application/json")
这个方案的好处是和GeoDjango的生态完全兼容,如果你的项目已经在用GeoDjango,用这个会更顺手。
3. PyShp(不推荐用于多CRS场景)
PyShp的优势是轻量,但它本身不支持CRS解析和转换,你需要额外依赖pyproj库来处理坐标转换,还要自己解析SHP附带的.prj文件来获取源CRS信息,步骤繁琐且容易出错。比如你需要先读取.prj文件,用pyproj创建转换对象,然后逐个遍历shape的坐标点进行转换,这种方式只适合简单的、已知CRS的场景,面对多个不同CRS的SHP文件时,维护成本很高,所以不推荐。
总结
- 如果追求最快上手、最少代码,选ogr2ogr命令行方式;
- 如果要和GeoDjango深度集成,选DataSource方案;
- PyShp只适合不需要CRS转换的简单SHP处理场景,你的需求下不建议使用。
内容的提问来源于stack exchange,提问作者user1162541

