Trilaterazione utilizzando 3 punti di latitudine / longitudine e 3 distanze?


34

Voglio scoprire una posizione target sconosciuta (coordinate di latitudine e longitudine). Esistono 3 punti noti (coppie di coordinate di latitudine e longitudine) e per ogni punto una distanza in chilometri dalla posizione target. Come posso calcolare le coordinate della posizione target?

Ad esempio, supponiamo che io abbia i seguenti punti dati

37.418436,-121.963477   0.265710701754km
37.417243,-121.961889   0.234592423446km
37.418692,-121.960194   0.0548954278262km

Quello che mi piacerebbe è qual è la matematica per una funzione che lo prende come input e restituisce 37.417959, -121.961954 come output.

Capisco come calcolare la distanza tra due punti, da http://www.movable-type.co.uk/scripts/latlong.html Comprendo il principio generale che con tre cerchi come questi ottieni esattamente un punto di sovrapposizione. Ciò su cui sono confuso è la matematica necessaria per calcolare quel punto con questo input.


Ecco una pagina che ti guida attraverso la matematica per trovare il centro di tre coordinate. Forse potrebbe aiutare in qualche modo. < mathforum.org/library/drmath/view/68373.html >
Jon Bringhurst

1
Questo deve essere sulla sfera / sferoide o un algoritmo planare va bene?
fmark

1
Non posso darti la risposta, ma penso di poterti indirizzare nella giusta direzione. Tre coordinate = tre punti centrali. Tre distanze = tre cerchi. Due cerchi che si intersecano possono avere una / nessuna / due soluzioni. Tre cerchi possono avere nessuno / uno / o un'area come soluzione. Ottieni la formula del cerchio per i tre cerchi e risolverlo con Sistemi di equazioni / Algebra.
CrazyEnigma,

In realtà, non hai nemmeno bisogno di sistemi per risolvere questo. Esistono una o due possibilità, ma poiché hai un valore di distanza, puoi separare la risposta corretta.
George Silva,

1
+1 Questa è una buona domanda. All'inizio ho pensato che una soluzione potesse essere facilmente trovata con google, ma apparentemente no. Forse il problema potrebbe essere affermato più in generale: dati N punti con ogni punto che non ha solo una distanza ma anche un margine di errore, trova l'ellisse di confidenza.
Kirk Kuykendall,

Risposte:


34

Dopo aver guardato intorno a Wikipedia e la stessa domanda / risposta su StackOverflow , ho pensato che avrei preso un colpo, e ho cercato di colmare le lacune.

Prima di tutto, non sono sicuro di dove hai ottenuto l'output, ma sembra essere sbagliato. Ho tracciato i punti in ArcMap, li ho bufferizzati alle distanze specificate, ho eseguito l'intersezione sui buffer e quindi ho catturato il vertice dell'intersezione per ottenere le soluzioni. L'output proposto è il punto in verde. Ho calcolato il valore nella casella callout, che è circa 3 metri da ciò che ArcMap ha dato per la soluzione derivata dall'intersezione.

testo alternativo

La matematica sulla pagina di Wikipedia non è poi così male, basta solo convertire le coordinate geodetiche nell'ECEF cartesiano, che puoi trovare qui . i termini a / x + h possono essere sostituiti dal raggio della sfera autalica, se non si utilizza un ellissoide.

Probabilmente il più semplice ti dà un po 'di codice (?) Ben documentato, quindi eccolo in Python

import math
import numpy

#assuming elevation = 0
earthR = 6371
LatA = 37.418436
LonA = -121.963477
DistA = 0.265710701754
LatB = 37.417243
LonB = -121.961889
DistB = 0.234592423446
LatC = 37.418692
LonC = -121.960194
DistC = 0.0548954278262

#using authalic sphere
#if using an ellipsoid this step is slightly different
#Convert geodetic Lat/Long to ECEF xyz
#   1. Convert Lat/Long to radians
#   2. Convert Lat/Long(radians) to ECEF
xA = earthR *(math.cos(math.radians(LatA)) * math.cos(math.radians(LonA)))
yA = earthR *(math.cos(math.radians(LatA)) * math.sin(math.radians(LonA)))
zA = earthR *(math.sin(math.radians(LatA)))

xB = earthR *(math.cos(math.radians(LatB)) * math.cos(math.radians(LonB)))
yB = earthR *(math.cos(math.radians(LatB)) * math.sin(math.radians(LonB)))
zB = earthR *(math.sin(math.radians(LatB)))

xC = earthR *(math.cos(math.radians(LatC)) * math.cos(math.radians(LonC)))
yC = earthR *(math.cos(math.radians(LatC)) * math.sin(math.radians(LonC)))
zC = earthR *(math.sin(math.radians(LatC)))

P1 = numpy.array([xA, yA, zA])
P2 = numpy.array([xB, yB, zB])
P3 = numpy.array([xC, yC, zC])

#from wikipedia
#transform to get circle 1 at origin
#transform to get circle 2 on x axis
ex = (P2 - P1)/(numpy.linalg.norm(P2 - P1))
i = numpy.dot(ex, P3 - P1)
ey = (P3 - P1 - i*ex)/(numpy.linalg.norm(P3 - P1 - i*ex))
ez = numpy.cross(ex,ey)
d = numpy.linalg.norm(P2 - P1)
j = numpy.dot(ey, P3 - P1)

#from wikipedia
#plug and chug using above values
x = (pow(DistA,2) - pow(DistB,2) + pow(d,2))/(2*d)
y = ((pow(DistA,2) - pow(DistC,2) + pow(i,2) + pow(j,2))/(2*j)) - ((i/j)*x)

# only one case shown here
z = numpy.sqrt(pow(DistA,2) - pow(x,2) - pow(y,2))

#triPt is an array with ECEF x,y,z of trilateration point
triPt = P1 + x*ex + y*ey + z*ez

#convert back to lat/long from ECEF
#convert to degrees
lat = math.degrees(math.asin(triPt[2] / earthR))
lon = math.degrees(math.atan2(triPt[1],triPt[0]))

print lat, lon

1
Stavo per mettere insieme una risposta simile, ma ora non è necessario! Ottiene il mio voto.
Wrass,

intorpidito in soccorso! Si compila quando 'triPt' viene sostituito con 'triLatPt', ma in caso contrario restituisce 37.4191023738 -121.960579208. Buon lavoro
WolfOdrade,

Buon lavoro! Se sostituisco il sistema di coordinate geografiche a un sistema di coordinate [cartesiano] locale, funzionerà comunque?
zengr,

per quelli nel dominio c ++..ha messo insieme uno molto veloce pastebin.com/9Dur6RAP
raaj

2
Grazie @wwnick! L'ho portato su JavaScript (destinato a Node ma può essere facilmente convertito per funzionare nel browser). gist.github.com/dav-/bb7103008cdf9359887f
DC_

6

Non sono sicuro di essere ingenuo, ma, se si buffer ogni punto per dimensione, e quindi si intersecano tutti e tre i cerchi che ti porterebbero nella posizione corretta?

È possibile calcolare l'intersezione utilizzando le API spaziali. Esempi:

  • GeoScript
  • Suite di topologia Java
  • NET Topology Suite
  • GEOS

1
Esattamente, è interessato alle formule per ottenere quel punto di intersezione.
Vinko Vrsalovic,

Usando un'API spaziale puoi farlo senza usare la matematica pura.
George Silva,

1
@George puoi fare un esempio di tale API?
no, il

Post modificato per riflettere la richiesta di nessuno.
George Silva,

+1, buon pensiero laterale, anche se forse non il più efficiente dal punto di vista computazionale!
fmark

2

Le seguenti note usano la geometria planaritmica (cioè dovresti proiettare le tue coordinate in un sistema di coordinate locale appropriato).

Il mio ragionamento, con un esempio funzionante in Python, segue:

Prendi 2 dei punti dati (chiamali ae b). Chiama il nostro punto di arrivo x. Conosciamo già le distanze axe bx. Possiamo calcolare la distanza abusando il teorema di Pitagora.

>>> import math
>>> a = (1, 4)
>>> b = (3, 6)
>>> dist_ax = 3
>>> dist_bx = 5.385
# Pythagoras's theorem
>>> dist_ab = math.sqrt(abs(a[0]-b[0])**2 + abs(a[1]-b[1])**2)
>>> dist_ab
2.8284271247461903

Ora puoi capire gli angoli di queste linee:

>>> angle_abx = math.acos((dist_bx * dist_bx + dist_ab * dist_ab - dist_ax * dist_ax)/(2 * dist_bx * dist_ab))
>>> math.degrees(angle_abx)
23.202973815040256
>>> angle_bax = math.acos((dist_ax * dist_ax + dist_ab * dist_ab - dist_bx * dist_bx)/(2 * dist_ax * dist_ab))
>>> math.degrees(angle_bax)
134.9915256259537
>>> angle_axb = math.acos((dist_ax * dist_ax + dist_bx * dist_bx - dist_ab * dist_ab)/(2 * dist_ax * dist_bx))
>>> math.degrees(angle_axb)
21.805500559006095

Purtroppo ho poco tempo per completare la risposta, tuttavia, ora che conosci gli angoli, puoi calcolare due possibili posizioni per x. Quindi, utilizzando il terzo punto c, è possibile calcolare quale posizione è corretta.


2

Questo potrebbe funzionare. Di nuovo rapidamente in Python, potresti inserirlo nel corpo di una funzione xN, yN = coordinate dei punti, r1 & r2 = valori del raggio

dX = x2 - x1
dY = y2 - y1

centroidDistance = math.sqrt(math.pow(e,2) + math.pow(dY,2)) #distance from centroids
distancePL = (math.pow(centroidDistance,2) + (math.pow(r1,2) - math.pow(r2,2))) / (2 * centroidDistance) #distance from point to a line splitting the two centroids

rx1 = x1 + (dX *k)/centroidDistance + (dY/centroidDistance) * math.sqrt(math.pow(r1,2) - math.pow(distancePL,2))
ry1 = y1 + (dY*k)/centroidDistance - (dX /centroidDistance) * math.sqrt(math.pow(r1,2) - math.pow(distancePL,2))

rx2 = x1 + (dX *k)/centroidDistance - (dY/centroidDistance) * math.sqrt(math.pow(r1,2) - math.pow(distancePL,2))
ry2 = y1 + (dY*k)/centroidDistance + (dX /centroidDistance) * math.sqrt(math.pow(r1,2) - math.pow(distancePL,2))

I valori rx & ry sono i valori di ritorno (dovrebbero essere in una matrice) dei due punti di intersezione su un cerchio, se ciò aiuta a chiarire le cose.

Fallo per i primi 2 cerchi, poi di nuovo per il primo e l'ultimo. Se uno dei risultati della prima iterazione si confronta con i risultati della seconda (probabilmente entro una certa tolleranza), allora hai il punto di intersezione. Non è un'ottima soluzione soprattutto quando inizi ad aggiungere più di punti nel processo, ma è la più semplice che posso vedere senza andare a risolvere un sistema di equazioni.


Cosa sono 'e' e 'k' nel tuo codice?
ReinierDG

Non ricordo :-) la risposta di Wwnick è più sulla falsariga di qualcosa che vorresti implementare se avessi solo tre cerchi.
WolfOdrade,

1

Puoi usare l'API spaziale di postgis (funzioni St_Intersection, St_buffer). Come notato da fmark, devi anche ricordare che Postgis usa algoritmi planari, ma per piccole aree, usando la predizione equidistante non si presentano molti errori.


PostGIS può eseguire calcoli sferoidali utilizzando il GEOGRAPHYtipo anziché il GEOMETRYtipo.
fmark

1

Fallo in linguaggio PHP:

// assumendo elevazione = 0
$ earthR = 6371; // in km (= 3959 in miglia)

$ LatA = 37.418436;
$ LonA = -121,963477;
$ DistA = 0,265710701754;

$ LatB = 37.417243;
$ LonB = -121.961889;
$ DistB = 0,234592423446;

$ LatC = 37.418692;
$ LonC = -121.960194;
$ DistC = 0,0548954278262;

/ *
#utilizzando sfera autalica
#se usando un ellissoide questo passaggio è leggermente diverso
# Converti lat geodetico / Long in ECEF xyz
# 1. Converti Lat / Long in radianti
# 2. Converti Lat / Long (radianti) in ECEF
* /
$ xA = $ earthR * (cos (deg2rad ($ LatA)) * cos (deg2rad ($ LonA)));
$ yA = $ earthR * (cos (deg2rad ($ LatA)) * sin (deg2rad ($ LonA)));
$ zA = $ earthR * (sin (deg2rad ($ LatA)));

$ xB = $ earthR * (cos (deg2rad ($ LatB)) * cos (deg2rad ($ LonB)));
$ yB = $ earthR * (cos (deg2rad ($ LatB)) * sin (deg2rad ($ LonB)));
$ zB = $ earthR * (sin (deg2rad ($ LatB)));

$ xC = $ earthR * (cos (deg2rad ($ LatC)) * cos (deg2rad ($ LonC)));
$ yC = $ earthR * (cos (deg2rad ($ LatC)) * sin (deg2rad ($ LonC)));
$ zC = $ earthR * (sin (deg2rad ($ LatC)));

/ *
INSTALLARE:
sudo pear installa Math_Vector-0.7.0
sudo pear installa Math_Matrix-0.8.7
* /
// Includi PEAR :: Math_Matrix
// /usr/share/php/Math/Matrix.php
// include_path = ".: / usr / local / php / pear /"
require_once 'Math / Matrix.php';
require_once 'Math / Vector.php';
require_once 'Math / Vector3.php';


$ P1vector = new Math_Vector3 (array ($ xA, $ yA, $ zA));
$ P2vector = new Math_Vector3 (array ($ xB, $ yB, $ zB));
$ P3vector = new Math_Vector3 (array ($ xC, $ yC, $ zC));

#da wikipedia: http://it.wikipedia.org/wiki/Trilateration
#transform per ottenere il cerchio 1 all'origine
#transform per ottenere il cerchio 2 sull'asse x

// CALC EX
$ P2minusP1 = Math_VectorOp :: substract ($ P2vector, $ P1vector);
$ l = nuovo Math_Vector ($ P2minusP1);
$ P2minusP1_length = $ l-> length ();
$ norm = new Math_Vector3 (array ($ P2minusP1_length, $ P2minusP1_length, $ P2minusP1_length));
$ d = $ norma; // salva calc D
$ ex = Math_VectorOp :: divide ($ P2minusP1, $ norm);
// echo "ex:". $ ex-> toString (). "\ n";
$ ex_x = floatval ($ ex -> _ tuple-> getData () [0]);
$ ex_y = floatval ($ ex -> _ tuple-> getData () [1]);
$ ex_z = floatval ($ ex -> _ tuple-> getData () [2]);
$ ex = new Math_Vector3 (array ($ ex_x, $ ex_y, $ ex_z));

// CALC i
$ P3minusP1 = Math_VectorOp :: substract ($ P3vector, $ P1vector);
$ P3minusP1_x = floatval ($ P3minusP1 -> _ tuple-> getData () [0]);
$ P3minusP1_y = floatval ($ P3minusP1 -> _ tuple-> getData () [1]);
$ P3minusP1_z = floatval ($ P3minusP1 -> _ tuple-> getData () [2]);
$ P3minusP1 = new Math_Vector3 (array ($ P3minusP1_x, $ P3minusP1_y, $ P3minusP1_z));
$ i = Math_VectorOp :: dotProduct ($ ex, $ P3minusP1);
// echo "i = $ i \ n";

// CALC EY
$ iex = Math_VectorOp :: scale ($ i, $ ex);
// echo "iex =". $ iex-> toString (). "\ n";
$ P3P1iex = Math_VectorOp :: substract ($ P3minusP1, $ iex);
// echo "P3P1iex =". $ P3P1iex-> toString (). "\ n";
$ l = nuovo Math_Vector ($ P3P1iex);
$ P3P1iex_length = $ l-> length ();
$ norm = new Math_Vector3 (array ($ P3P1iex_length, $ P3P1iex_length, $ P3P1iex_length));
// echo "norm:". $ norm-> toString (). "\ n";
$ ey = Math_VectorOp :: divide ($ P3P1iex, $ norm);
// echo "ey =". $ ey-> toString (). "\ n";
$ ey_x = floatval ($ ey -> _ tuple-> getData () [0]);
$ ey_y = floatval ($ ey -> _ tuple-> getData () [1]);
$ ey_z = floatval ($ ey -> _ tuple-> getData () [2]);
$ ey = new Math_Vector3 (array ($ ey_x, $ ey_y, $ ey_z));

// CALC EZ
$ ez = Math_VectorOp :: crossProduct ($ ex, $ ey);
// echo "ez =". $ ez-> toString (). "\ n";

// CALC D
// fallo prima
$ d = floatval ($ d -> _ tuple-> getData () [0]);
// echo "d = $ d \ n";

// CALC J
$ j = Math_VectorOp :: dotProduct ($ ey, $ P3minusP1);
// echo "j = $ j \ n";

#da Wikipedia
#plug e chug usando i valori sopra
$ x = (pow ($ DistA, 2) - pow ($ DistB, 2) + pow ($ d, 2)) / (2 * $ d);
$ y = ((pow ($ DistA, 2) - pow ($ DistC, 2) + pow ($ i, 2) + pow ($ j, 2)) / (2 * $ j)) - (($ i / $ j) * $ x);

# solo un caso mostrato qui
$ z = sqrt (pow ($ DistA, 2) - pow ($ x, 2) - pow ($ y, 2));

// echo "x = $ x - y = $ y - z = $ z \ n";

#triPt è un array con ECEF x, y, z del punto di trilaterazione
$ xex = Math_VectorOp :: scale ($ x, $ ex);
$ yey = Math_VectorOp :: scale ($ y, $ ey);
$ zez = Math_VectorOp :: scale ($ z, $ ez);

// CALC $ triPt = $ P1vector + $ xex + $ yey + $ zez;
$ triPt = Math_VectorOp :: add ($ P1vector, $ xex);
$ triPt = Math_VectorOp :: add ($ triPt, $ yey);
$ triPt = Math_VectorOp :: add ($ triPt, $ zez);
// echo "triPt =". $ triPt-> toString (). "\ n";
$ triPt_x = floatval ($ triPt -> _ tuple-> getData () [0]);
$ triPt_y = floatval ($ triPt -> _ tuple-> getData () [1]);
$ triPt_z = floatval ($ triPt -> _ tuple-> getData () [2]);


# converte indietro in lat / long da ECEF
# converti in gradi
$ lat = rad2deg (asin ($ triPt_z / $ earthR));
$ lon = rad2deg (atan2 ($ triPt_y, $ triPt_x));

echo $ lat. ','. $ Lon;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.