2015-02-01 24 views
6

Sto lavorando su un problema di analisi spaziale e parte di questo flusso di lavoro consiste nel calcolare l'angolo tra i segmenti di linea collegati.Calcolo degli angoli tra i segmenti di linea (Python) con math.atan2

Ogni segmento di linea è composto da due soli punti e ogni punto ha una coppia di coordinate XY (cartesiane). Ecco l'immagine di GeoGebra. Sono sempre interessato a ottenere un angolo positivo nell'intervallo da 0 a 180. Tuttavia, ottengo tutti i tipi di angoli a seconda dell'ordine dei vertici nei segmenti di linea di input.

enter image description here

I dati di ingresso con cui lavoro è fornito come tuple di coordinate. A seconda dell'ordine di creazione del vertice, il punto finale/finale per ogni segmento di linea può essere diverso. Ecco alcuni dei casi in codice Python. L'ordine dei segmenti di linea in cui li ottengo è casuale, ma in una tupla di tuple, il primo elemento è il punto iniziale e il secondo è il punto finale. DE segmento di linea, ad esempio, avrebbe ((1,1.5),(2,2)) e (1,1.5) è il punto di partenza perché ha la prima posizione nella tupla di coordinate.

Tuttavia, è necessario accertarsi di ottenere lo stesso angolo tra DE,DF e ED,DF e così via.

vertexType = "same start point; order 1" 
      #X, Y X Y coords 
lineA = ((1,1.5),(2,2)) #DE 
lineB = ((1,1.5),(2.5,0.5)) #DF 
calcAngle(lineA, lineB,vertexType) 
#flip lines order 
vertexType = "same start point; order 2" 
lineB = ((1,1.5),(2,2)) #DE 
lineA = ((1,1.5),(2.5,0.5)) #DF 
calcAngle(lineA, lineB,vertexType) 

vertexType = "same end point; order 1" 
lineA = ((2,2),(1,1.5)) #ED 
lineB = ((2.5,0.5),(1,1.5)) #FE 
calcAngle(lineA, lineB,vertexType) 
#flip lines order 
vertexType = "same end point; order 2" 
lineB = ((2,2),(1,1.5)) #ED 
lineA = ((2.5,0.5),(1,1.5)) #FE 
calcAngle(lineA, lineB,vertexType) 

vertexType = "one line after another - down; order 1" 
lineA = ((2,2),(1,1.5)) #ED 
lineB = ((1,1.5),(2.5,0.5)) #DF 
calcAngle(lineA, lineB,vertexType) 
#flip lines order 
vertexType = "one line after another - down; order 2" 
lineB = ((2,2),(1,1.5)) #ED 
lineA = ((1,1.5),(2.5,0.5)) #DF 
calcAngle(lineA, lineB,vertexType) 

vertexType = "one line after another - up; line order 1" 
lineA = ((1,1.5),(2,2)) #DE 
lineB = ((2.5,0.5),(1,1.5)) #FD 
calcAngle(lineA, lineB,vertexType) 
#flip lines order 
vertexType = "one line after another - up; line order 2" 
lineB = ((1,1.5),(2,2)) #DE 
lineA = ((2.5,0.5),(1,1.5)) #FD 
calcAngle(lineA, lineB,vertexType) 

Ho scritto una piccola funzione che prende combinazioni di linee come args e calcola l'angolo tra di esse. Sto usando il math.atan2 che sembrava essere il più adatto per questo.

def calcAngle(lineA,lineB,vertexType): 
    line1Y1 = lineA[0][1] 
    line1X1 = lineA[0][0] 
    line1Y2 = lineA[1][1] 
    line1X2 = lineA[1][0] 

    line2Y1 = lineB[0][1] 
    line2X1 = lineB[0][0] 
    line2Y2 = lineB[1][1] 
    line2X2 = lineB[1][0] 

    #calculate angle between pairs of lines 
    angle1 = math.atan2(line1Y1-line1Y2,line1X1-line1X2) 
    angle2 = math.atan2(line2Y1-line2Y2,line2X1-line2X2) 
    angleDegrees = (angle1-angle2) * 360/(2*math.pi) 
    print angleDegrees, vertexType 

L'output che ottengo è:

> -299.744881297 same start point; order 1 
> 299.744881297 same start point; order 2 
> 60.2551187031 same end point; order 1 
> -60.2551187031 same end point; order 2 
> -119.744881297 one line after another - down; order 1 
> 119.744881297 one line after another - down; order 2 
> -119.744881297 one line after another - up; line order 1 
> 119.744881297 one line after another - up; line order 2 

Come potete vedere, io sono sempre valori diversi a seconda l'ordine dei vertici in un ordine segmento di linea e segmenti di linea. Ho provato a post-processare gli angoli scoprendo quale tipo di relazione avesse la linea sorgente e sfogliando le righe, modificando l'angolo, ecc. Ho chiuso con una dozzina di casi simili e ad un certo punto iniziano a sovrapporsi e io non può più sapere se -119,744 dovrebbe diventare 60,255 (angolo acuto) oppure essere lasciato come 119,744 (angolo ottuso) ecc

esiste un modo discreto per elaborare i valori di angolo di uscita inviate da math.atan2 per ottenere solo valore positivo nell'intervallo da 0 a 180?In caso negativo, che tipo di altro approccio dovrei prendere?

risposta

6

Il modo più semplice e più logico per risolvere questo problema è utilizzare il prodotto dot.

provare questo codice (ho commentato praticamente tutto):

import math 
def dot(vA, vB): 
    return vA[0]*vB[0]+vA[1]*vB[1] 
def ang(lineA, lineB): 
    # Get nicer vector form 
    vA = [(lineA[0][0]-lineA[1][0]), (lineA[0][1]-lineA[1][1])] 
    vB = [(lineB[0][0]-lineB[1][0]), (lineB[0][1]-lineB[1][1])] 
    # Get dot prod 
    dot_prod = dot(vA, vB) 
    # Get magnitudes 
    magA = dot(vA, vA)**0.5 
    magB = dot(vB, vB)**0.5 
    # Get cosine value 
    cos_ = dot_prod/magA/magB 
    # Get angle in radians and then convert to degrees 
    angle = math.acos(dot_prod/magB/magA) 
    # Basically doing angle <- angle mod 360 
    ang_deg = math.degrees(angle)%360 

    if ang_deg-180>=0: 
     # As in if statement 
     return 360 - ang_deg 
    else: 

     return ang_deg 

Ora provate le varianti di Linea e lineB e tutti dovrebbero dare la stessa risposta.

+0

L'essenza del codice di test e dell'output è [qui] (https://gist.github.com/abhinavrk/2100332f7d7f4c127b8c) –

+0

grazie per lo snippet di codice, ottimo per iniziare. Il problema con il codice è che non riporta mai angoli ottusi (> 90). Mettiti alla prova con '' lineA = ((0.6,3.6), (1,6,3)) riga B = ((1,6,3), (2,3,6)) ''. Segnala 87,27, ma dovrebbe essere 92,73. Cosa si potrebbe fare per risolverlo? –

+0

Ho modificato il mio post. Credo di averlo aggiustato per dare anche degli angoli ottusi. Guarda. –

1

Troppo lavoro. Prendi il valore assoluto dell'arcoseno dello dot product dei due vettori diviso per ciascuna delle lunghezze delle linee.

Problemi correlati