Трилатерація, використовуючи 3 точки широти / довготи та 3 відстані?


34

Я хочу з’ясувати невідоме цільове місце (координати широти та довготи). Існує 3 відомих точки (пари координат широти та довготи) і для кожної точки відстань у кілометрах до цільового місця. Як я можу обчислити координати цільового місця розташування?

Наприклад, скажіть, що у мене є такі дані даних

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

Мені б хотілося, що це математика для функції, яка приймає це як вхід і повертає 37.417959, -121.961954 як вихід.

Я розумію, як обчислити відстань між двома точками, з http://www.movable-type.co.uk/scripts/latlong.html я розумію загальний принцип, що з трьох подібних кіл ви отримуєте рівно одну точку накладання. Що мені туманне - це математика, необхідна для обчислення цього пункту за допомогою цього вводу.


Ось сторінка, на якій ви пройдете математику пошуку центру трьох координат. Можливо, це може допомогти якимось чином. < mathforum.org/library/drmath/view/68373.html >
Джон Брінгхерст

1
Це має бути у сфері / сфероїді, або планарний алгоритм добре?
fmark

1
Я не можу дати тобі відповіді, але думаю, що можу направити тебе в правильному напрямку. Три координати = три центральні точки. Три відстані = три кола. Два кола, що перетинаються, можуть мати можливість не / один / два рішення. Три кола можуть не мати жодного / одного / або області як його рішення. Отримайте формулу кола для трьох кіл і розв’яжіть його за допомогою систем рівнянь / алгебри.
CrazyEnigma

Насправді вам навіть не потрібні системи для вирішення цього. Є одна або дві можливості, але оскільки у вас є значення відстані, ви можете відокремити правильну відповідь.
Джордж Сільва

1
+1 Це гарне запитання. Спочатку я зрозумів, що рішення можна легко знайти в Google, але, мабуть, ні. Можливо, проблема могла б бути більш загальновикладеною: задавши N балів, кожна точка має не просто відстань, а й помилку, знайдіть еліпс довіри.
Кірк Куйкендалл

Відповіді:


34

Подивившись у Вікіпедію та те саме запитання / відповідь у StackOverflow , я зрозумів, що вдавлюсь до неї, і спробую заповнити прогалини.

По-перше, не впевнений, де ви отримали вихід, але, здається, це неправильно. Я побудував точки в ArcMap, побудував їх на вказані відстані, пробіг перехрестя на буфери, а потім захопив вершину перетину, щоб отримати рішення. Запропонований висновок - крапка зеленого кольору. Я обчислював значення у графі з описом, що становить приблизно 3 метри того, що ArcMap дав для розчину, отриманого з перетину.

alt текст

Математика на сторінці вікіпедії не надто погана, просто потрібно привести свої геодезичні координати до декартового ECEF, який можна знайти тут . умови a / x + h можуть бути замінені аутальним радіусом сфери, якщо ви не використовуєте еліпсоїда.

Напевно, найпростіше просто дати вам якийсь добре (?) Задокументований код, так ось він знаходиться в 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
Я збирався зібрати аналогічну відповідь, але зараз немає потреби! Отримує мою нагороду.
Wrass

нудьга на допомогу! Він компілюється, коли 'triPt' замінюється на 'triLatPt', але в іншому випадку повертається 37.4191023738 -121.960579208. Гарна робота
WolfOdrade

Хороша робота! Якщо я заміню географічну систему координат на локальну [декартову] систему координат, чи все одно це буде працювати?
zengr

для тих , хто в C ++ domain..hacked разом реальний швидкий один pastebin.com/9Dur6RAP
Raaj

2
Дякую @wwnick! Я переніс це в JavaScript (призначений для Node, але його можна легко перетворити на роботу в браузері). gist.github.com/dav-/bb7103008cdf9359887f
DC_

6

Я не впевнений, чи я наївний, але якщо ви забудовуєте кожну точку за розміром, а потім перетинаєте всі три кола, які отримають би вам правильне розташування?

Ви можете обчислити перехрестя за допомогою просторових API. Приклади:

  • GeoScript
  • Java Topology Suite
  • NET Topology Suite
  • ГЕОС

1
Саме його цікавлять формули, щоб отримати цю точку перетину.
Вінко Врсалович

Використовуючи просторовий API, ви можете це зробити без використання чистої математики.
Джордж Сільва

1
@George, ви можете навести приклад такого API?
nohat

Відредагована публікація, щоб відображати запит ні на що.
Джордж Сільва

+1, гарне латеральне мислення, навіть якщо не, можливо, найбільш обчислювально ефективне!
fmark

2

У наступних примітках використовується планаритмічна геометрія (тобто вам доведеться проектувати свої координати у відповідну локальну систему координат).

Мої міркування з відпрацьованого прикладу в Python випливають:

Візьміть 2 точки даних (дзвоніть їм aі b). Зателефонуйте до нашої цільової точки x. Ми вже знаємо відстані axі bx. Ми можемо обчислити відстань, abвикористовуючи теорему Піфагора.

>>> 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

Тепер ви можете опрацювати кути цих ліній:

>>> 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

На жаль, мені не вистачає часу, щоб завершити відповідь за вас, однак, тепер ви знаєте кути, ви можете розрахувати два можливі місця x. Потім, використовуючи третю точку c, ви можете обчислити, яке місце правильне.


2

Це може спрацювати. Швидко знову в python ви можете поставити це в тілі функції xN, yN = координати точок, r1 & r2 = значення радіуса

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))

Значення rx & ry - це значення, що повертаються (мають бути в масиві) двох точок перетину на колі, якщо це допомагає з’ясувати речі.

Зробіть це для перших 2 кіл, потім знову для першого та останнього. Якщо будь-який з результатів першої ітерації порівняти з результатами другого (можливо, в межах деякої толерантності), ви маєте точку перетину. Це не чудове рішення, особливо коли ви починаєте додавати в процес більше, ніж балів, але це найпростіше, що я бачу, не збираючись вирішувати систему рівнянь.


Що означають 'e' та 'k' у вашому коді?
ReinierDG

Я не пам’ятаю :-) відповідь wwnick більше узгоджується з тим, що ви хотіли б реалізувати, якщо у вас просто три кола.
WolfOdrade

1

Ви можете використовувати просторовий API від postgis (St_Intersection, St_buffer функції). Як зауважив fmark, ви також повинні пам’ятати, що Postgis використовує планарні алгоритми, але для невеликих областей використання рівновіддаленого прогнозування не вводить великих помилок.


PostGIS може робити сфероїдальні обчислення, використовуючи GEOGRAPHYтип, а не GEOMETRYтип.
fmark

1

Зробіть це мовою PHP:

// припускаючи висоту = 0
$ земляR = 6371; // в км (= 3959 миль)

$ 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;

/ *
# використовуюча ауталічна сфера
# Якщо за допомогою еліпсоїда цей крок дещо інший
# Конвертувати геодезичний Lat / Long в ECEF xyz
№ 1. Перетворити Lat / Long в радіани
№ 2. Перетворити Lat / Long (радіани) в 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)));

/ *
ВСТАНОВИТИ:
встановити грушу sudo Math_Vector-0.7.0
встановити грушу sudo Math_Matrix-0.8.7
* /
// Включити PEAR :: Math_Matrix
// /usr/share/php/Math/Matrix.php
// include_path = ".: / usr / local / php / pear /"
Requ_once 'Math / Matrix.php';
need_once 'Math / Vector.php';
need_once 'Math / Vector3.php';


$ P1vector = новий Math_Vector3 (масив ($ xA, $ yA, $ zA));
$ P2vector = новий Math_Vector3 (масив ($ xB, $ yB, $ zB));
$ P3vector = новий Math_Vector3 (масив ($ xC, $ yC, $ zC));

# від wikipedia: http://en.wikipedia.org/wiki/Trilateration
#transform, щоб отримати коло 1 на початку
#transform, щоб отримати коло 2 по осі x

// CALC EX
$ P2minusP1 = Math_VectorOp :: субстрат ($ P2vector, $ P1vector);
$ l = новий Math_Vector ($ P2minusP1);
$ P2minusP1_length = $ l-> length ();
$ norm = новий Math_Vector3 (масив ($ P2minusP1_length, $ P2minusP1_length, $ P2minusP1_length));
$ d = $ норма; // зберегти calc D
$ ex = Math_VectorOp :: ділити ($ P2minusP1, $ норма);
// 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 = новий Math_Vector3 (масив ($ ex_x, $ ex_y, $ ex_z));

// CALC i
$ P3minusP1 = Math_VectorOp :: субстрат ($ P3vector, $ P1vector);
$ P3minusP1_x = floatval ($ P3minusP1 -> _ tuple-> getData () [0]);
$ P3minusP1_y = floatval ($ P3minusP1 -> _ tuple-> getData () [1]);
$ P3minusP1_z = floatval ($ P3minusP1 -> _ tuple-> getData () [2]);
$ P3minusP1 = новий Math_Vector3 (масив ($ P3minusP1_x, $ P3minusP1_y, $ P3minusP1_z));
$ i = Math_VectorOp :: dotProduct ($ ex, $ P3minusP1);
// відлуння "i = $ i \ n";

// CALC EY
$ iex = Math_VectorOp :: шкала ($ i, $ ex);
// echo "iex =". $ iex-> toString (). "\ n";
$ P3P1iex = Math_VectorOp :: субстрат ($ P3minusP1, $ iex);
// echo "P3P1iex =". $ P3P1iex-> toString (). "\ n";
$ l = новий Math_Vector ($ P3P1iex);
$ P3P1iex_length = $ l-> length ();
$ norm = новий Math_Vector3 (масив ($ P3P1iex_length, $ P3P1iex_length, $ P3P1iex_length));
// echo "норма:". $ norm-> toString (). "\ n";
$ ey = Math_VectorOp :: ділити ($ P3P1iex, $ норма);
// 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 = новий Math_Vector3 (масив ($ ey_x, $ ey_y, $ ey_z));

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

// CALC D
// робити це раніше
$ d = floatval ($ d -> _ tuple-> getData () [0]);
// відлуння "d = $ d \ n";

// CALC J
$ j = Math_VectorOp :: dotProduct ($ ey, $ P3minusP1);
// відлуння "j = $ j \ n";

# від wikipedia
#plug and chug, використовуючи вищевказані значення
$ 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);

# показаний лише один випадок
$ z = sqrt (pow ($ DistA, 2) - pow ($ x, 2) - pow ($ y, 2));

// відлуння "x = $ x - y = $ y - z = $ z \ n";

#triPt - це масив з ECEF x, y, z точки трилатерації
$ xex = Math_VectorOp :: шкала ($ x, $ ex);
$ yey = Math_VectorOp :: шкала ($ y, $ ey);
$ zez = Math_VectorOp :: шкала ($ 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]);


#convert назад до лат. / довгий з ECEF
# конвертувати в градуси
$ lat = rad2deg (asin ($ triPt_z / $ earthR));
$ lon = rad2deg (atan2 ($ triPt_y, $ triPt_x));

echo $ lat. ','. $ lon;
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.