Đường cắt của Greedy


9

Tôi muốn đưa một bộ polylines (đường màu đen trong hình bên dưới) vào ranh giới bên ngoài của một đa giác. Bất kỳ khoảng trống trong đa giác nên được bỏ qua. Đầu ra lý tưởng của tôi là các đường màu vàng nét đứt. Các dòng ban đầu có thể hoặc không thể thẳng. Hình ảnh là một ví dụ đơn giản, trong thực tế, đa giác phức tạp hơn nhiều và có hàng trăm dòng. Tôi không nghĩ một thân tàu lồi sẽ hoạt động (nhưng tôi có thể sai). Tôi mở các giải pháp trong arcgis, qgis, arcpy, shapely, v.v. Mã hóa tốt nhất là trong python bởi tôi mở cho các tùy chọn khác nếu cần. Arcgis cũng sẽ thích hợp hơn để giúp đồng nghiệp của tôi chia sẻ công cụ dễ dàng hơn nhưng không phải là một yêu cầu.

Điều tốt nhất tôi có thể nghĩ đến ngay bây giờ là giao một đường riêng lẻ với đa giác tạo ra một tập hợp các điểm tại tất cả các giao điểm biên. Sắp xếp các điểm theo khoảng cách đến điểm bắt đầu của dòng. Các điểm xa nhất và gần nhất (FAC) sẽ là ranh giới bên ngoài của đa giác. Sau đó sử dụng các điểm FAC để chọn các đỉnh thích hợp từ đường ban đầu và tạo đường đứt nét màu vàng từ các điểm thích hợp. Nó nên hoạt động nhưng có vẻ phức tạp hơn mức cần thiết.

Một vài suy nghĩ bổ sung:

  • Các dòng là "đủ" tuyến tính mà một phép tính khoảng cách đơn giản giữa các điểm sẽ hoạt động, không cần tham chiếu tuyến tính.
  • Điều này sẽ dễ dàng trong arcpy nếu có một công cụ để phân chia một dòng tại một điểm nhưng tôi không thể tìm thấy một dòng.

Suy nghĩ của ai?

Thí dụ


+1, vấn đề thú vị! Tôi muốn xem những giải pháp nào khả dụng =)
Joseph

Chỉ có đường giữa của bạn khó đạt được - đỉnh và đáy chỉ xuất phát từ một clip sau khi lấp đầy bất kỳ khoảng trống nào. Do đó, tôi nghĩ bạn nên tập trung câu hỏi của mình vào đó và thu hẹp phạm vi của nó thành ArcPy nếu đó là công cụ ưa thích của bạn. Bạn luôn có thể hỏi về một công cụ khác, nếu điều đó không mang lại giải pháp.
PolyGeo

làm các dòng vượt qua nhiều đa giác?
Emil Brundage

Emil, giả sử rằng các dòng có thể vượt qua nhiều đa giác. Tuy nhiên, khác với hình học, không có sự khác biệt giữa các đa giác để chúng có thể được hòa tan, hợp nhất thành một tính năng nhiều phần, v.v. nếu điều đó làm cho thuật toán dễ dàng hơn. Một dòng vượt qua nhiều đa giác có thể sẽ hiếm và đó có thể là trường hợp được gắn cờ để xử lý bằng tay nếu cần thiết.
Mike Bannister

Cấp giấy phép của bạn là gì?
Emil Brundage

Câu trả lời:


4

Tôi muốn ném vào giải pháp pyQGIS của tôi, không có gì khác.

from PyQt4.QtCore import QVariant
from qgis.analysis import QgsGeometryAnalyzer

# get layers
lines = QgsMapLayerRegistry.instance().mapLayersByName('lines')[0]
clipper = QgsMapLayerRegistry.instance().mapLayersByName('clipper')[0]

# prepare result layer
clipped = QgsVectorLayer('LineString?crs=epsg:4326', 'clipped', 'memory')
clipped.startEditing()
clipped.addAttribute(QgsField('fid', QVariant.Int))
fni = clipped.fieldNameIndex('fid')
clipped.commitChanges()

prov = clipped.dataProvider()
fields = prov.fields()

for line in lines.getFeatures():
    # to increase performance filter possible clippers 
    clippers = clipper.getFeatures(QgsFeatureRequest().setFilterRect(line.geometry().boundingBox()))
    for clip in clippers:
            # split the line
            line1 = line.geometry().splitGeometry(clip.geometry().asPolygon()[0], True)
            feats = []
            # get the split points
            vertices = [QgsPoint(vert[0], vert[1]) for vert in line1[2]]
            for part in line1[1]:
                # for each split part check, if first AND last vertex equal to split points
                if part.vertexAt(0) in vertices and part.vertexAt(len(part.asPolyline())-1) in vertices:
                    # if so create feature and set fid to original line's id
                    feat = QgsFeature(fields)
                    feat.setAttributes([line.id()])
                    feat.setGeometry(part)
                    feats.append(feat)

            prov.addFeatures(feats)

# expose layer
clipped.updateExtents()
QgsMapLayerRegistry.instance().addMapLayers([clipped])

# now dissolve lines having the same value in field fni: here original line's id
diss = QgsGeometryAnalyzer()
diss.dissolve(clipped, 'E:\\clipped.shp', uniqueIdField=fni)

Trường hợp thử nghiệm của tôi - trước khi cắt: trước clip

Sau khi cắt:

sau

Để có được tập hợp đầy đủ các thuộc tính của các dòng ban đầu, tôi nghĩ rằng sẽ tốt nhất khi kết hợp chúng với kết quả. Mặt khác, phải được tạo trong phần chuẩn bị và được đặt trong vòng lặp bên trong nhất. Nhưng tôi đã không kiểm tra nếu họ vượt qua quá trình hòa tan hoặc nếu họ bị lạc, bởi vì về nguyên tắc họ có thể có các giá trị khác nhau.


Câu trả lời rất súc tích. Làm thế nào để ảnh chụp màn hình của QGIS luôn trông giống như QGIS?
Mike Bannister

3

Điều này sẽ dễ dàng trong arcpy nếu có một công cụ để phân chia một dòng tại một điểm nhưng tôi không thể tìm thấy một dòng.

Nếu bạn chạy Tích hợp với đa giác và đường thẳng làm đầu vào, nó sẽ thêm một đỉnh vào mỗi nơi chúng giao nhau. (Cẩn thận, vì Tích hợp sửa đổi đầu vào thay vì tạo đầu ra mới.)

Khi bạn chắc chắn có các đỉnh trùng khớp, bạn có thể lặp lại các đỉnh của đường thẳng và kiểm tra xem mỗi đỉnh có chạm vào tính năng kia không. Từ danh sách các đỉnh được sắp xếp theo thứ tự, hãy lấy mức tối thiểu và tối đa từ tập hợp. Sau đó, tạo hai dòng từ mỗi tính năng, A: (bắt đầu, ..., phút) và B: (tối đa, ..., kết thúc).

Một tùy chọn khác, mặc dù tôi không chắc ArcPy có giữ thứ tự phần tính năng dựa trên thứ tự các đỉnh trong đối tượng đầu vào hay không, sẽ chạy clip như hiện tại. Đối với đường giữa trong ví dụ của bạn, nó sẽ dẫn đến một tính năng nhiều phần với ba phần. Tùy thuộc vào thứ tự, bạn có thể lặp lại trên mỗi dòng nhiều phần được tạo bởi Clip và xóa tất cả trừ phần đầu tiên và phần cuối của tính năng nhiều phần.


3

Có ba vấn đề cần giải quyết trong trường hợp này:

  • Hố
  • Các đường giữa các đa giác
  • Dòng kết thúc

nhập mô tả hình ảnh ở đây

Hố

Vì bất kỳ dòng nào trong một lỗ sẽ được duy trì, hãy loại bỏ các lỗ khỏi đa giác. Trong kịch bản dưới đây, tôi làm như vậy bằng cách sử dụng con trỏ và hình học.

Các đường giữa các đa giác

Các dòng chạm hai đa giác cần phải được loại bỏ. Trong kịch bản bên dưới, tôi làm như vậy bằng cách thực hiện phép nối không gian one to many, với các dòng của tôi là lớp tính năng đầu vào và đa giác của tôi là lớp tính năng tham gia của tôi. Bất kỳ dòng nào được tạo hai lần chạm vào hai đa giác và được loại bỏ.

Dòng kết thúc

Để xóa các dòng chỉ chạm vào một đa giác ở một đầu, tôi chuyển đổi các dòng thành điểm cuối. Sau đó tôi sử dụng các lớp tính năng và các lựa chọn để xác định điểm cuối nào là dấu phao. Tôi chọn các điểm cuối giao nhau với đa giác. Sau đó tôi chuyển đổi lựa chọn của mình. Điều này chọn điểm cuối không giao nhau đa giác. Tôi chọn bất kỳ dòng nào giao nhau với các điểm đã chọn và xóa chúng.

Kết quả

nhập mô tả hình ảnh ở đây

Giả định

  • Đầu vào là các lớp tính năng cơ sở dữ liệu địa lý
  • Giấy phép nâng cao ArcGIS có sẵn (do một erasevà a feature vertices to points)
  • Các đường liên tục, được kết nối là một tính năng duy nhất
  • Đa giác không trùng nhau
  • Không có đa giác nhiều phần

Kịch bản

Tập lệnh bên dưới xuất ra một lớp đối tượng với tên của lớp đối tượng đường cộng của bạn _GreedyClip, trong cùng cơ sở dữ liệu địa lý với lớp đối tượng đường dây của bạn. Một vị trí không gian làm việc cũng cần thiết.

#input polygon feature class
polyFc = r"C:\Users\e1b8\Desktop\E1B8\Workspace\Workspace.gdb\testPolygon2"
#input line feature class
lineFc = r"C:\Users\e1b8\Desktop\E1B8\Workspace\Workspace.gdb\testLine"
#workspace
workspace = r"in_memory"

print "importing"
import arcpy
import os

#generate a unique ArcGIS file name
def UniqueFileName(location = "in_memory", name = "file", extension = ""):
    if extension:
        outName = os.path.join (location, name + "." + extension)
    else:
        outName = os.path.join (location, name)
    i = 0
    while arcpy.Exists (outName):
        i += 1
        if extension:
            outName = os.path.join (location, "{0}_{1}.{2}".format (name, i, extension))
        else:
            outName = os.path.join (location, "{0}_{1}".format (name, i))
    return outName

#remove holes from polygons
def RemoveHoles (inFc, workspace):
    outFc = UniqueFileName (workspace)
    array = arcpy.Array ()
    sr = arcpy.Describe (inFc).spatialReference
    outPath, outName = os.path.split (outFc)
    arcpy.CreateFeatureclass_management (outPath, outName, "POLYGON", spatial_reference = sr)
    with arcpy.da.InsertCursor (outFc, "SHAPE@") as iCurs:
        with arcpy.da.SearchCursor (inFc, "SHAPE@") as sCurs:
            for geom, in sCurs:
                try:
                    part = geom.getPart (0)
                except:
                    continue
                for pnt in part:
                    if not pnt:
                        break
                    array.add (pnt)
                polygon = arcpy.Polygon (array)
                array.removeAll ()
                row = (polygon,)
                iCurs.insertRow (row)
    del iCurs
    del sCurs
    return outFc

#split line fc by polygon fc
def SplitLinesByPolygon (lineFc, polygonFc, workspace):
    #clip
    clipFc = UniqueFileName(workspace)
    arcpy.Clip_analysis (lineFc, polygonFc, clipFc)
    #erase
    eraseFc = UniqueFileName(workspace)
    arcpy.Erase_analysis (lineFc, polygonFc, eraseFc)
    #merge
    mergeFc = UniqueFileName(workspace)
    arcpy.Merge_management ([clipFc, eraseFc], mergeFc)
    #multipart to singlepart
    outFc = UniqueFileName(workspace)
    arcpy.MultipartToSinglepart_management (mergeFc, outFc)
    #delete intermediate data
    for trash in [clipFc, eraseFc, mergeFc]:
        arcpy.Delete_management (trash)
    return outFc

#remove lines between two polygons and end lines
def RemoveLines (inFc, polygonFc, workspace):
    #check if "TARGET_FID" is in fields
    flds = [f.name for f in arcpy.ListFields (inFc)]
    if "TARGET_FID" in flds:
        #delete "TARGET_FID" field
        arcpy.DeleteField_management (inFc, "TARGET_FID")
    #spatial join
    sjFc = UniqueFileName(workspace)
    arcpy.SpatialJoin_analysis (inFc, polygonFc, sjFc, "JOIN_ONE_TO_MANY")
    #list of TARGET_FIDs
    targetFids = [fid for fid, in arcpy.da.SearchCursor (sjFc, "TARGET_FID")]
    #target FIDs with multiple occurances
    deleteFids = [dFid for dFid in targetFids if targetFids.count (dFid) > 1]
    if deleteFids:
        #delete rows with update cursor
        with arcpy.da.UpdateCursor (inFc, "OID@") as cursor:
            for oid, in cursor:
                if oid in deleteFids:
                    cursor.deleteRow ()
        del cursor
    #feature vertices to points
    vertFc = UniqueFileName(workspace)
    arcpy.FeatureVerticesToPoints_management (inFc, vertFc, "BOTH_ENDS")
    #select points intersecting polygons
    arcpy.MakeFeatureLayer_management (vertFc, "vertLyr")
    arcpy.SelectLayerByLocation_management ("vertLyr", "", polygonFc, "1 FEET")
    #switch selection
    arcpy.SelectLayerByAttribute_management ("vertLyr", "SWITCH_SELECTION")
    arcpy.MakeFeatureLayer_management (inFc, "lineLyr")
    #check for selection
    if arcpy.Describe ("vertLyr").FIDSet:
        #select lines by selected points
        arcpy.SelectLayerByLocation_management ("lineLyr", "", "vertLyr", "1 FEET")
        #double check selection (should always have selection)
        if arcpy.Describe ("lineLyr").FIDSet:
            #delete selected rows
            arcpy.DeleteFeatures_management ("lineLyr")

    #delete intermediate data
    for trash in [sjFc, "vertLyr", "lineLyr"]:
        arcpy.Delete_management (trash)

#main script
def main (polyFc, lineFc, workspace):

    #remove holes
    print "removing holes"
    holelessPolyFc = RemoveHoles (polyFc, workspace)

    #split line at polygons
    print "splitting lines at polygons"
    splitFc = SplitLinesByPolygon (lineFc, holelessPolyFc, workspace)

    #delete unwanted lines
    print "removing unwanted lines"
    RemoveLines (splitFc, polyFc, workspace)

    #create output feature class
    outFc = lineFc + "_GreedyClip"
    outFcPath, outFcName = os.path.split (outFc)
    outFc = UniqueFileName (outFcPath, outFcName)
    arcpy.CopyFeatures_management (splitFc, outFc)
    print "created:"
    print outFc
    print
    print "cleaning up"
    #delete intermediate data
    for trash in [holelessPolyFc, splitFc]:
        arcpy.Delete_management (trash)

    print "done"                    

if __name__ == "__main__":
    main (polyFc, lineFc, workspace)  

Giải pháp tốt đẹp Emil. Đó là ít mã hơn tôi đã kết thúc với.
Mike Bannister
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.