다각형이있는 "기쁨"클리핑 라인


9

폴리 라인 세트 (아래 이미지의 검은 선)를 다각형의 외부 경계에 자르고 싶습니다. 다각형 내의 모든 공극은 무시해야합니다. 이상적인 출력은 점선으로 된 노란색 선입니다. 초기 선은 직선이거나 직선이 아닐 수 있습니다. 이미지는 단순화 된 예입니다. 실제로 다각형은 훨씬 더 복잡하며 수백 개의 선이 있습니다. 볼록 껍질이 작동하지 않는다고 생각합니다 (그러나 잘못되었을 수도 있습니다). 나는 arcgis, qgis, arcpy, shapely 등의 솔루션에 개방적입니다. 코딩은 필요한 경우 다른 옵션에 개방되어 있으면 파이썬으로 작성하는 것이 좋습니다. 동료가 도구를 더 쉽게 공유 할 수 있도록하기 위해 Arcgis를 사용하는 것이 바람직하지만 필수 사항은 아닙니다.

내가 지금 생각할 수있는 가장 좋은 것은 모든 경계 교차점에서 점 세트를 만드는 다각형으로 개별 선을 교차시키는 것입니다. 선의 시작점까지의 거리를 기준으로 점을 정렬합니다. 가장 먼 거리 (FAC)는 다각형의 외부 경계가됩니다. 그런 다음 FAC 점을 사용하여 원래 선에서 적절한 정점을 선택하고 해당 점에서 노란색 파선을 만듭니다. 작동해야하지만 필요한 것보다 더 복잡해 보입니다.

몇 가지 추가 생각 :

  • 선은 "충분한"선으로, 점들 사이의 간단한 거리 계산이 작동해야하며, 선형 참조는 필요하지 않습니다.
  • 한 지점에서 선을 분리하는 도구가 있었지만 찾을 수 없다면 아크 피가 발생하기 쉽습니다.

누구 생각?

예


+1, 재미있는 문제! 어떤 솔루션을 사용할 수 있는지 알고 싶습니다. =)
Joseph

중간 선 만 달성하기 어렵습니다. 상단 및 하단은 공극을 채운 후 클립에서 나옵니다. 결과적으로, 당신은 그것에 당신의 질문에 집중하고 그것이 선호하는 도구라면 그것의 범위를 ArcPy로 좁혀 야한다고 생각합니다. 솔루션을 얻지 못하면 다른 도구에 대해 언제든지 문의 할 수 있습니다.
PolyGeo

선이 여러 다각형을 교차합니까?
Emil Brundage 2016 년

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

한 지점에서 선을 분리하는 도구가 있었지만 찾을 수 없다면 아크 피가 발생하기 쉽습니다.

입력으로 다각형 및 선과 통합을 실행하면 교차하는 각각에 정점이 추가됩니다. (통합은 새 출력을 생성하는 대신 입력을 수정하므로주의하십시오.)

일치하는 정점이 있는지 확인한 후에는 선의 정점을 반복하고 각각이 다른 피쳐에 닿는 지 테스트 할 수 있습니다. 터치하는 정렬 된 정점 목록에서 세트에서 최소값과 최대 값을 가져옵니다. 그런 다음 각 기능에서 A : (시작, ..., 최소) 및 B : (최대, ..., 끝)의 두 줄을 만듭니다.

다른 옵션은 ArcPy가 입력 객체의 정점 순서를 기반으로 피처 파트 순서를 유지하는지 확실하지 않지만 클립을 그대로 실행하는 것입니다. 예제의 중간 줄의 경우 세 부분으로 된 다중 부분 피처가 생성됩니다. 순서에 따라 Clip에서 생성 된 모든 멀티 파트 라인을 반복하고 멀티 파트 기능의 첫 번째 및 마지막 부분을 제외한 모든 부분을 제거 할 수 있습니다.


3

이 경우에 해결해야 할 세 가지 문제가 있습니다.

  • 구멍
  • 다각형 사이의 선
  • 엔드 라인

여기에 이미지 설명을 입력하십시오

구멍

구멍 내의 선이 유지되므로 다각형에서 구멍을 제거하십시오. 아래 스크립트에서 커서와 도형을 사용하여 수행합니다.

다각형 사이의 선

두 개의 다각형에 닿는 선을 제거해야합니다. 아래 스크립트에서 나는 one to many입력 피쳐 클래스로 내 선 을 , 결합 피쳐 클래스로 다각형을 사용 하여의 공간 결합을 수행하여 그렇게합니다 . 두 번 생성 된 선은 두 다각형에 닿아 제거됩니다.

엔드 라인

한쪽 끝의 다각형에만 닿는 선을 제거하기 위해 선을 끝점으로 변환합니다. 그런 다음 피처 레이어와 선택 영역을 사용하여 플로터가되는 엔드 포인트를 결정합니다. 다각형과 교차하는 끝점을 선택합니다. 그런 다음 선택을 전환합니다. 다각형과 교차하지 않는 끝점을 선택합니다. 선택한 점과 교차하는 선을 선택하고 삭제합니다.

결과

여기에 이미지 설명을 입력하십시오

가정

  • 입력은 파일 지오 데이터베이스 피처 클래스입니다.
  • ArcGIS 고급 라이센스를 사용할 수 있습니다 ( erase및 로 인해 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.