"""GIS parser — Shapefile, GeoJSON, KML via geopandas.""" import os import json import geopandas as gpd def parse_gis(file_path: str) -> dict: ext = os.path.splitext(file_path)[1].lower() try: if ext == ".geojson" or ext == ".json": gdf = gpd.read_file(file_path, driver="GeoJSON") elif ext == ".shp": gdf = gpd.read_file(file_path) elif ext == ".kml": gpd.io.file.fiona.drvsupport.supported_drivers["KML"] = "r" gdf = gpd.read_file(file_path, driver="KML") elif ext == ".gpkg": gdf = gpd.read_file(file_path) else: return {"markdown": "", "error": f"不支持的 GIS 格式: {ext}"} except Exception as e: return {"markdown": "", "error": f"GIS 文件解析失败: {e}"} parts = ["## GIS 数据解析结果\n"] parts.append(f"**文件**: {os.path.basename(file_path)}") parts.append(f"**要素数量**: {len(gdf)}") parts.append(f"**坐标系**: {gdf.crs or '未定义'}\n") # Geometry types geom_types = gdf.geometry.geom_type.value_counts() if not geom_types.empty: parts.append("### 几何类型\n\n| 类型 | 数量 |\n| --- | --- |") for gt, cnt in geom_types.items(): parts.append(f"| {gt} | {cnt} |") parts.append("") # Bounds bounds = gdf.total_bounds # [minx, miny, maxx, maxy] parts.append(f"### 范围\n\n- 最小经度: {bounds[0]:.6f}\n- 最小纬度: {bounds[1]:.6f}\n- 最大经度: {bounds[2]:.6f}\n- 最大纬度: {bounds[3]:.6f}\n") # Attributes non_geom_cols = [c for c in gdf.columns if c != "geometry"] if non_geom_cols: parts.append("### 属性字段\n\n| 字段名 | 类型 | 示例值 |\n| --- | --- | --- |") for col in non_geom_cols: dtype = str(gdf[col].dtype) sample = str(gdf[col].iloc[0]) if len(gdf) > 0 else "" if len(sample) > 80: sample = sample[:80] + "..." parts.append(f"| {col} | {dtype} | {sample} |") parts.append("") # First N features as table n_preview = min(20, len(gdf)) if n_preview > 0 and non_geom_cols: parts.append(f"### 前 {n_preview} 条要素属性\n") header = "| " + " | ".join(non_geom_cols) + " |" sep = "| " + " | ".join("---" for _ in non_geom_cols) + " |" parts.append(header) parts.append(sep) for _, row in gdf.head(n_preview).iterrows(): vals = [] for c in non_geom_cols: v = str(row[c]) if row[c] is not None else "" if len(v) > 60: v = v[:60] + "..." vals.append(v) parts.append("| " + " | ".join(vals) + " |") if len(gdf) > n_preview: parts.append(f"\n> 共 {len(gdf)} 条要素,仅显示前 {n_preview} 条。") parts.append("") return {"markdown": "\n".join(parts)}