如何将GeoPandas中2D LineString转为带不同Z值的3D LineString?
给两点2D LineString添加不同端点Z坐标的直接实现
需求概述
现有GeoDataFrame,其中每条2D LineString仅包含两个端点,且每条线对应两个不同的Z值(z1对应起点,z2对应终点),需要将这些Z值分别添加到对应端点生成3D LineString,要求实现方式比“先创建3D点再生成LineString”更直接,且不使用统一Z值的方法。
解决方案一:直接构造3D LineString
利用Shapely的LineString构造器,直接将原2D坐标与对应Z值打包成3D坐标元组,无需显式创建3D点对象,写法简洁高效:
from shapely.geometry import LineString # 为每条线生成3D LineString gdf['geometry'] = gdf.apply( lambda row: LineString( [(x, y, z) for (x, y), z in zip(row['geometry'].coords, [row['z1'], row['z2']])] ), axis=1 )
说明
- 遍历每条线的2D坐标对,与对应的
z1、z2一一配对,直接生成3D坐标列表 - 一步完成3D LineString的创建,比分步创建点再连线更简洁
解决方案二:使用shapely.ops.transform实现
通过闭包维护当前处理的端点索引,让transform函数为每个端点匹配对应的Z值,满足你尝试使用transform的需求:
from shapely.ops import transform def create_z_transformer(z_values): # 用闭包记录当前处理的点索引 index = 0 def transformer(x, y): nonlocal index # 返回带Z值的3D坐标 result = (x, y, z_values[index]) # 切换到下一个Z值(仅针对两点的线,循环后回到0不影响) index = (index + 1) % len(z_values) return result return transformer # 应用transform到每条线 gdf['geometry'] = gdf.apply( lambda row: transform(create_z_transformer([row['z1'], row['z2']]), row['geometry']), axis=1 )
说明
create_z_transformer返回一个带状态的转换函数,每次处理坐标点时会自动切换到对应的Z值- 完美适配两点LineString的场景,确保起点用
z1,终点用z2
完整可运行代码
将上述方案整合到你的代码中:
import pandas as pd import geopandas as gpd from shapely import wkt from shapely.geometry import LineString from shapely.ops import transform df = pd.DataFrame( { "ID": [1, 2, 3], "z1": [133.56, 184.84, 230.53], "z2": [158.66, 212.68, 241.20], "geometry": [ "LINESTRING (565035.0499992011 6622605.24012313, 565064.889999201 6622483.90012313)", "LINESTRING (565084.009999201 6622367.28012313, 565101.339999201 6622257.27012313)", "LINESTRING (565119.8599992 6622140.37012313, 565144.4599992 6621985.04012313)"] } ) df["geometry"] = gpd.GeoSeries.from_wkt(df["geometry"]) gdf = gpd.GeoDataFrame(df, geometry="geometry") gdf = gdf.set_crs('epsg:25832') # 选择其中一种方案执行: # 方案一 gdf['geometry'] = gdf.apply( lambda row: LineString( [(x, y, z) for (x, y), z in zip(row['geometry'].coords, [row['z1'], row['z2']])] ), axis=1 ) # 或者方案二 # def create_z_transformer(z_values): # index = 0 # def transformer(x, y): # nonlocal index # result = (x, y, z_values[index]) # index = (index + 1) % len(z_values) # return result # return transformer # gdf['geometry'] = gdf.apply( # lambda row: transform(create_z_transformer([row['z1'], row['z2']]), row['geometry']), # axis=1 # ) # 验证结果 print(gdf['geometry'])
运行后,geometry列将变为LINESTRING Z类型,每个端点都带有对应的Z值,与你期望的输出一致。
内容的提问来源于stack exchange,提问作者Lars Christian Østgaard
相关产品推荐
相关产品推荐

