From 75a8fcf1146db0c2c90aa533f427ce2d14e3af56 Mon Sep 17 00:00:00 2001 From: SuiteForge Date: Wed, 22 Jul 2026 21:43:54 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20Step=201=20=E6=89=8B=E5=8A=A8=E8=A7=A3?= =?UTF-8?q?=E6=9E=90=20GeoJSON=EF=BC=8C=E5=85=BC=E5=AE=B9=20ArcGIS=2010.8?= =?UTF-8?q?=20(#3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- suites/test-windows/scripts/arcpy_convert.py | 115 +++++++++++++++++-- suites/test-windows/workflow.yaml | 2 +- 2 files changed, 107 insertions(+), 10 deletions(-) diff --git a/suites/test-windows/scripts/arcpy_convert.py b/suites/test-windows/scripts/arcpy_convert.py index 4688ae9..2f1d29c 100644 --- a/suites/test-windows/scripts/arcpy_convert.py +++ b/suites/test-windows/scripts/arcpy_convert.py @@ -2,7 +2,10 @@ # -*- coding: utf-8 -*- """ Step 1: GeoJSON → SHP (arcpy) -从内置 data/ 目录读取 GeoJSON,转为 Shapefile +从内置 data/ 目录读取标准 GeoJSON,手动解析后构建 Shapefile。 + +背景(GitHub #3):arcpy.JSONToFeatures_conversion 仅支持 ArcGIS 内部 JSON 格式, +不兼容标准 GeoJSON(RFC 7946),需自行解析坐标并构建要素。 """ import sys import json @@ -11,6 +14,8 @@ import arcpy arcpy.env.overwriteOutput = True +SR_WGS84 = arcpy.SpatialReference(4326) + def get_data_dir(): """返回内置 data/ 目录路径(相对脚本位置)""" @@ -18,6 +23,92 @@ def get_data_dir(): 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(): output_dir = os.environ.get("PARAM_OUTPUT_DIR", r"C:\temp\output\shp") @@ -26,12 +117,12 @@ def main(): data_dir = get_data_dir() - # 数据文件映射 + # 数据文件映射:输出名 → 文件名 geo_files = { "province": "yunnan.geojson", - "kunming": "kunming.geojson", - "dali": "dali.geojson", - "baise": "baise.geojson", + "kunming": "kunming.geojson", + "dali": "dali.geojson", + "baise": "baise.geojson", } converted = [] @@ -40,14 +131,20 @@ def main(): if not os.path.isfile(filepath): print json.dumps({"warn": u"内置数据文件不存在,跳过: " + filepath}) continue + print json.dumps({"msg": u"读取内置数据: " + filename}) shp_path = os.path.join(output_dir, name + ".shp") - arcpy.JSONToFeatures_conversion(filepath, shp_path) - converted.append(name) - print json.dumps({"msg": u"已转换: " + filename + u" → " + shp_path}) + + try: + 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: - print json.dumps({"error": u"未找到任何内置 GeoJSON 数据", "success": False}) + print json.dumps({"error": u"未成功转换任何 GeoJSON 数据", "success": False}) sys.exit(1) result = { diff --git a/suites/test-windows/workflow.yaml b/suites/test-windows/workflow.yaml index ac780df..571f787 100644 --- a/suites/test-windows/workflow.yaml +++ b/suites/test-windows/workflow.yaml @@ -40,7 +40,7 @@ description: | - Step 1、2 依赖系统 **ArcGIS 10.x**(arcpy,Python 2.7 环境) - Step 3 使用系统本机 **Python 3** 运行时(需安装 openpyxl) - 建议安装顺序:ArcGIS → Python 3 → `pip install openpyxl` -version: 1.0.4 +version: 1.0.5 author: Robert platform: windows slug: test-yunnan-overlay-windows