“ โลภ” ตัดเส้นด้วยรูปหลายเหลี่ยม


9

ฉันต้องการคลิปชุด polylines (เส้นสีดำในภาพด้านล่าง) ไปยังขอบเขตด้านนอกของรูปหลายเหลี่ยม ควรละเว้นช่องว่างใด ๆ ภายในรูปหลายเหลี่ยม ผลลัพธ์ที่ดีที่สุดของฉันคือเส้นสีเหลืองประ บรรทัดเริ่มต้นอาจจะหรืออาจจะไม่ตรง ภาพเป็นตัวอย่างที่เรียบง่าย แต่ในความเป็นจริงรูปหลายเหลี่ยมนั้นซับซ้อนกว่ามากและมีหลายร้อยบรรทัด ฉันไม่คิดว่าตัวเรือนูนจะทำงานได้ (แต่ฉันอาจผิด) ฉันเปิดให้แก้ปัญหาใน arcgis, qgis, arcpy, หุ่นดี ฯลฯ การเขียนโปรแกรมจะอยู่ในหลามโดยฉันเปิดให้ตัวเลือกอื่น ๆ หากจำเป็น Arcgis น่าจะดีกว่าที่จะทำให้เพื่อนร่วมงานของฉันแบ่งปันเครื่องมือได้ง่ายขึ้น แต่ไม่ใช่ข้อกำหนด

สิ่งที่ดีที่สุดที่ฉันคิดได้ในตอนนี้คือการตัดเส้นแต่ละเส้นด้วยรูปหลายเหลี่ยมที่สร้างชุดของจุดที่จุดตัดของขอบเขตทั้งหมด เรียงคะแนนตามระยะทางถึงจุดเริ่มต้นของบรรทัด จุดที่อยู่ไกลและใกล้ที่สุด (FAC) จะเป็นขอบเขตด้านนอกของรูปหลายเหลี่ยม จากนั้นใช้จุด FAC เพื่อเลือกจุดยอดที่เหมาะสมจากบรรทัดเดิมและสร้างเส้นประสีเหลืองจากจุดที่เหมาะสม ควรใช้งานได้ แต่ดูเหมือนจะซับซ้อนเกินความจำเป็น

ความคิดเพิ่มเติมบางประการ:

  • เส้นดังกล่าวเป็นเส้นตรง "เพียงพอ" ที่การคำนวณระยะทางง่าย ๆ ระหว่างจุดควรใช้งานไม่จำเป็นต้องมีการอ้างอิงเชิงเส้น
  • นี่จะเป็นเรื่องง่ายใน arcpy ถ้ามีเครื่องมือในการแยกเส้นตรงจุด แต่ฉันไม่สามารถหาได้

คิดว่าทุกคน?

ตัวอย่าง


+1 ปัญหาที่น่าสนใจ! ฉันกระตือรือร้นที่จะดูว่ามีวิธีแก้ปัญหาใดบ้าง =)
โยเซฟ

บรรทัดกลางของคุณเท่านั้นที่ทำได้ยาก - ด้านบนและล่างมาจากคลิปหลังจากเติมช่องว่างใด ๆ ดังนั้นฉันคิดว่าคุณควรมุ่งเน้นคำถามของคุณในเรื่องนั้นและ จำกัด ขอบเขตให้แคบลงเพื่อเพียงแค่ ArcPy หากนั่นเป็นเครื่องมือที่คุณต้องการ คุณสามารถถามเกี่ยวกับเครื่องมืออื่นได้เสมอหากไม่ได้ผล
PolyGeo

เส้นข้ามรูปหลายเหลี่ยมหลายอันได้อย่างไร
Emil Brundage

Emil สมมติว่าเส้นนั้นอาจข้ามหลายรูปหลายเหลี่ยม อย่างไรก็ตามนอกเหนือจากรูปทรงเรขาคณิตไม่มีความแตกต่างระหว่างรูปหลายเหลี่ยมดังนั้นพวกเขาจึงสามารถละลายรวมเข้ากับคุณสมบัติหลายส่วน ฯลฯ หากนั่นทำให้อัลกอริทึมง่ายขึ้น การข้ามเส้นข้ามหลายรูปหลายเหลี่ยมน่าจะเป็นของหายากและอาจเป็นกรณีที่ถูกทำเครื่องหมายด้วยมือหากจำเป็น
Mike Bannister

ระดับสิทธิ์การใช้งานของคุณคืออะไร
Emil Brundage

คำตอบ:


4

ฉันต้องการที่จะโยนใน pyqgis ทางออกของฉันไม่มีอะไรอื่น

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)

กรณีทดสอบของฉัน - ก่อนที่จะตัด: ก่อนคลิป

หลังจากการตัด:

หลังจาก

เพื่อให้ได้ชุดเต็มของคุณสมบัติของบรรทัดต้นฉบับฉันคิดว่ามันจะเป็นการดีที่สุดที่จะเข้าร่วมกับผลลัพธ์เหล่านั้น มิฉะนั้นจะต้องสร้างขึ้นในส่วนการเตรียมการและตั้งค่าในลูปภายในสุด แต่ฉันไม่ได้ทดสอบว่าพวกเขาผ่านกระบวนการละลายหรือถ้าพวกเขาหลงทางเพราะในหลักการพวกเขาอาจมีค่าต่างกัน


คำตอบที่กระชับมาก ภาพหน้าจอของ QGIS มีลักษณะเหมือน QGIS อย่างไร
Mike Bannister

3

นี่จะเป็นเรื่องง่ายใน arcpy ถ้ามีเครื่องมือในการแยกเส้นตรงจุด แต่ฉันไม่สามารถหาได้

หากคุณเรียกใช้ผสานรวมกับรูปหลายเหลี่ยมและเส้นเป็นอินพุตมันจะเพิ่มจุดสุดยอดให้กับแต่ละจุดที่พวกเขาตัดกัน (ระวังเนื่องจากการรวมเข้าด้วยกันจะแก้ไขอินพุตแทนการสร้างเอาต์พุตใหม่)

เมื่อคุณแน่ใจว่ามีจุดยอดที่เหมือนกันคุณสามารถวนซ้ำจุดยอดของเส้นและทดสอบเพื่อดูว่าแต่ละสัมผัสกับคุณสมบัติอื่น ๆ จากรายการของจุดยอดที่สัมผัสสัมผัสสั่งขั้นต่ำและสูงสุดจากการตั้งค่า จากนั้นสร้างสองบรรทัดจากแต่ละคุณสมบัติ A: (เริ่ม, ... , นาที) และ B: (สูงสุด, ... , สิ้นสุด)

ตัวเลือกอื่นแม้ว่าฉันจะไม่แน่ใจว่า ArcPy รักษาการสั่งซื้อชิ้นส่วนตามการเรียงลำดับจุดยอดในวัตถุอินพุตหรือไม่ สำหรับเส้นตรงกลางในตัวอย่างของคุณควรส่งผลให้คุณลักษณะหลายส่วนมีสามส่วน ขึ้นอยู่กับการสั่งซื้อคุณสามารถวนซ้ำทุกบรรทัดที่ผลิตโดยคลิปและลบทั้งหมดยกเว้นส่วนแรกและส่วนสุดท้ายของคุณลักษณะหลายส่วน


3

ในกรณีนี้มีสามประเด็นที่ต้องแข่งขันกัน:

  • หลุม
  • เส้นแบ่งระหว่างรูปหลายเหลี่ยม
  • สิ้นสุดบรรทัด

ป้อนคำอธิบายรูปภาพที่นี่

หลุม

เนื่องจากเส้นใด ๆ ที่อยู่ภายในรูจะถูกดูแลรักษาเอาหลุมออกจากรูปหลายเหลี่ยม ในสคริปต์ด้านล่างฉันทำได้โดยใช้เคอร์เซอร์และรูปทรงเรขาคณิต

เส้นแบ่งระหว่างรูปหลายเหลี่ยม

ต้องลบเส้นที่สัมผัสสองรูปหลายเหลี่ยม ในสคริปต์ด้านล่างฉันทำได้โดยดำเนินการเข้าร่วมเชิงพื้นที่one to manyโดยมีบรรทัดของฉันเป็นชั้นเรียนคุณลักษณะการป้อนข้อมูลของฉันและรูปหลายเหลี่ยมของฉันเป็นชั้นเรียนคุณลักษณะการเข้าร่วมของฉัน บรรทัดใด ๆ ที่สร้างขึ้นสองครั้งจะสัมผัสกับรูปหลายเหลี่ยมสองอันและถูกลบออก

สิ้นสุดบรรทัด

หากต้องการลบเส้นที่แตะรูปหลายเหลี่ยมที่ด้านเดียวฉันจะแปลงบรรทัดเป็นจุดสิ้นสุด ฉันใช้ประโยชน์จากเลเยอร์คุณลักษณะและการเลือกเพื่อกำหนดว่าจุดสิ้นสุดใดที่เป็นจุดลอยตัว ฉันเลือกจุดสิ้นสุดที่ตัดกันรูปหลายเหลี่ยม จากนั้นฉันสลับการเลือกของฉัน นี่เป็นการเลือกจุดสิ้นสุดที่ไม่ตัดกันรูปหลายเหลี่ยม ฉันเลือกบรรทัดใดก็ได้ที่ตัดกันจุดที่เลือกเหล่านี้และลบออก

ผลลัพธ์

ป้อนคำอธิบายรูปภาพที่นี่

สมมติฐาน

  • อินพุตเป็นคลาสของคุณลักษณะทางภูมิศาสตร์ฐานข้อมูลไฟล์
  • มีใบอนุญาตขั้นสูงของ ArcGIS (เนื่องจากeraseและ a feature vertices to points)
  • เส้นต่อเนื่องที่เชื่อมต่อกันเป็นคุณสมบัติเดียว
  • รูปหลายเหลี่ยมไม่ทับซ้อนกัน
  • ไม่มีรูปหลายเหลี่ยมหลายส่วน

ต้นฉบับ

สคริปต์ด้านล่างแสดงคลาสคุณลักษณะที่มีชื่อคลาสคุณลักษณะบรรทัดของคุณบวก_GreedyClipในฐานข้อมูลภูมิศาสตร์เดียวกันกับคลาสคุณลักษณะบรรทัดของคุณ ต้องการพื้นที่ทำงานด้วย

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

โซลูชั่นที่ดี Emil นั่นเป็นรหัสที่น้อยกว่าที่ฉันได้รับ
Mike Bannister
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.