Çokgenli “açgözlü” kırpma hatları


9

Bir poligon kümesini (aşağıdaki resimde siyah çizgiler) çokgenin dış sınırına kırpmak istiyorum. Çokgen içindeki boşluklar göz ardı edilmelidir. İdeal çıktım kesikli sarı çizgiler. Başlangıç ​​çizgileri düz olabilir veya olmayabilir. Görüntü basitleştirilmiş bir örnektir, gerçekte çokgen çok daha karmaşıktır ve yüzlerce çizgi vardır. Dışbükey bir gövdenin işe yarayacağını sanmıyorum (ama yanlış olabilirim). Arcgis, qgis, arcpy, shapely vb. Çözümlere açıkım. Arcgis, iş arkadaşlarımın aracı paylaşmasını kolaylaştırmak için de tercih edilir, ancak bir gereklilik değildir.

Şu anda düşünebildiğim en iyi şey, tüm sınır kavşaklarında bir dizi nokta oluşturarak çokgenle tek bir çizgiyi kesiştirmektir. Noktaları çizginin başlangıcına olan mesafeye göre sıralayın. En uzak ve en yakın (FAC) noktalar çokgenin dış sınırı olacaktır. Ardından, orijinal satırdan doğru köşeleri seçmek için FAC noktalarını kullanın ve uygun noktalardan sarı kesikli çizgi oluşturun. Çalışmalı ama gerekenden daha karmaşık görünüyor.

Birkaç ek düşünce:

  • Çizgiler, noktalar arasında basit bir mesafe hesaplamasının çalışması için doğrusal "yeterlidir", doğrusal referans gerekli değildir.
  • Bir noktayı bir noktaya bölmek için bir araç olsaydı, arcpy'de bu kolay olurdu, ancak bir tane bulamıyorum.

Düşünceler?

Misal


+1, ilginç bir sorun! Hangi çözümlerin mevcut olduğunu görmeye hevesliyim =)
Joseph

Sadece orta çizginize ulaşmak zordur - üst ve alt boşlukları doldurduktan sonra bir klipten gelir. Sonuç olarak, sorunuzu buna odaklamanız ve tercih ettiğiniz araçsa kapsamını sadece ArcPy'ye daraltmanız gerektiğini düşünüyorum. Bir çözüm vermezse, her zaman başka bir araç hakkında soru sorabilirsiniz.
PolyGeo

çizgiler birden çok çokgeni geçiyor mu?
Emil Brundage

Emil, çizgilerin birden çok çokgenden geçebileceğini varsayalım. Bununla birlikte, geometri dışında, çokgenler arasında bir fark yoktur, bu nedenle algoritmayı kolaylaştırırsa çözülebilir, çok parçalı bir özellikte vb. Birleştirilebilirler. Birden çok çokgen üzerinden bir çizgi kesilmesi nadiren gerçekleşir ve gerekirse el ile ele alınması gereken işaretli bir durum olabilir.
Mike Bannister

Lisans seviyeniz nedir?
Emil Brundage

Yanıtlar:


4

PyQGIS çözümümü atmak istiyorum, başka bir şey değil.

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)

Test durumum - kırpmadan önce: klipten önce

Kırpmadan sonra:

sonra

Orijinal satırların tüm özelliklerini almak için sonuçla onlara katılmak en iyisi olacağını düşünüyorum. Aksi takdirde, hazırlık bölümünde oluşturulması ve en iç döngüde ayarlanması gerekir. Fakat erime sürecini geçip geçmediklerini veya kaybolup kaybolmadıklarını test etmedim, çünkü prensipte farklı değerlere sahip olabilirler.


Çok özlü bir cevap. QGIS ekran görüntüleri her zaman QGIS'e nasıl benziyor?
Mike Bannister

3

Bir noktayı bir noktaya bölmek için bir araç olsaydı, arcpy'de bu kolay olurdu, ancak bir tane bulamıyorum.

Çokgenler ve çizgilerle Entegrasyon'u girdi olarak çalıştırırsanız, kesiştikleri her yere bir tepe noktası eklenir. (Entegre, yeni çıktılar üretmek yerine girdileri değiştirirken dikkatli olun.)

Çakışan köşe noktaları olduğundan emin olduktan sonra, çizginin köşe noktalarını tekrarlayabilir ve her birinin diğer özelliğe dokunup dokunmadığını test edebilirsiniz. Dokunma yapan sıralı köşe listesinden, kümeden minimum ve maksimum değerleri alın. Ardından, her bir özellikten iki satır yapın: A: (başlangıç, ..., dak) ve B: (maks, ..., bitiş).

ArcPy'nin, giriş nesnesindeki köşelerin sırasına göre özellik parçası sırasını koruduğundan emin olmasam da başka bir seçenek, klibi olduğu gibi çalıştırmak olacaktır. Örneğinizdeki orta çizgi için, üç parçalı çok parçalı bir özelliğe neden olmalıdır. Sıralamaya bağlı olarak, Clip tarafından üretilen her çok parçalı çizgi üzerinde yineleme yapabilir ve dışarı çok parçalı özelliğin ilk ve son kısmı hariç tümünü kaldırabilirsiniz.


3

Bu durumda üzerinde durulması gereken üç konu vardır:

  • delikler
  • Çokgenler arasındaki çizgiler
  • Bitiş çizgileri

resim açıklamasını buraya girin

delikler

Bir delik içindeki herhangi bir çizgi korunacağından delikleri çokgenlerden çıkarın. Aşağıdaki senaryoda bunu imleçler ve geometriler kullanarak yapıyorum.

Çokgenler arasındaki çizgiler

İki çokgene temas eden çizgilerin kaldırılması gerekir. Aşağıdaki komut dosyasında one to many, satırlarım girdi özelliği sınıfım ve çokgenlerim de birleştirme özelliği sınıfım ile uzamsal birleştirme gerçekleştirerek yapıyorum . İki kez oluşturulan herhangi bir çizgi iki çokgene temas eder ve kaldırılır.

Bitiş çizgileri

Yalnızca bir uçtaki bir çokgene temas eden çizgileri kaldırmak için çizgileri bitiş noktalarına dönüştürürüm. Daha sonra hangi uç noktaların yüzen olduğunu belirlemek için özellik katmanlarını ve seçimlerini kullanırım. Çokgenlerle kesişen bitiş noktalarını seçiyorum. Sonra seçimimi değiştiririm. Bu, çokgenlerle kesişmeyen uç noktaları seçer. Bu seçilen noktalarla kesişen herhangi bir satırı seçip silerim.

Sonuç

resim açıklamasını buraya girin

Varsayımlar

  • Girdiler dosya coğrafi veritabanı özellik sınıflarıdır
  • ArcGIS gelişmiş lisansı mevcuttur ( erasea ve a nedeniyle feature vertices to points)
  • Sürekli, bağlı hatlar tek bir özelliktir
  • Çokgenler örtüşmez
  • Çok parçalı çokgen yok

Senaryo

Aşağıdaki komut dosyası, çizgi özellik sınıfınızın adı artı _GreedyClip, çizgi özellik sınıfınızla aynı coğrafi veritabanında bir özellik sınıfı çıkarır. Çalışma alanı konumu da gereklidir.

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

Güzel çözüm Emil. Bu ben sona erdi daha az kod.
Mike Bannister
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.