fix: Step 1 手动解析 GeoJSON,兼容 ArcGIS 10.8 (#3)

arcpy.JSONToFeatures_conversion 仅支持 ArcGIS 内部 JSON 格式,
不兼容标准 GeoJSON(RFC 7946),ArcGIS 10.8 报 ERROR 001558。

重写 arcpy_convert.py:
- 自行读取 GeoJSON 文件,解析 coordinates
- 使用 arcpy.Polygon + InsertCursor 手动构建面要素
- 支持 Polygon 和 MultiPolygon 两种几何类型
- 保留原有字段(name, code, population)
- 保持 # -*- coding: utf-8 -*- 声明

workflow.yaml: version 1.0.4 → 1.0.5
This commit is contained in:
SuiteForge
2026-07-22 21:43:54 +08:00
parent 52858c151b
commit 75a8fcf114
2 changed files with 107 additions and 10 deletions
+106 -9
View File
@@ -2,7 +2,10 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Step 1: GeoJSON → SHP (arcpy) Step 1: GeoJSON → SHP (arcpy)
从内置 data/ 目录读取 GeoJSON转为 Shapefile 从内置 data/ 目录读取标准 GeoJSON手动解析后构建 Shapefile
背景(GitHub #3):arcpy.JSONToFeatures_conversion 仅支持 ArcGIS 内部 JSON 格式,
不兼容标准 GeoJSON(RFC 7946),需自行解析坐标并构建要素。
""" """
import sys import sys
import json import json
@@ -11,6 +14,8 @@ import arcpy
arcpy.env.overwriteOutput = True arcpy.env.overwriteOutput = True
SR_WGS84 = arcpy.SpatialReference(4326)
def get_data_dir(): def get_data_dir():
"""返回内置 data/ 目录路径(相对脚本位置)""" """返回内置 data/ 目录路径(相对脚本位置)"""
@@ -18,6 +23,92 @@ def get_data_dir():
return os.path.normpath(os.path.join(script_dir, "..", "data")) return os.path.normpath(os.path.join(script_dir, "..", "data"))
def coords_to_arcpy_polygon(coordinates):
"""
将 GeoJSON Polygon 坐标数组转为 arcpy Polygon 对象。
GeoJSON Polygon.coordinates = [outer_ring, hole1, hole2, ...]
每个 ring = [[x1,y1], [x2,y2], ...](首尾闭合)
"""
rings = arcpy.Array()
for ring in coordinates:
pts = arcpy.Array()
for pt in ring:
# GeoJSON 坐标顺序是 [经度, 纬度] = [x, y]
pts.add(arcpy.Point(pt[0], pt[1]))
rings.add(pts)
return arcpy.Polygon(rings, SR_WGS84)
def process_geojson(filepath, shp_path):
"""读取单个 GeoJSON 文件,写入 Shapefile"""
with open(filepath, "r") as f:
geojson = json.load(f)
fc_type = geojson.get("type", "")
features = geojson.get("features", [])
if fc_type != "FeatureCollection" or not features:
raise ValueError(u"GeoJSON 应为 FeatureCollection,包含至少一个 Feature")
# 从第一个要素探查属性字段
sample_props = features[0].get("properties", {})
field_defs = []
for key, val in sample_props.items():
if isinstance(val, int):
field_defs.append((key, "LONG"))
elif isinstance(val, float):
field_defs.append((key, "DOUBLE"))
else:
field_defs.append((key, "TEXT"))
# 创建 Shapefile(面要素)
out_path, out_name = os.path.split(shp_path)
arcpy.CreateFeatureclass_management(
out_path, out_name, "POLYGON",
spatial_reference=SR_WGS84
)
# 添加属性字段(Shapefile 字段名最多 10 字符,这里都满足)
for fname, ftype in field_defs:
arcpy.AddField_management(shp_path, fname, ftype)
# 写入要素
field_names = ["SHAPE@"] + [f[0] for f in field_defs]
with arcpy.da.InsertCursor(shp_path, field_names) as cursor:
for feat in features:
geom = feat.get("geometry", {})
props = feat.get("properties", {})
geom_type = geom.get("type")
coords = geom.get("coordinates", [])
if geom_type == "Polygon":
shape = coords_to_arcpy_polygon(coords)
elif geom_type == "MultiPolygon":
# 多个 Polygon 合并为一个 MultiPolygon
parts = arcpy.Array()
for polygon_coords in coords:
for ring in polygon_coords:
pts = arcpy.Array()
for pt in ring:
pts.add(arcpy.Point(pt[0], pt[1]))
parts.add(pts)
shape = arcpy.Polygon(parts, SR_WGS84)
else:
print json.dumps({"warn": u"跳过不支持的几何类型: " + str(geom_type)})
continue
row = [shape]
for fname, _ in field_defs:
val = props.get(fname)
if val is None:
val = 0 if _ in ("LONG", "DOUBLE") else ""
row.append(val)
cursor.insertRow(tuple(row))
def main(): def main():
output_dir = os.environ.get("PARAM_OUTPUT_DIR", r"C:\temp\output\shp") output_dir = os.environ.get("PARAM_OUTPUT_DIR", r"C:\temp\output\shp")
@@ -26,12 +117,12 @@ def main():
data_dir = get_data_dir() data_dir = get_data_dir()
# 数据文件映射 # 数据文件映射:输出名 → 文件名
geo_files = { geo_files = {
"province": "yunnan.geojson", "province": "yunnan.geojson",
"kunming": "kunming.geojson", "kunming": "kunming.geojson",
"dali": "dali.geojson", "dali": "dali.geojson",
"baise": "baise.geojson", "baise": "baise.geojson",
} }
converted = [] converted = []
@@ -40,14 +131,20 @@ def main():
if not os.path.isfile(filepath): if not os.path.isfile(filepath):
print json.dumps({"warn": u"内置数据文件不存在,跳过: " + filepath}) print json.dumps({"warn": u"内置数据文件不存在,跳过: " + filepath})
continue continue
print json.dumps({"msg": u"读取内置数据: " + filename}) print json.dumps({"msg": u"读取内置数据: " + filename})
shp_path = os.path.join(output_dir, name + ".shp") shp_path = os.path.join(output_dir, name + ".shp")
arcpy.JSONToFeatures_conversion(filepath, shp_path)
converted.append(name) try:
print json.dumps({"msg": u"已转换: " + filename + u"" + shp_path}) process_geojson(filepath, shp_path)
converted.append(name)
print json.dumps({"msg": u"已转换: " + filename + u" \u2192 " + shp_path})
except Exception as e:
print json.dumps({"error": u"转换失败 " + filename + u": " + str(e), "success": False})
sys.exit(1)
if not converted: if not converted:
print json.dumps({"error": u"找到任何内置 GeoJSON 数据", "success": False}) print json.dumps({"error": u"成功转换任何 GeoJSON 数据", "success": False})
sys.exit(1) sys.exit(1)
result = { result = {
+1 -1
View File
@@ -40,7 +40,7 @@ description: |
- Step 1、2 依赖系统 **ArcGIS 10.x**arcpyPython 2.7 环境) - Step 1、2 依赖系统 **ArcGIS 10.x**arcpyPython 2.7 环境)
- Step 3 使用系统本机 **Python 3** 运行时(需安装 openpyxl - Step 3 使用系统本机 **Python 3** 运行时(需安装 openpyxl
- 建议安装顺序:ArcGIS → Python 3 → `pip install openpyxl` - 建议安装顺序:ArcGIS → Python 3 → `pip install openpyxl`
version: 1.0.4 version: 1.0.5
author: Robert author: Robert
platform: windows platform: windows
slug: test-yunnan-overlay-windows slug: test-yunnan-overlay-windows