3 enlem / boylam noktası ve 3 mesafeden oluşan trilatülasyon?


34

Bilinmeyen bir hedef konum bulmak istiyorum (enlem ve boylam koordinatları). Bilinen 3 nokta (enlem ve boylam koordinasyon çiftleri) ve her nokta için hedef konuma kilometre cinsinden bir mesafe vardır. Hedef yerin koordinatlarını nasıl hesaplayabilirim?

Örneğin, aşağıdaki veri noktalarına sahip olduğumu varsayalım.

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

İstediğim şey, girdi olarak alan ve çıktı olarak 37.417959, -121.961954 döndüren bir fonksiyonun matematiğidir.

Http://www.movable-type.co.uk/scripts/latlong.html adresinden iki nokta arasındaki mesafenin nasıl hesaplanacağını anlıyorum. Tehlikeli olduğum şey, bu noktayı bu girdiyle hesaplamak için gereken matematik.


İşte size üç koordinatın merkezini bulma matematiğini yönlendiren bir sayfa. Belki bir şekilde yardımcı olabilir. < mathforum.org/library/drmath/view/68373.html >
Jon Bringhurst

1
Bunun küre / küre üzerinde olması mı gerekiyor yoksa bir düzlemsel algoritma uygun mu?
10:49

1
Sana cevabı veremem ama sanırım seni doğru yöne işaret edebilirim. Üç koordinat = üç merkez noktası. Üç mesafe = üç daire. Kesişen iki daire hiçbirinin / bir / iki çözüm olasılığına sahip olamaz. Üç Çember, çözümü olarak hiçbir / bir / veya bir alana sahip olamaz. Üç çember için çember formülünü elde edin ve bunu Denklem Sistemleri / Cebir ile çözün.
CrazyEnigma

Aslında, bunu çözmek için sistemlere bile ihtiyacınız yok. Bir veya iki olasılık var, ancak mesafe değerine sahip olduğunuzdan, doğru cevabı ayırabilirsiniz.
George Silva,

1
+1 Bu iyi bir soru. İlk başta bir çözümün google ile kolayca bulunabileceğini düşündüm, ancak görünüşe göre bulamadım. Belki de sorun daha genel bir şekilde ifade edilebilir: Her bir nokta için yalnızca bir mesafe değil, aynı zamanda bir hata payı olan N noktaları göz önüne alındığında, güven elipsini bulun.
Kirk Kuykendall

Yanıtlar:


34

Bazıları Wikipedia'ya ve StackOverflow'ta aynı soruya / cevaba baktıktan sonra , ona bir göz atacağımı ve boşlukları doldurmaya çalışacağımı düşündüm.

İlk önce, çıktıyı nereden aldığınızdan emin değilsiniz, ancak yanlış görünüyor. Noktaları ArcMap'te çizdim, onları belirtilen mesafelere tamponladım, tamponlarla kesiştim ve sonra çözümleri elde etmek için kesişimin tepe noktasını yakaladım. Önerilen çıktı, yeşil olan nokta. Belirtme kutusundaki değeri ArcMap'in kesişimden türetilen çözüm için verdiği değerin yaklaşık 3 metre olanını hesapladım.

alt metin

Wikipedia sayfasında matematik sadece bulunabilir kartezyen ECEF, sizin jeodezik koordinatları gizli için ihtiyaç çok fena değil burada . Eğer bir elipsoid kullanmıyorsanız a / x + h terimleri otoriter küre yarıçapı ile değiştirilebilir.

Muhtemelen en kolayı sadece bazı iyi (?) Dökümanlı kodlar verir, işte burada 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
Benzer bir cevabı bir araya getirecektim ama şimdi gerek yok! Olumlu oyumu alır.
Wrass,

kurtarmaya hazır! 'TriPt', 'triLatPt' ile değiştirildiğinde derler, ancak aksi takdirde 37.4191023738 -121.960579208 döndürür. İyi iş
WolfOdrade

İyi iş! Coğrafi koordinat sistemini yerel bir [Kartezyen] koordinat sistemine değiştirirsem, bu hala işe yarar mı?
zengr

c ++ etki alanında olanlar için ... birlikte çok hızlı bir hacked pastebin.com/9Dur6RAP
raaj,

2
@Wwnick teşekkürler! Bunu JavaScript'e taşıdım (Düğüm için tasarlanmıştır, ancak tarayıcıda çalışmaya kolayca dönüştürülebilir). gist.github.com/dav-/bb7103008cdf9359887f
DC_

6

Naif olup olmadığımdan emin değilim, ancak her noktayı boyuta göre tamponlarsanız ve sonra da sizi doğru yere götürecek üç dairenin kesişmesini sağlarsanız?

Mekansal API'leri kullanarak kesişimi hesaplayabilirsiniz. Örnekler:

  • GeoScript
  • Java Topolojisi Takımı
  • NET Topoloji Takımı
  • GEOS

1
Kesinlikle, kesişme noktasını elde etmek için formüllerle ilgileniyor.
Vinko Vrsalovic

Uzamsal bir API kullanarak, saf matematik kullanmadan yapabilirsiniz.
George Silva,

1
@George böyle bir API örneği verebilir misiniz?
nohat

Hiç kimsenin isteğini yansıtmayacak şekilde düzenlenmiş yayın.
George Silva,

+1, iyi bir yanal düşünce, belki de en hesaplamalı olarak verimli olmasa bile!
fmark

2

Aşağıdaki notlar düzlemsel geometri kullanır (yani koordinatlarınızı uygun bir yerel koordinat sistemine yansıtmanız gerekir).

Mantığım, Python'da çalışılmış bir örnekle:

Veri noktalarından 2 tanesini alın (arayın ave arayın b). Hedef noktamızı arayın x. Mesafeleri zaten biliyoruz axve bx. abPisagor teoremini kullanarak mesafeyi hesaplayabiliriz .

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

Şimdi, bu çizgilerin açılarını hesaplayabilirsiniz:

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

Maalesef sizin için cevabı tamamlamak için tam zamanında gelemiyorum, Ancak, şimdi açıları biliyorsunuz, bunun için iki muhtemel konum hesaplayabilirsiniz x. Ardından, üçüncü c noktasını kullanarak hangi konumun doğru olduğunu hesaplayabilirsiniz.


2

Bu işe yarayabilir. Hemen tekrar python'da bunu xN, yN = nokta koordinatlarının, r1 & r2 = radius değerlerinin bir fonksiyonunun gövdesine koyabilirsiniz.

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 değerleri, bir dairenin üzerindeki iki kavşak noktasının dönüş değerleridir (bir dizide olmalıdır), eğer bu durumların netleşmesine yardımcı olur.

Bunu ilk 2 daire için yapın, ardından ilk ve sonuncu için tekrar yapın. Birinci yinelemeden elde edilen sonuçlardan herhangi biri ikinciden gelen sonuçlarla karşılaştırılırsa (muhtemelen bir tolerans dahilinde), o zaman kesişme noktanız olur. Bu özellikle, sürece sürece noktadan fazlasını eklemeye başladığınızda harika bir çözüm değildir, ancak bir denklem sistemini çözmeden görebildiğim en basit çözümdür.


Kodunuzda 'e' ve 'k' nedir?
ReinierDG

Hatırlamıyorum :-) wwnick'in cevabı, sadece üç çemberiniz varsa uygulamak isteyeceğiniz bir şeyin çizgisinde daha fazladır.
WolfOdrade 16:13

1

Mekansal API'yi postgis'den kullanabilirsiniz (St_Intersection, St_buffer işlevleri). Fmark'ın belirttiği gibi, Postgis'in düzlemsel algoritmalar kullandığını da hatırlamalısınız, fakat küçük alanlar için eşit mesafeli prjeksiyon kullanmak fazla hata getirmez.


PostGIS GEOGRAPHYtürü yerine türünü kullanarak küresel hesaplamalar yapabilir GEOMETRY.
fmark

1

PHP dilinde yapın:

// yükseklik varsayımı = 0
$ earthR = 6371; // km (= 3959 mil)

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

/ *
#halli küre kullanma
#if elipsoid kullanmak bu adım biraz farklıdır
#Convert geodetic Lat / Long ila ECEF xyz
# 1. Enlem / Boylamı radyanlara çevir
# 2. Enlem / Boylamı (radyan) ECEF ye çevir
* /
$ 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))));

/ *
YÜKLEME:
sudo armut yüklemek Math_Vector-0.7.0
sudo armut yüklemek Math_Matrix-0.8.7
* /
// PEAR Dahil Et :: Math_Matrix
// /usr/share/php/Math/Matrix.php
// include_path = ".: / usr / yerel / php / armut /"
necess_once 'Math / Matrix.php';
necess_once 'Matematik / Vektör.php';
necess_once 'Matematik / Vektör3.php';


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

wikipedia'dan #: http://en.wikipedia.org/wiki/Trilateration
#transform, kaynak 1'de daire 1'i almak için
#transform, x ekseninde 2. daireyi elde etmek için

// CALC EX
$ P2minusP1 = Math_VectorOp :: substract ($ P2vector, $ P1vector);
$ l = yeni Math_Vector ($ P2minusP1);
$ P2minusP1_length = $ l-> uzunluk ();
$ norm = yeni Math_Vector3 (dizi ($ P2minusP1_length, $ P2minusP1_length, $ P2minusP1_length));
$ d = $ norm; // kalkayı sakla D
$ ex = Math_VectorOp :: bölme ($ 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 = yeni Math_Vector3 (dizi ($ ex_x, $ ex_y, $ ex_z));

// CALC i
$ P3minusP1 = Math_VectorOp :: substract ($ P3vector, $ P1vector);
$ P3minusP1_x = değişken ($ P3minusP1 -> _ tuple-> getData () [0]);
$ P3minusP1_y = floatval ($ P3minusP1 -> _ tuple-> getData () [1]);
$ P3minusP1_z = değişken ($ P3minusP1 -> _ tuple-> getData () [2]);
$ P3minusP1 = yeni Math_Vector3 (dizi ($ 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 = yeni Math_Vector ($ P3P1iex);
$ P3P1iex_length = $ l-> length ();
$ norm = yeni Math_Vector3 (dizi ($ P3P1iex_length, $ P3P1iex_length, $ P3P1iex_length));
// echo "norm:". $ norm-> toString (). "\ n";
$ ey = Math_VectorOp :: bölme ($ 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 = yeni Math_Vector3 (dizi ($ ey_x, $ ey_y, $ ey_z));

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

// CALC D
// daha önce yap
$ d = değişken ($ d -> _ tuple-> getData () [0]);
// echo "d = $ d \ n";

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

# wikipedia'dan
#plug ve chug yukarıdaki değerleri kullanarak
$ 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);

# burada gösterilen tek dava
$ z = sqrt (pow ($ DistA, 2) - pow ($ x, 2) - pow ($ y, 2));

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

#triPt, ECEF x, y, trilasyon noktasını z olan bir dizidir
$ 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]);


# ECEF'den lat / long değerine geri dön
# dereceye çevir
$ lat = rad2deg (asin ($ triPt_z / $ earthR));
$ lon = rad2deg (atan2 ($ triPt_y, $ triPt_x));

echo $ lat. ','. boylam, $;
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.