如何拆分由多个线串组成的 shapefile
How to split a shapefile compose of multiple linestrings
我有一个由 4000 个特征(线)构成的区域网络的 shapefile。我想分割长于 500 米的线,以便每条线的最大长度为 500 米。比如一条7公里长的线路,需要裁剪14个部分,每个部分都要保持和原来一样的属性。
我尝试使用 Shapely 中的函数 cut
,但它不起作用,因为我需要分离地理数据框中要素的几何形状以切割线。
为了解决这个问题,我决定开发一个功能,根据条数将一条线切割成多条线。
首先,我检查了 shapely 用户手册 (https://shapely.readthedocs.io/en/latest/manual.html) 中的 "cut" 函数。此函数允许将线 (LineString) 分成两部分,线和距离作为参数
def cut(line, distance):
# Cuts a line in two at a distance from its starting point
if distance <= 0.0 or distance >= line.length:
return [LineString(line)]
coords = list(line.coords)
for i, p in enumerate(coords):
pd = line.project(Point(p))
if pd == distance:
return [
LineString(coords[:i+1]),
LineString(coords[i:])]
if pd > distance:
cp = line.interpolate(distance)
return [
LineString(coords[:i] + [(cp.x, cp.y)]),
LineString([(cp.x, cp.y)] + coords[i:])]
然后,我使用这个函数使用参数块和线多次切割一个LineString。 Pieces 用于将线的长度等分,以便切割线。
MultiCut 是用要切割的线和您想从该线获得的件数来定义的
def MultiCut(line, pieces):
#Firts we need to define two list to append the results (lines) and the lines to cut
lines_result = [] #All the lines are add to this list
lines_to_cut = [] #The lines to cut are add to this list
#We must ensure that pieces are higher than 2
if pieces == 1: #If pieces are 1 the result is the same line
lines_result.append(line)
elif pieces == 2: #If pieces are 2 the result is the line cut by the middle point
distance = (line.length)/pieces
lines_result.append(cut(line, distance)[0])
lines_result.append(cut(line, distance)[1])
else: # If pieces is more than 3 we star to cut the line in the number of pieces
# We use a loop from the first to the penultimate piece
for i in range(1, pieces):
# The first piece is cut to save the result and the rest of the line
if i == 1:
distance = (line.length)/pieces #Distance is calculate as the lenght of the line divided by the number of pieces
lines_result.append(cut(line, distance)[0]) #We cut the line and
save the first part in lines result
lines_to_cut = cut(line, distance)[1] #We save the rest of the line in lines to cut in order to continue with the split
#If pieces are equal to pieces minus two we can split the line and
#save only the first part in lines result in order to continue with
#the split
if 1 < i <= pieces - 2:
distance = (line.length)/pieces
lines_result.append(cut(lines_to_cut, distance)[0])
lines_to_cut = cut(lines_to_cut, distance)[1]
#Finally if pieces are equal to pieces minus 1 we can cut the line
#and save both of the parts in lines result
if (i != 1) and (i == pieces-1):
distance = (line.length)/pieces
lines_result.append(cut(lines_to_cut, distance)[0])
lines_result.append(cut(lines_to_cut, distance)[1])
return lines_result
最后,我们将线切割成您想要的件数。例如,如果您有一排 5 个单位:
line = LineString([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)])
您可以使用距离分割线
cut(line, 2.5)
或者,您可以使用 MultiCut 将线分割成您需要的部分
MultiCut(line, 2) #Cut the line by middle
MultiCut(line, 5) #Cut the line in five parts with 1 as distance
因为我需要在多行中剪切一个 shapefile,所以我在一个循环中使用 MultiCut 来完成这项工作。
import pandas as pd
import geopandas as gpd
import shapely.geometry as geom
import numpy as np
from shapely.geometry import LineString
from shapely.geometry import Point
from shapely.geometry import MultiLineString
#I read the shapefile with geopandas
df = gpd.read_file('Red_local_Samana.shp')
#I project the shapefile
df = df.to_crs("EPSG:3115")
#I took the lines (LineString) of the geodataframe
network_to_cut = df.geometry
results = []
for j in range(0, len(network_to_cut)):
if network_to_cut[j].length > 500:
line = network_to_cut[j]
pieces = int(((line.length)//500)+1)
results.append(list(MultiCut(line, pieces)))
通过这种方法,我将 shapefile 中所有高于 500 米的线分割成至少 500 米的片段
我有一个由 4000 个特征(线)构成的区域网络的 shapefile。我想分割长于 500 米的线,以便每条线的最大长度为 500 米。比如一条7公里长的线路,需要裁剪14个部分,每个部分都要保持和原来一样的属性。
我尝试使用 Shapely 中的函数 cut
,但它不起作用,因为我需要分离地理数据框中要素的几何形状以切割线。
为了解决这个问题,我决定开发一个功能,根据条数将一条线切割成多条线。
首先,我检查了 shapely 用户手册 (https://shapely.readthedocs.io/en/latest/manual.html) 中的 "cut" 函数。此函数允许将线 (LineString) 分成两部分,线和距离作为参数
def cut(line, distance):
# Cuts a line in two at a distance from its starting point
if distance <= 0.0 or distance >= line.length:
return [LineString(line)]
coords = list(line.coords)
for i, p in enumerate(coords):
pd = line.project(Point(p))
if pd == distance:
return [
LineString(coords[:i+1]),
LineString(coords[i:])]
if pd > distance:
cp = line.interpolate(distance)
return [
LineString(coords[:i] + [(cp.x, cp.y)]),
LineString([(cp.x, cp.y)] + coords[i:])]
然后,我使用这个函数使用参数块和线多次切割一个LineString。 Pieces 用于将线的长度等分,以便切割线。
MultiCut 是用要切割的线和您想从该线获得的件数来定义的
def MultiCut(line, pieces):
#Firts we need to define two list to append the results (lines) and the lines to cut
lines_result = [] #All the lines are add to this list
lines_to_cut = [] #The lines to cut are add to this list
#We must ensure that pieces are higher than 2
if pieces == 1: #If pieces are 1 the result is the same line
lines_result.append(line)
elif pieces == 2: #If pieces are 2 the result is the line cut by the middle point
distance = (line.length)/pieces
lines_result.append(cut(line, distance)[0])
lines_result.append(cut(line, distance)[1])
else: # If pieces is more than 3 we star to cut the line in the number of pieces
# We use a loop from the first to the penultimate piece
for i in range(1, pieces):
# The first piece is cut to save the result and the rest of the line
if i == 1:
distance = (line.length)/pieces #Distance is calculate as the lenght of the line divided by the number of pieces
lines_result.append(cut(line, distance)[0]) #We cut the line and
save the first part in lines result
lines_to_cut = cut(line, distance)[1] #We save the rest of the line in lines to cut in order to continue with the split
#If pieces are equal to pieces minus two we can split the line and
#save only the first part in lines result in order to continue with
#the split
if 1 < i <= pieces - 2:
distance = (line.length)/pieces
lines_result.append(cut(lines_to_cut, distance)[0])
lines_to_cut = cut(lines_to_cut, distance)[1]
#Finally if pieces are equal to pieces minus 1 we can cut the line
#and save both of the parts in lines result
if (i != 1) and (i == pieces-1):
distance = (line.length)/pieces
lines_result.append(cut(lines_to_cut, distance)[0])
lines_result.append(cut(lines_to_cut, distance)[1])
return lines_result
最后,我们将线切割成您想要的件数。例如,如果您有一排 5 个单位:
line = LineString([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)])
您可以使用距离分割线
cut(line, 2.5)
或者,您可以使用 MultiCut 将线分割成您需要的部分
MultiCut(line, 2) #Cut the line by middle
MultiCut(line, 5) #Cut the line in five parts with 1 as distance
因为我需要在多行中剪切一个 shapefile,所以我在一个循环中使用 MultiCut 来完成这项工作。
import pandas as pd
import geopandas as gpd
import shapely.geometry as geom
import numpy as np
from shapely.geometry import LineString
from shapely.geometry import Point
from shapely.geometry import MultiLineString
#I read the shapefile with geopandas
df = gpd.read_file('Red_local_Samana.shp')
#I project the shapefile
df = df.to_crs("EPSG:3115")
#I took the lines (LineString) of the geodataframe
network_to_cut = df.geometry
results = []
for j in range(0, len(network_to_cut)):
if network_to_cut[j].length > 500:
line = network_to_cut[j]
pieces = int(((line.length)//500)+1)
results.append(list(MultiCut(line, pieces)))
通过这种方法,我将 shapefile 中所有高于 500 米的线分割成至少 500 米的片段