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

如何用Python围绕点簇绘制非重叠多边形并聚合至GeoDataFrame?

为点簇生成非重叠自定义多边形解决方案

首先修正你初始代码中的两处问题:一是gpd.points_from_xy调用时用了不存在的Long/Lat列(实际是小写的lon/lat);二是直接为经纬度坐标设置投影坐标系ESRI:102003,正确流程应先指定原始坐标为WGS84(EPSG:4326)再投影。

修正后的基础代码

import pandas as pd
import numpy as np
import geopandas as gpd
from sklearn.cluster import DBSCAN
from shapely.geometry import Polygon
from shapely.ops import unary_union

# 初始化数据
df = pd.DataFrame({
    'yr': [2018, 2017, 2018, 2016],
    'id': [0, 1, 2, 3],
    'v': [10, 12, 8, 10],
    'lat': [32.7418248, 32.8340583, 32.8340583, 32.7471895],
    'lon': [-97.524066, -97.0805484, -97.0805484, -96.9400779]
})

# 创建GeoDataFrame并完成坐标投影
gdf = gpd.GeoDataFrame(
    df,
    geometry=gpd.points_from_xy(df['lon'], df['lat']),
    crs="EPSG:4326"  # 原始经纬度坐标的CRS
)
gdf = gdf.to_crs("ESRI:102003")  # 转换为适合距离计算的投影CRS

步骤1:识别空间点簇

用DBSCAN算法将空间上邻近的点归为同一簇,参数eps(聚类半径)和min_samples(簇内最小点数)可根据你的数据调整:

# 提取点坐标作为聚类输入
coords = np.array([(geom.x, geom.y) for geom in gdf.geometry])

# 执行DBSCAN聚类
dbscan = DBSCAN(eps=10000, min_samples=1)
gdf['cluster_id'] = dbscan.fit_predict(coords)

步骤2:生成自定义多边形(两种可选方案)

方案1:Alpha形状(贴合点簇的不规则多边形)

Alpha形状能生成贴合点集分布的不规则多边形,比凸包更灵活,且确保顶点数≥5:

def alpha_shape(points, alpha=0.0001):
    """基于点集生成Alpha形状多边形"""
    from shapely.ops import triangulate
    from shapely.geometry import MultiPoint
    
    if len(points) < 3:
        return None
    
    multi_point = MultiPoint(points)
    # 生成Delaunay三角网
    triangles = triangulate(multi_point)
    alpha_sq = alpha ** 2
    edges = set()
    
    # 筛选符合Alpha阈值的三角形边
    for tri in triangles:
        vertices = list(tri.exterior.coords)[:-1]
        dists = [
            (vertices[i][0]-vertices[j][0])**2 + (vertices[i][1]-vertices[j][1])**2
            for i, j in [(0,1), (1,2), (2,0)]
        ]
        if all(d <= alpha_sq for d in dists):
            for i in range(3):
                edge = tuple(sorted([vertices[i], vertices[(i+1)%3]]))
                edges.add(edge)
    
    # 构建多边形环
    if not edges:
        return multi_point.convex_hull
    
    edge_map = {}
    for p1, p2 in edges:
        edge_map.setdefault(p1, []).append(p2)
        edge_map.setdefault(p2, []).append(p1)
    
    start = next(iter(edge_map.keys()))
    current, next_p = start, edge_map[start][0]
    poly_coords = [start]
    
    while next_p != start:
        poly_coords.append(next_p)
        neighbors = edge_map[next_p]
        next_p = neighbors[0] if neighbors[1] == current else neighbors[1]
        current = poly_coords[-1]
    
    poly_coords.append(start)
    
    # 若顶点数不足5,用凸包替代
    return Polygon(poly_coords) if len(poly_coords) >=5 else multi_point.convex_hull

# 为每个簇生成Alpha形状多边形
cluster_polys = {}
for cluster_id in gdf['cluster_id'].unique():
    cluster_points = gdf[gdf['cluster_id'] == cluster_id].geometry
    point_coords = [(p.x, p.y) for p in cluster_points]
    cluster_polys[cluster_id] = alpha_shape(point_coords)

# 将多边形匹配到每个点的新列
gdf['cluster_polygon'] = gdf['cluster_id'].map(cluster_polys)

方案2:正N边形(规则非圆形多边形)

如果需要规则形状的多边形,可生成边数≥5的正多边形,围绕簇中心扩展:

def regular_polygon(center, radius, num_sides=5):
    """生成围绕中心点的正N边形"""
    from math import pi, cos, sin
    
    angles = [2 * pi * i / num_sides for i in range(num_sides)]
    coords = [
        (center.x + radius * cos(angle), center.y + radius * sin(angle))
        for angle in angles
    ]
    coords.append(coords[0])  # 闭合多边形
    return Polygon(coords)

# 为每个簇生成正5边形
cluster_polys = {}
for cluster_id in gdf['cluster_id'].unique():
    cluster_gdf = gdf[gdf['cluster_id'] == cluster_id]
    # 计算簇中心
    cluster_center = cluster_gdf.geometry.unary_union.centroid
    # 计算簇内点到中心的最大距离,作为基础半径并添加缓冲
    max_dist = max(cluster_gdf.geometry.distance(cluster_center))
    poly = regular_polygon(cluster_center, max_dist + 2000, num_sides=5)
    cluster_polys[cluster_id] = poly

# 匹配多边形到数据列
gdf['cluster_polygon'] = gdf['cluster_id'].map(cluster_polys)

步骤3:处理多边形重叠

若生成的多边形存在重叠,可通过以下方式处理:

# 合并所有多边形并拆分非重叠部分
all_polys = gpd.GeoSeries(cluster_polys.values())
union = unary_union(all_polys)
# 若合并后为MultiPolygon,可拆分后重新匹配到簇(需根据业务逻辑调整)
if union.geom_type == 'MultiPolygon':
    split_polys = list(union.geoms)
    # 此处需添加逻辑将拆分后的多边形与原簇匹配

最终结果

处理后的gdf新增了cluster_id(簇标识)和cluster_polygon(对应多边形)列,可直接用于分析或导出:

# 查看结果
print(gdf[['id', 'cluster_id', 'cluster_polygon']])

# 可选:转换回WGS84坐标系便于查看
gdf_wgs84 = gdf.to_crs("EPSG:4326")

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:36:25