Ho due featureclasses di linea intersecanti. Voglio trovare l'angolo in ogni punto di intersezione usando ArcGIS 10 e Python.
Qualcuno può aiutare?
Ho due featureclasses di linea intersecanti. Voglio trovare l'angolo in ogni punto di intersezione usando ArcGIS 10 e Python.
Qualcuno può aiutare?
Risposte:
C'è un flusso di lavoro relativamente semplice. Supera i potenziali problemi che due caratteristiche possono intersecare in più di un punto. Non richiede script (ma può essere prontamente trasformato in uno script). Può essere fatto principalmente dal menu ArcGIS.
L'idea è di sfruttare uno strato di punti di intersezione, un punto per ogni coppia distinta di polilinee intersecanti. È necessario ottenere un piccolo pezzo di ciascuna polilinea che si interseca in questi punti di intersezione. Usa gli orientamenti di questi pezzi per calcolare i loro angoli di intersezione.
Ecco i passaggi:
Assicurati che ciascuna delle caratteristiche della polilinea abbia un identificatore univoco nella sua tabella degli attributi. Questo verrà utilizzato in seguito per unire alcuni attributi geometrici delle polilinee alla tabella dei punti di intersezione.
Geoprocessing | Intersect ottiene i punti (assicurarsi di specificare i punti desiderati per l'output).
Geoprocessing | Buffer consente di bufferizzare i punti di una piccola quantità. Renderlo davvero minuscolo in modo che la parte di ogni linea all'interno di un buffer non si pieghi.
Geoprocessing | Clip (applicato due volte) limita i livelli di polilinea originali solo ai buffer. Poiché questo produce nuovi set di dati per il suo output, le operazioni successive non modificheranno i dati originali (il che è positivo).
Ecco uno schema di ciò che accade: due strati di polilinea, mostrati in azzurro e rosso chiaro, hanno prodotto punti di intersezione scuri. Attorno a questi punti, i piccoli buffer sono mostrati in giallo. I segmenti blu e rosso più scuri mostrano i risultati del clipping delle caratteristiche originali su questi buffer. Il resto dell'algoritmo funziona con i segmenti scuri. (Non puoi vederlo qui, ma una minuscola polilinea rossa interseca due delle linee blu in un punto comune, producendo quello che sembra essere un buffer attorno a due polilinee blu. Sono in realtà due buffer attorno a due punti sovrapposti di intersezione rosso-blu. , questo diagramma mostra cinque buffer in tutto.)
Utilizzare lo strumento AddField per creare quattro nuovi campi in ciascuno di questi livelli ritagliati: [X0], [Y0], [X1] e [Y1]. Conterranno le coordinate dei punti, quindi falli raddoppiare e dai loro molta precisione.
Calcola geometria (invocata facendo clic con il pulsante destro del mouse su ciascuna nuova intestazione del campo) consente di calcolare le coordinate x e y dei punti iniziale e finale di ciascuna polilinea ritagliata: inserirle in [X0], [Y0], [X1] e [Y1], rispettivamente. Questo viene fatto per ogni livello ritagliato, quindi sono necessari 8 calcoli.
Utilizzare lo strumento AddField per creare un nuovo campo [Angolo] nel livello del punto di intersezione.
Unire le tabelle ritagliate alla tabella dei punti di intersezione in base agli identificatori di oggetti comuni. (I join vengono eseguiti facendo clic con il pulsante destro del mouse sul nome del layer e selezionando "Join e relativi".)
A questo punto la tabella di intersezione dei punti ha 9 nuovi campi: due sono chiamati [X0], ecc. E uno è chiamato [Angolo]. Alias i campi [X0], [Y0], [X1] e [Y1] che appartengono a una delle tabelle unite. Chiamiamo questi (diciamo) "X0a", "Y0a", "X1a" e "Y1a".
Utilizzare il calcolatore di campo per calcolare l'angolo nella tabella delle intersezioni. Ecco un blocco di codice Python per il calcolo:
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
L'espressione di calcolo del campo è, ovviamente, semplicemente
c
Nonostante la lunghezza di questo blocco di codice, la matematica è semplice: (dx, dy) è un vettore di direzione per la prima polilinea e (dxa, dya) è un vettore di direzione per la seconda. Le loro lunghezze, r e ra (calcolate tramite il Teorema di Pitagora), vengono utilizzate per normalizzarle in vettori di unità. (Non dovrebbero esserci problemi con lunghezze pari a zero, perché il clipping dovrebbe produrre caratteristiche di lunghezza positiva.) Le dimensioni del loro prodotto a cuneo dx dya - dydxa (dopo la divisione per r e ra) è il seno dell'angolo. (L'uso del prodotto a cuneo piuttosto che del normale prodotto interno dovrebbe fornire una migliore precisione numerica per angoli vicini allo zero.) Infine, l'angolo viene convertito da radianti a gradi. Il risultato sarà compreso tra 0 e 90. Nota l'evitamento della trigonometria fino alla fine: questo approccio tende a produrre risultati affidabili e facilmente calcolabili.
Alcuni punti possono apparire più volte nel livello di intersezione. In tal caso, otterranno più angoli associati a loro.
Il buffering e il clipping in questa soluzione sono relativamente costosi (passaggi 3 e 4): non si desidera farlo in questo modo quando sono coinvolti milioni di punti di intersezione. L'ho raccomandato perché (a) semplifica il processo di ricerca di due punti successivi lungo ciascuna polilinea all'interno del suo punto di intersezione e (b) il buffering è così semplice da eseguire in qualsiasi GIS - non sono necessarie licenze aggiuntive sopra il livello base di ArcMap - e di solito produce risultati corretti. (Altre operazioni di "geoprocessing" potrebbero non essere così affidabili.)
!table1.x0!
.
Credo che tu debba creare uno script Python.
Puoi farlo usando strumenti di geoprocessing e arcpy.
Ecco i principali strumenti e idee che possono esserti utili:
Può essere molto difficile codificare il passaggio 2 (anche alcuni strumenti richiedono la licenza ArcInfo). Quindi puoi anche provare ad analizzare i vertici di ogni polilinea (raggruppandoli per ID dopo l'intersezione).
Ecco il modo di farlo:
point_x
, point_y
)vert0_x
, vert0_y
) e secondi ( vert1_x
, vert1_y
) vertici di esso.tan0 = (point_y - vert0_y) / (point_x - vert0_x)
tan1 = (vert1_y - point_y) / (vert1_x - point_x)
tan1
è uguale a tan2
, hai trovato due vertici della tua linea che hanno un punto di intersezione tra loro e puoi calcolare l'angolo di intersezione per questa linea. Altrimenti devi passare alla prossima coppia di vertici (seconda, terza) e così via.Di recente stavo cercando di farlo da solo.
La mia funzione di indizio si basa su punti circolari attorno alle intersezioni di linee e su punti situati ad una distanza di un metro dalle intersezioni. L'output è una classe caratteristica polilinea che ha gli attributi del numero di angoli su intersezioni e angolo.
Si noti che le linee devono essere planarizzate al fine di trovare intersezioni e il riferimento spaziale deve essere impostato con la visualizzazione della lunghezza della linea corretta (la mia è WGS_1984_Web_Mercator_Auxitime_Sphere).
In esecuzione nella console ArcMap ma può essere facilmente convertito in uno script nella casella degli strumenti. Questo script utilizza solo il livello di linea in TOC, niente di più.
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)