使用GeoPandas和Folium在Python中绘制区域多边形周长问题
问题:绘制区域外围多边形时出现乱线
我尝试基于场地的经纬度点绘制外围周长多边形,但结果是一团相互连接的乱线,不是预期的外围轮廓。我的代码如下:
#Import the source data and libraries import pandas as pd import geopandas as gpd import folium from shapely.geometry import Polygon df = pd.read_csv('tacs.csv') #Extract the lat long lists from the datasource lat_point_list = df['magnet.latitude'].tolist() lon_point_list = df['magnet.longitude'].tolist() #Some wizardry polygon_geom = Polygon(zip(lon_point_list, lat_point_list)) crs = {'init': 'epsg:4326'} polygon = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom]) #output to files polygon.to_file(filename='polygon.geojson', driver='GeoJSON') polygon.to_file(filename='polygon.shp', driver="ESRI Shapefile") #plot on a map with central point being birmingham m = folium.Map([51.509865, -0.118092], zoom_start=12, tiles='cartodbpositron') folium.GeoJson(polygon).add_to(m) folium.LatLngPopup().add_to(m) m
解决方案
出现乱线的核心原因是:你传入Polygon的经纬度点没有按区域外围的顺时针/逆时针顺序排列。Shapely的Polygon会严格按照给定的点顺序依次连接,点顺序混乱的话就会出现交叉、重叠的线条。
根据你的数据类型,这里提供两种针对性的解决方法:
方法一:用凸包自动生成外围轮廓(适合散点数据)
如果你的CSV里的点是区域内的散点(比如采集的多个磁测点),而非预先按顺序排列的外围边界点,最简便的方法是使用Shapely的convex_hull生成所有点的外围凸多边形:
修改代码中的多边形生成部分:
# 新增导入MultiPoint from shapely.geometry import Polygon, MultiPoint #Extract the lat long lists from the datasource lat_point_list = df['magnet.latitude'].tolist() lon_point_list = df['magnet.longitude'].tolist() # 先将点转为MultiPoint对象,再生成凸包(外围轮廓) points = MultiPoint(zip(lon_point_list, lat_point_list)) polygon_geom = points.convex_hull # 这一步替换原来的Polygon生成代码 # 后续代码保持不变 crs = {'init': 'epsg:4326'} polygon = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom]) ...
凸包会自动计算所有点的最外围边界,避免点顺序混乱带来的交叉问题。
方法二:Delaunay三角剖分提取非凸区域边界(适合凹形区域)
如果你的场地是凹形的,凸包会忽略凹进去的部分,这时候可以用Delaunay三角剖分提取精准的外围边界。需要先安装scipy库:
pip install scipy
然后修改代码:
import pandas as pd import geopandas as gpd import folium from shapely.geometry import Polygon from scipy.spatial import Delaunay import numpy as np df = pd.read_csv('tacs.csv') # 先清理空值 df = df.dropna(subset=['magnet.latitude', 'magnet.longitude']) lat_point_list = df['magnet.latitude'].tolist() lon_point_list = df['magnet.longitude'].tolist() # 将经纬度转为numpy数组 points_np = np.array(list(zip(lon_point_list, lat_point_list))) # 执行Delaunay三角剖分 tri = Delaunay(points_np) # 提取只出现一次的边(即外围边界边) edges = set() for simplex in tri.simplices: for i in range(3): # 对边的两个点索引排序,避免重复记录 edge = tuple(sorted((simplex[i], simplex[(i+1)%3]))) if edge in edges: edges.remove(edge) else: edges.add(edge) # 将边界点按顺序连接成闭合多边形 current_idx = next(iter(edges))[0] ordered_indices = [current_idx] while edges: # 找到包含当前点的边 next_edge = next((e for e in edges if current_idx in e), None) if not next_edge: break # 切换到边的另一个端点 current_idx = next_edge[0] if next_edge[1] == current_idx else next_edge[1] ordered_indices.append(current_idx) edges.remove(next_edge) # 闭合多边形 ordered_indices.append(ordered_indices[0]) # 生成最终的多边形 polygon_geom = Polygon(points_np[ordered_indices]) # 后续代码保持不变 crs = {'init': 'epsg:4326'} polygon = gpd.GeoDataFrame(index=[0], crs=crs, geometry=[polygon_geom]) ...
额外检查建议
- 先清理数据:用
df.dropna(subset=['magnet.latitude', 'magnet.longitude'])移除空值或异常点,避免干扰边界计算。 - 如果是手动采集的外围点:确保记录点时是按顺时针/逆时针的顺序依次采集的,这样直接用
Polygon(zip(...))就能得到正确的轮廓。
内容的提问来源于stack exchange,提问作者kikee1222
相关产品推荐
相关产品推荐

