You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从MultiPolygon GeoJSON提取各城市独立的海岸/非交界边界线?

提取城市独立边界/海岸线的实现方案

完全可行,核心是识别多边形的非共享边——也就是仅属于单个城市的边界(对应你需求中的彩色线条)。以下是具体实现方法:


一、软件工具实现(QGIS)

无需编程,用开源GIS工具QGIS即可完成:

  • 步骤1:加载你的GeoJSON文件到QGIS
  • 步骤2:拆分MultiPolygon为单个Polygon:
    打开Vector > Geometry Tools > Multipart to Singleparts,输入原图层,输出新的单多边形图层
  • 步骤3:提取所有边界线:
    打开Vector > Geometry Tools > Polygons to Lines,输入上一步的图层,输出所有边界线段图层
  • 步骤4:识别共享边界:
    1. 打开Vector > Geoprocessing Tools > Dissolve,勾选Dissolve all,合并所有线段
    2. 打开Vector > Analysis Tools > Count Overlapping Features,输入合并后的图层,得到每条线段的重叠次数(即被多少城市共享)
  • 步骤5:筛选独立边界:
    打开线段图层的属性表,筛选count = 1的记录,这些就是单个城市独有的边界
  • 步骤6:按城市拆分保存:
    打开Vector > Data Management Tools > Split vector layer,选择城市名称字段,设置输出文件夹,每个城市会生成独立的GeoJSON文件

二、编程实现

Python方案(Geopandas + Shapely)

适合批量处理或自动化场景,需要安装依赖:pip install geopandas shapely pandas

import geopandas as gpd
from shapely.geometry import LineString
from shapely.ops import unary_union, linemerge

# 加载GeoJSON数据
gdf = gpd.read_file("你的城市数据.geojson")

# 1. 提取所有城市的边界线段(拆分为单条线段)
all_lines = []
for idx, row in gdf.iterrows():
    city_name = row["city"]  # 替换为你数据中的城市名字段
    # 遍历MultiPolygon中的每个Polygon
    for polygon in row.geometry.geoms:
        # 处理外环(海岸线/主边界)
        exterior_coords = list(polygon.exterior.coords)
        for i in range(len(exterior_coords)-1):
            line = LineString([exterior_coords[i], exterior_coords[i+1]])
            all_lines.append({"city": city_name, "geometry": line})
        # 可选:处理内环(如城市内的湖泊边界)
        # for interior in polygon.interiors:
        #     interior_coords = list(interior.coords)
        #     for i in range(len(interior_coords)-1):
        #         line = LineString([interior_coords[i], interior_coords[i+1]])
        #         all_lines.append({"city": city_name, "geometry": line})

# 转为GeoDataFrame
lines_gdf = gpd.GeoDataFrame(all_lines, crs=gdf.crs)

# 2. 统计每条线段的共享城市数量
merged_lines = unary_union(lines_gdf.geometry)
unique_lines = list(merged_lines.geoms)

line_counts = []
for line in unique_lines:
    # 匹配重合线段(设置精度容差避免浮点误差)
    matches = lines_gdf[lines_gdf.geometry.equals_exact(line, tolerance=0.0001)]
    unique_cities = matches["city"].unique()
    line_counts.append({
        "geometry": line,
        "count": len(unique_cities),
        "cities": unique_cities.tolist()
    })

counts_gdf = gpd.GeoDataFrame(line_counts, crs=gdf.crs)

# 3. 筛选非共享线段并按城市保存
non_shared_lines = counts_gdf[counts_gdf["count"] == 1]

for city in non_shared_lines["cities"].apply(lambda x: x[0]).unique():
    city_segments = non_shared_lines[non_shared_lines["cities"].apply(lambda x: x[0] == city)]
    # 合并同城市的连续线段(可选)
    merged_city_line = linemerge(unary_union(city_segments.geometry))
    # 保存为GeoJSON
    city_gdf = gpd.GeoDataFrame({"city": [city], "geometry": [merged_city_line]}, crs=gdf.crs)
    city_gdf.to_file(f"{city}_独立边界.geojson", driver="GeoJSON")

JS方案(Turf.js)

适合前端或Node.js环境,依赖安装:npm install @turf/turf(Node.js),前端可直接引入CDN

const turf = require('@turf/turf');
const fs = require('fs');

// 加载GeoJSON数据
const cityData = JSON.parse(fs.readFileSync('你的城市数据.geojson', 'utf8'));

// 1. 提取所有城市的边界线段
const allLines = [];
cityData.features.forEach(feature => {
    const cityName = feature.properties.city; // 替换为你数据中的城市名字段
    // 处理MultiPolygon的每个Polygon
    feature.geometry.coordinates.forEach(polygonCoords => {
        // 处理外环
        const exterior = polygonCoords[0];
        for (let i = 0; i < exterior.length - 1; i++) {
            const line = turf.lineString([exterior[i], exterior[i+1]], {city: cityName});
            allLines.push(line);
        }
        // 可选:处理内环
        // for (let j = 1; j < polygonCoords.length; j++) {
        //     const interior = polygonCoords[j];
        //     for (let i = 0; i < interior.length - 1; i++) {
        //         const line = turf.lineString([interior[i], interior[i+1]], {city: cityName});
        //         allLines.push(line);
        //     }
        // }
    });
});

// 2. 统计每条线段的共享城市数
const mergedLines = turf.lineMerge(turf.featureCollection(allLines));
const uniqueLines = turf.lineSplit(mergedLines, mergedLines).features;

const lineStats = uniqueLines.map(line => {
    // 匹配重合线段(截断精度避免浮点误差)
    const matches = allLines.filter(l => turf.booleanEqual(turf.truncate(l, {precision: 6}), turf.truncate(line, {precision: 6})));
    const uniqueCities = [...new Set(matches.map(l => l.properties.city))];
    return {
        ...line,
        properties: {
            count: uniqueCities.length,
            cities: uniqueCities
        }
    };
});

// 3. 筛选非共享线段并按城市分组保存
const nonSharedLines = lineStats.filter(stat => stat.properties.count === 1);

const cityGroups = {};
nonSharedLines.forEach(line => {
    const city = line.properties.cities[0];
    cityGroups[city] = cityGroups[city] || [];
    cityGroups[city].push(line);
});

// 保存每个城市的边界文件
Object.keys(cityGroups).forEach(city => {
    const featureCollection = turf.featureCollection(cityGroups[city]);
    fs.writeFileSync(`${city}_独立边界.geojson`, JSON.stringify(featureCollection, null, 2));
});

内容的提问来源于stack exchange,提问作者good112233

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 22:50:07