Как разделить шейп-файл на несколько строк - PullRequest
1 голос
/ 09 апреля 2020

У меня есть шейп-файл региональной сети, построенной из 4000 элементов (линий). Я хотел бы разделить линии длиннее 500 метров, чтобы получить максимальную длину 500 метров для каждой линии. Например, для линии длиной 7 км необходимо разрезать 14 частей, и каждая из них должна поддерживать те же атрибуты оригинала.

Я пытаюсь использовать функцию cut из Shapely, но она не работает, потому что мне нужно разделить геометрию объектов в геоданных, чтобы отрезать линию.

1 Ответ

0 голосов
/ 10 апреля 2020

Чтобы решить эту проблему, я решил разработать функцию обрезки строки в несколько строк в соответствии с количеством кусочков.

Прежде всего, я проверил функцию «обрезки» из стройного руководства пользователя (https://shapely.readthedocs.io/en/latest/manual.html). Эта функция позволяет разрезать линию (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 с использованием частей аргумента и линии. Штук используется для деления длины линии на равные части, чтобы отрезать линию.

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

Поскольку мне нужно вырезать шейп-файл во многих строках, я использую MultiCut в al oop, чтобы сделать это.

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))) 

С этим Методология Я разделяю все линии в моем шейп-файле на куски выше 500 метров по крайней мере на 500 метров

...