여러 선스트링으로 구성된 shapefile을 분할하는 방법 (How to split a shapefile compose of multiple linestrings)


문제 설명

여러 선스트링으로 구성된 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 UrreaSantiago Cardona Urrea)

참조 문서

  1. How to split a shapefile compose of multiple linestrings (CC BY‑SA 2.5/3.0/4.0)

#geopandas #shapely #Python






관련 질문

{'northeast': {'lat':}, 'southwest': {'lat': }}(Google 지도) Python에서 다각형을 만드는 방법 (How to create Polygon out of {'northeast': {'lat':}, 'southwest': {'lat': }} (google maps) Python)

모양이 교차하는 다각형의 수를 어떻게 계산합니까? (How can I count the number of polygons a shape intersects?)

GeoPandas를 사용하여 Python에서 GRASS 벡터 데이터 소스 읽기 (Read GRASS vector datasources in Python using GeoPandas)

어떤 점에 포함된 영역을 찾는 Geopandas (Geopandas to locate which area does a point contains)

Geopandas Dataframe이 인덱스 오류를 반환하는 이유 (Why is the Geopandas Dataframe returning an Index Error)

여러 선스트링으로 구성된 shapefile을 분할하는 방법 (How to split a shapefile compose of multiple linestrings)

LineString 유형의 개체는 JSON 직렬화가 불가능합니다. (Object of type LineString is not JSON serializable)

큰 geotif 파일 열기 (Open large geotif file)

점이 있는 geopandas 데이터 프레임에서 다각형 만들기 (Creating a polygon from a geopandas dataframe with points)

여러 소스 및 대상 노드에 대한 Networkx 최단 경로 분석 (Networkx Shortest Path Analysis on multiple source and target nodes)

위도/경도 포인트가 다각형 내에 있는지 어떻게 확인합니까? (How do I determine if a lat/long point is within a polygon?)

Geodataframe.explore() 오류: __init__() 누락된 1개의 필수 위치 인수: '위치' (Geodataframe.explore() error: __init__() missing 1 required positional argument: 'location')







코멘트