문제 설명
여러 선스트링으로 구성된 shapefile을 분할하는 방법 (How to split a shapefile compose of multiple linestrings)
4000개의 기능(선)으로 구성된 지역 네트워크의 shapefile이 있습니다. 각 라인의 최대 길이가 500m가 되도록 500m보다 긴 라인을 분할하고 싶습니다. 예를 들어 7km 라인의 경우 14개의 부품을 절단해야 하며 각 부품은 원본과 동일한 속성을 유지해야 합니다.
Shaply의 cut
기능을 사용해 보았지만 선을 자르기 위해 지오데이터 프레임에 있는 기능의 지오메트리를 분리해야 하기 때문에 작동하지 않습니다.
참조 솔루션
방법 1:
In order to solve the problem I decide to develop a function to cut a line in multiple lines according to number of pieces.
First of all I checked "cut" function from shapely users manual (https://shapely.readthedocs.io/en/latest/manual.html). This function allows to cut a line (LineString) in two parts with the line and the distance as arguments
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:])]
Then, I use this function to cut a LineString multiple times using the argument pieces and the line. Pieces is use to divided the length of the line in equal parts in order to cut the line.
MultiCut is define with the line to cut and the number of pieces that you want to obtain from the line
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
Finally we have the line cut in the number of pieces that you want. For example if you have a line of 5 units:
line = LineString([(0, 0), (1, 0), (2, 0), (3, 0), (4, 0), (5, 0)])
You can split the line with cut using a distance
cut(line, 2.5)
Or, you can use MultiCut to split the line in the number of pieces that you need
MultiCut(line, 2) #Cut the line by middle
MultiCut(line, 5) #Cut the line in five parts with 1 as distance
Because I need to cut a shapefile in many lines, I use MultiCut in a loop to did that.
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)))
With this methodology I split all the lines in my shapefile higher than 500 meters in pieces at least of 500 meters
(by Santiago Cardona Urrea、Santiago Cardona Urrea)