ArcGIS Desktop ve Python kullanarak iki özellik sınıfında kesişen özellikler arasındaki açıyı mı buluyorsunuz? [kapalı]


19

İki kesişen çizgi özelliği var. ArcGIS 10 ve Python kullanarak her kesişme noktasındaki açıyı bulmak istiyorum.

Biri yardım edebilir mi?


Arcpy kullanarak bir python komut dosyasında whuber yöntemi (teşekkür ederim) çoğaltılmış ama açı hesaplama ile ilgili sorunlar yaşıyorum. Esri ArcMap (alan hesap makinesi) içinde tamamlandığında doğru hesaplar. Bir python betiği içinde hesaplandığında (yine alan hesaplayıcısını kullanarak) yanlış hesaplar (ondalık olarak). Bu sadece radyandan derece problemine dönüşüm değil. Alanı bir açı olarak hesaplamak için ark işlevi aşağıdadır. Özellik sınıfları öngörülmektedir (İngiliz Ulusal Izgarası). Bir harita belgesinden uzağa python açılarını hesaplamak için atmam gereken ek bir adım var mı
Andy

Yanıtlar:


13

Nispeten basit bir iş akışı vardır. İki özelliğin birden fazla noktada kesişebileceği potansiyel sorunların üstesinden gelir. Komut dosyası gerektirmez (ancak kolayca bir komut dosyasına dönüştürülebilir). Öncelikle ArcGIS menüsünden yapılabilir.

Buradaki fikir, kesişen her farklı çift hat için bir nokta olan bir kesişim noktası katmanından faydalanmaktır. Bu kesişme noktalarında her kesişen çoklu çizginin küçük bir parçasını almanız gerekir . Kesişme açılarını hesaplamak için bu parçaların yönlerini kullanın.

İşte adımlar:

  1. Çoklu çizgi özelliklerinin her birinin, özellik tablosunda benzersiz bir tanımlayıcıya sahip olduğundan emin olun . Bu daha sonra, çoklu çizgilerin bazı geometrik özelliklerini kesişim noktası tablosuna birleştirmek için kullanılacaktır.

  2. Kesişme noktaları alır ( çıktı için istediğiniz noktaları belirttiğinizden emin olun ).

  3. Geoprocessing | Buffer noktaları küçük bir miktarda tamponlamanızı sağlar. Bir arabellekteki her satırın bölümünün bükülmemesi için gerçekten küçük yapın .

  4. Geoprocessing | Clip (iki kez uygulanır) orijinal çoklu çizgi katmanlarını sadece tamponlarla sınırlar. Bu, çıktısı için yeni veri kümeleri ürettiğinden, sonraki işlemler orijinal verileri değiştirmez (bu iyi bir şeydir).

    şekil

    İşte olanların şeması: açık mavi ve açık kırmızı ile gösterilen iki çoklu çizgi katmanı koyu kesişme noktaları oluşturdu. Bu noktaların etrafında küçük tamponlar sarı renkte gösterilir. Koyu mavi ve kırmızı segmentler, orijinal özelliklerin bu arabelleklere kırpılmasının sonuçlarını gösterir. Algoritmanın geri kalanı karanlık segmentlerle çalışır. (Burada göremezsiniz, ancak küçük bir kırmızı çoklu çizgi, mavi çizgilerin ikisini ortak bir noktada keser ve iki mavi poliilin etrafında tampon gibi görünen şeyi üretir. , bu şemada toplam beş tampon görüntülenir.)

  5. Bu kırpılmış katmanların her birinde dört yeni alan oluşturmak için AddField aracını kullanın : [X0], [Y0], [X1] ve [Y1]. Nokta koordinatlarını tutacaklar, bu yüzden onları iki katına çıkarın ve onlara çok fazla hassasiyet verin.

  6. Geometri Hesapla (her yeni alan başlığına sağ tıklayarak çağrılır), kırpılan her çoklu çizginin başlangıç ​​ve bitiş noktalarının x ve y koordinatlarını hesaplamanızı sağlar: bunları [X0], [Y0], [X1] ve [Y1]. Bu, her kırpılmış katman için yapılır, bu nedenle 8 hesaplama gereklidir.

  7. Kesişme noktası katmanında yeni bir [Açı] alanı oluşturmak için AddField aracını kullanın .

  8. Üyelik ortak nesne tanımlayıcıları temel kesişme noktası tabloya kırpılmış tablolar. (Birleşimler katman adına sağ tıklanarak ve "Birleştir ve İlişkiler" seçilerek gerçekleştirilir.)

    Bu noktada nokta kesişim tablosunda 9 yeni alan vardır: ikisi [X0] vb. Olarak adlandırılır ve biri de [Açı] olarak adlandırılır. Birleştirilen tablolardan birine ait olan [X0], [Y0], [X1] ve [Y1] alanlarını diğer adla değiştirin. Bunlara "X0a", "Y0a", "X1a" ve "Y1a" diyelim.

  9. Kavşak tablosundaki açıyı hesaplamak için Alan Hesaplayıcı'yı kullanın . Hesaplama için bir Python kod bloğu:

    dx = !x1!-!x0!
    dy = !y1!-!y0!
    dxa = !x1a!-!x0a!
    dya = !y1a!-!y0a!
    r = math.sqrt(math.pow(dx,2) + math.pow(dy,2))
    ra = math.sqrt(math.pow(dxa,2) + math.pow(dya,2))
    c = math.asin(abs((dx*dya - dy*dxa))/(r*ra)) / math.pi * 180

    Alan hesaplama ifadesi elbette sadece

    c

Bu kod bloğunun uzunluğuna rağmen, matematik basittir: (dx, dy) birinci çoklu çizgi için bir yön vektörü ve (dxa, dya) ikinci için bir yön vektörüdür. Uzunlukları, r ve ra (Pisagor Teoremi ile hesaplanır), bunları birim vektörlere normalleştirmek için kullanılır. (Kırpma uzunluğuyla ilgili bir sorun olmamalıdır, çünkü kırpma pozitif uzunluk özellikleri üretmelidir.) Kama ürünlerinin boyutu dx dya - dydxa (r ve ra ile bölündükten sonra) açının sinüsüdür. (Normal iç ürün yerine kama ürününün kullanılması, sıfıra yakın açılar için daha iyi sayısal hassasiyet sağlamalıdır.) Son olarak, açı radyandan dereceye dönüştürülür. Sonuç 0 ile 90 arasında olacaktır. Trigonometrinin sonuna kadar kaçınılmasına dikkat edin: bu yaklaşım güvenilir ve kolayca hesaplanan sonuçlar üretme eğilimindedir.

Bazı noktalar, kesişim katmanında birden çok kez görünebilir. Eğer öyleyse, kendileriyle ilişkili birden fazla açı elde ederler.

Bu çözümde tamponlama ve kırpma nispeten pahalıdır (adım 3 ve 4): milyonlarca kavşak noktası söz konusu olduğunda bunu yapmak istemezsiniz. Bunu tavsiye ettim, çünkü (a) kavşak noktası mahallesindeki her bir çoklu çizgi boyunca birbirini takip eden iki nokta bulma işlemini basitleştirir ve (b) arabelleğe alma o kadar basittir ki herhangi bir CBS'de yapılması kolaydır - ek lisanslamaya gerek yoktur temel ArcMap seviyesinin üstünde - ve genellikle doğru sonuçlar verir. (Diğer "coğrafi işleme" işlemleri bu kadar güvenilir olmayabilir.)


1
Bu işe yarayabilir, ancak kod bloğundaki alan adlarına başvuramazsınız, bu nedenle kodu bir işleve sarmanız ve alan adlarını bağımsız değişken olarak kullanarak çağırmanız gerekir.
mvexel

@mv Bu gözlem için teşekkürler. Bir de yerine Python VBS kullanabilirsiniz - VBS olacak kod bloğunda alan adlarını ayrıştırmak.
whuber

1
Bir işlev sarıcısı kullanırken aslında bir cazibe gibi çalıştı. ArcGIS 10'da ve Python'u kullanırken, değişkenleri takma adınızın gerekmediğini, alan başvurusunda birleştirme tablosu adını başa başlayabileceğinizi buldum !table1.x0!.
mvexel

6

Python betiği oluşturmanız gerektiğine inanıyorum.

Geoprocessing araçları ve arcpy kullanarak yapabilirsiniz.

İşte sizin için yararlı olabilecek ana araçlar ve fikirler:

  1. Intersect aracını kullanarak iki çoklu çizginizin (PLINE_FC1, PLINE_FC2 olarak adlandırın) özellik sınıflarının (sonuç olarak nokta özelliklerine ihtiyacınız var - POINT_FC) kesişmesini sağlayın . POINT_FC noktalarında PLINE_FC1, PLINE_FC2 kimliklerine sahip olacaksınız.
  2. Noktadaki Satırı Böl aracını kullanarak PLINE_FC1 öğesini POINT_FC'ye böl. Sonuç olarak, bölünmüş çoklu çizgilere sahip olacaksınız - bunun ana avantajı, böyle bir çizginin ilk / son tepe noktasını bir sonraki / önceki tepe noktasına (koordinat farkı) karşılaştırabilmeniz ve açıyı hesaplayabilmenizdir. Böylece, kesişme noktasında çizginizin açısına sahip olacaksınız. Burada bir sorun var - çıktının nasıl yazıldığını anlamak için bu aracı birkaç kez manuel olarak çalıştırmanız gerekiyor. Yani eğer poliline alırsa, ayırırsa, çıktıya iki sonuç polinini yazıp bir sonraki poliline devam edin ve tekrarlayın. Ya da bu bölüm (bölünmenin sonucu) farklı bellek özelliği sınıflarına yazılmış olabilir, daha sonra çıktıya eklenebilir. Bu ana problemdir - bölündükten sonra her bir çoklu hattın sadece ilk bölümünü filtreleyebilmek için çıktıların nasıl yazıldığını anlamak. Başka bir olası çözüm, tüm sonuç bölünmüş polinleriSearchCursor ve yalnızca ilk karşılaşılanları (PLINE_FC1 kaynak çoklu hatlarının kimliğine göre) alın.
  3. Açı elde etmek için, sonuç çoklu çizginin dikey kısımlarına arcpy kullanarak erişmeniz gerekir . Ortaya çıkan açıları noktalara yazın (POINT_FC).
  4. PLINE_FC2 için 2-3. Adımları tekrarlayın.
  5. Açı özniteliklerini (POINT_FC'de) alt özetleyin ve sonuç alın.

2. adımı kodlamak çok zor olabilir (ayrıca bazı araçlar ArcInfo lisansı gerektirir). Ardından her çoklu çizginin dikey noktalarını analiz etmeyi deneyebilirsiniz (kesişimden sonra bunları kimliğe göre gruplandırın).

İşte bunu yapmanın yolu:

  1. İlk kesişme noktasını POINT_FC alın. Koordinatlarını al ( point_x, point_y)
  2. Kimliği ile PLINE_FC1'den ilgili kaynak çoklu hattını alın.
  3. İlk ( vert0_x, vert0_y) ve ikinci ( vert1_x, vert1_y) köşelerini alın.
  4. İlk tepe noktası için, bu tepe noktası ile kesişme noktası arasındaki çizginin tanjantını hesaplayın: tan0 = (point_y - vert0_y) / (point_x - vert0_x)
  5. İkinci köşe için aynı şeyi hesaplayın: tan1 = (vert1_y - point_y) / (vert1_x - point_x)
  6. Eğer tan1is için eşit tan2, o zaman aradaki kesişim noktasına sahip çizginin iki verteces bulduk ve bu hat için kesişme açısını hesaplayabilir. Aksi takdirde, bir sonraki köşe çiftine (ikinci, üçüncü) devam etmeniz gerekir.
  7. Her kesişim noktası için 1-6 arasındaki adımları tekrarlayın.
  8. PLINE_FC2 adlı ikinci çok satırlı özellik sınıfı için 1-7. Adımları tekrarlayın.
  9. PLINE_FC1 ve PLINE_FC2 öğelerinden özet açı niteliklerini alın ve sonuç alın.

1

Son zamanlarda kendi başıma yapmaya çalışıyordum.

İpucu özelliğim, çizgilerin kesişme noktalarındaki dairesel noktalara ve kesişim noktalarına bir metre mesafede bulunan noktalara dayanıyor. Çıktı, kavşaklar ve açı üzerinde açıların sayısı özelliklerine sahip olan çoklu çizgi özellik sınıfıdır.

Kesişmeleri bulmak için çizgilerin düzlemselleştirilmesi ve uzamsal referansın doğru çizgi uzunluğu gösterimi ile ayarlanması gerektiğini unutmayın (benimki WGS_1984_Web_Mercator_Auxiliary_Sphere).

ArcMap konsolunda çalışıyor ancak kolayca araç kutusundaki bir betiğe dönüştürülebilir. Bu komut dosyası yalnızca TOK'da satır katmanı kullanıyor, başka bir şey kullanmıyor.

import arcpy
import time

mxd = arcpy.mapping.MapDocument("CURRENT")
df = mxd.activeDataFrame


line = ' * YOUR POLYLINE FEATURE LAYER * ' # paste the name of line layer here    

def crossing_cors(line_layer):
    mxd = arcpy.mapping.MapDocument("CURRENT")
    df = mxd.activeDataFrame
    arcpy.env.overwriteOutput = True
    sr = arcpy.Describe(line_layer).spatialReference

    dict_cors = {}
    dang_list = []

    with arcpy.da.UpdateCursor(line_layer, ['SHAPE@', 'OID@']) as uc:
        for row in uc:
            if row[0] is None:
                uc.deleteRow()

    with arcpy.da.UpdateCursor(line_layer, 'SHAPE@', spatial_reference = sr) as uc:
        for row in uc:
            line = row[0].getPart(0)
            for cor in line:
                coord = (cor.X, cor.Y)
                try:
                    dict_cors[coord] += 1
                except:
                    dict_cors[coord] = 1
    cors_only = [f for f in dict_cors if dict_cors[f]!=1]
    cors_layer = arcpy.CreateFeatureclass_management('in_memory', 'cross_pnt', "POINT", spatial_reference = sr)
    arcpy.AddField_management(cors_layer[0], 'ANGLE_NUM', 'LONG')
    with arcpy.da.InsertCursor(cors_layer[0], ['SHAPE@', 'ANGLE_NUM']) as ic:
        for x in cors_only:
            pnt_geom = arcpy.PointGeometry(arcpy.Point(x[0], x[1]), sr)
            ic.insertRow([pnt_geom, dict_cors[x]])
    return cors_layer

def one_meter_dist(line_layer):
    mxd = arcpy.mapping.MapDocument("CURRENT")
    df = mxd.activeDataFrame
    arcpy.env.overwriteOutput = True
    sr = arcpy.Describe(line_layer).spatialReference

    dict_cors = {}
    dang_list = []
    cors_list = []
    with arcpy.da.UpdateCursor(line_layer, 'SHAPE@', spatial_reference = sr) as uc:
        for row in uc:
            line = row[0]
            length_line = line.length 
            if length_line > 2.0:
                pnt1 = line.positionAlongLine(1.0)
                pnt2 = line.positionAlongLine(length_line - 1.0)
                cors_list.append(pnt1)
                cors_list.append(pnt2)
            else:
                pnt = line.positionAlongLine(0.5, True)
    cors_layer = arcpy.CreateFeatureclass_management('in_memory', 'cross_one_meter', "POINT", spatial_reference = sr)
    ic = arcpy.da.InsertCursor(cors_layer[0], 'SHAPE@')
    for x in cors_list:
        ic.insertRow([x])
    return cors_layer

def circles(pnts):

    import math
    mxd = arcpy.mapping.MapDocument("CURRENT")
    df = mxd.activeDataFrame
    arcpy.env.overwriteOutput = True
    sr = df.spatialReference

    circle_layer = arcpy.CreateFeatureclass_management('in_memory', 'circles', "POINT", spatial_reference = sr)


    ic = arcpy.da.InsertCursor(circle_layer[0], 'SHAPE@')
    with arcpy.da.SearchCursor(pnts, 'SHAPE@', spatial_reference = sr) as sc:
        for row in sc:
            fp = row[0].centroid
            list_circle =[]
            for i in xrange(0,36):
                an = math.radians(i * 10)
                np_x = fp.X + (1* math.sin(an))
                np_y = fp.Y + (1* math.cos(an))
                pnt_new = arcpy.PointGeometry(arcpy.Point(np_x,np_y), sr)

                ic.insertRow([pnt_new])
    del ic 
    return circle_layer

def angles(centers, pnts, rnd):
    mxd = arcpy.mapping.MapDocument("CURRENT")
    df = mxd.activeDataFrame
    sr = df.spatialReference

    line_lyr = arcpy.CreateFeatureclass_management('in_memory', 'line_angles', "POLYLINE", spatial_reference = sr)
    arcpy.AddField_management(line_lyr[0], 'ANGLE', "DOUBLE")
    arcpy.AddField_management(line_lyr[0], 'ANGLE_COUNT', "LONG")

    ic = arcpy.da.InsertCursor(line_lyr[0], ['SHAPE@', 'ANGLE', 'ANGLE_COUNT'])

    arcpy.AddField_management(pnts, 'ID_CENT', "LONG")
    arcpy.AddField_management(pnts, 'CENT_X', "DOUBLE")
    arcpy.AddField_management(pnts, 'CENT_Y', "DOUBLE")
    arcpy.Near_analysis(pnts, centers,'',"LOCATION") 

    with arcpy.da.UpdateCursor(line, ['SHAPE@', 'OID@']) as uc:
        for row in uc:
            if row[0] is None:
                uc.deleteRow()

    with arcpy.da.UpdateCursor(pnts, [u'ID_CENT', u'CENT_X', u'CENT_Y', u'NEAR_FID', u'NEAR_DIST', u'NEAR_X', u'NEAR_Y'], spatial_reference = sr) as uc:
        for row in uc:
            row[0] = row[3]
            row[1] = row[5]
            row[2] = row[6]
            uc.updateRow(row)
            if row[4] > 1.1:
                uc.deleteRow()


    arcpy.Near_analysis(pnts, rnd,'',"LOCATION")     

    list_id_cent = []
    with arcpy.da.UpdateCursor(pnts, [u'ID_CENT', u'CENT_X', u'CENT_Y', u'NEAR_FID', u'NEAR_DIST', u'NEAR_X', u'NEAR_Y', 'SHAPE@'], spatial_reference = sr) as uc:
        for row in uc:
            pnt_init = (row[-1].centroid.X, row[-1].centroid.Y)
            list_id_cent.append([(row[1], row[2]), row[3], pnt_init])

    list_id_cent.sort()
    values = set(map(lambda x:x[0], list_id_cent))
    newlist = [[y for y in list_id_cent if y[0]==x] for x in values]

    dict_cent_angle = {}

    for comp in newlist:
        dict_ang = {}
        for i, val in enumerate(comp):

            curr_pnt = comp[i][2]
            prev_p = comp[i-1][2]
            init_p = comp[i][0]


            angle_prev = math.degrees(math.atan2(prev_p[1]-init_p[1], prev_p[0]-init_p[0]))
            angle_next = math.degrees(math.atan2(curr_pnt[1]-init_p[1], curr_pnt[0]-init_p[0]))

            diff = abs(angle_next-angle_prev)%180


            vec1 = [(curr_pnt[0] - init_p[0]), (curr_pnt[1] - init_p[1])]
            vec2 = [(prev_p[0] - init_p[0]), (prev_p[1] - init_p[1])]

            ab = (vec1[0] * vec2[0]) + (vec1[1] * vec2[1]) 
            mod_ab = math.sqrt(math.pow(vec1[0], 2) + math.pow(vec1[1], 2)) * math.sqrt(math.pow(vec2[0], 2) + math.pow(vec2[1], 2))
            cos_a = round(ab/mod_ab, 2)

            diff = math.degrees(math.acos(cos_a))

            pnt1 = arcpy.Point(prev_p[0], prev_p[1])
            pnt2 = arcpy.Point(init_p[0], init_p[1])
            pnt3 = arcpy.Point(curr_pnt[0], curr_pnt[1])


            line_ar = arcpy.Array([pnt1, pnt2, pnt3])
            line_geom = arcpy.Polyline(line_ar, sr)

            ic.insertRow([line_geom , diff, len(comp)])
    del ic

    lyr_lst = [f.name for f in arcpy.mapping.ListLayers(mxd)]
    if 'line_angles' not in lyr_lst:
        arcpy.mapping.AddLayer(df, arcpy.mapping.Layer(line_lyr[0]))


centers = crossing_cors(line)

pnts = one_meter_dist(line)

rnd = circles(centers)

angle_dict = angles(centers, pnts, rnd)
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.