Trilateration ใช้ 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 >
Jon Bringhurst

1
สิ่งนี้จำเป็นต้องอยู่บนทรงกลม / ทรงกลมหรืออัลกอริทึมระนาบไม่เป็นไร?
fmark

1
ฉันไม่สามารถให้คำตอบคุณได้ แต่ฉันคิดว่าฉันสามารถชี้คุณไปในทิศทางที่ถูกต้องได้ สามพิกัด = สามจุดศูนย์กลาง สามระยะทาง = สามวงกลม วงกลมสองวงที่ตัดกันสามารถมีความเป็นไปได้ของการแก้ปัญหาที่ไม่มี / หนึ่ง / สอง สามวงกลมสามารถไม่มีใคร / หนึ่ง / หรือพื้นที่เป็นวิธีการแก้ปัญหาของมัน การรับสูตรวงกลมสำหรับสามวงกลมและแก้ปัญหาด้วยระบบสมการ / พีชคณิต
CrazyEnigma

จริงๆแล้วคุณไม่จำเป็นต้องมีระบบเพื่อแก้ปัญหานี้ มีความเป็นไปได้หนึ่งหรือสองอย่าง แต่เนื่องจากคุณมีค่าระยะทางคุณสามารถแยกคำตอบที่ถูกต้องได้
George Silva

1
+1 นี่เป็นคำถามที่ดี ตอนแรกฉันคิดว่าวิธีแก้ปัญหาสามารถพบได้ง่ายด้วย google แต่ดูเหมือนจะไม่ บางทีปัญหาอาจมีการระบุโดยทั่วไปมากขึ้น: เมื่อได้รับคะแนน N กับแต่ละจุดที่ไม่เพียงระยะทาง แต่ยังมีขอบของข้อผิดพลาดให้หาวงรีความเชื่อมั่น
Kirk Kuykendall

คำตอบ:


34

หลังจากที่บางคนมองไปที่Wikipediaและคำถาม / คำตอบเดียวกันกับStackOverflowฉันคิดว่าฉันจะแทงมันและพยายามเติมเต็มช่องว่าง

ก่อนอื่นไม่แน่ใจว่าคุณได้รับผลลัพธ์ที่ใด แต่ดูเหมือนจะผิด ฉันพล็อตจุดใน ArcMap, บัฟเฟอร์พวกมันไปตามระยะทางที่กำหนดวิ่งตัดไปที่บัฟเฟอร์แล้วจับจุดสุดยอดของจุดตัดเพื่อหาคำตอบ ผลลัพธ์ที่คุณเสนอคือจุดสีเขียว ฉันคำนวณค่าในกล่องคำบรรยายภาพซึ่งประมาณ 3 เมตรของสิ่งที่ ArcMap มอบให้สำหรับวิธีการแก้ปัญหาที่ได้มาจากจุดตัด

ข้อความแสดงแทน

คณิตศาสตร์ในหน้าวิกิพีเดียที่ไม่เลวร้ายเกินไปเพียงแค่ต้องแอบแฝงพิกัด Geodetic ของคุณไปยัง ECEF คาร์ทีเซียนซึ่งสามารถพบได้ที่นี่ เงื่อนไข a / x + h สามารถถูกแทนที่ด้วยรัศมีทรงกลมอัตโนมัติหากคุณไม่ได้ใช้ทรงรี

น่าจะง่ายที่สุดเพียงให้โค้ดที่ดี (?) แก่คุณดังนั้นนี่คือไพ ธ อน

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
ฉันกำลังจะรวบรวมคำตอบที่คล้ายกัน แต่ตอนนี้ไม่จำเป็นต้องมี! รับ upvote ของฉัน
Wrass

ต้องการช่วยเหลือ! มันรวบรวมเมื่อ 'triPt' ถูกแทนที่ด้วย 'triLatPt' แต่กลับมาเป็นอย่างอื่น 37.4191023738 -121.960579208 เป็นงานที่ดี
WolfOdrade

การทำงานที่ดี! หากฉันเปลี่ยนระบบพิกัดทางภูมิศาสตร์เป็นระบบพิกัดในท้องถิ่น [คาร์ทีเซียน] สิ่งนี้จะยังใช้งานได้หรือไม่
zengr

สำหรับผู้ที่อยู่ในค ++ 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
  • GEOS

1
แน่นอนเขาสนใจสูตรที่จะได้จุดตัดกันนั้น
Vinko Vrsalovic

การใช้ปริภูมิ API คุณสามารถทำได้โดยไม่ต้องใช้คณิตศาสตร์ที่บริสุทธิ์
George Silva

1
@George คุณสามารถยกตัวอย่างของ API ได้หรือไม่
nohat

โพสต์ที่แก้ไขแล้วเพื่อสะท้อนการร้องขอของ nohat
George Silva

+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

สิ่งนี้อาจใช้ได้ อีกครั้งอย่างรวดเร็วในไพ ธ อนคุณสามารถใส่มันลงในเนื้อหาของฟังก์ชัน 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

คุณอาจใช้ spatial API จาก postgis (ฟังก์ชัน St_Intersection, St_buffer) ตามที่สังเกตเห็นได้จาก fmark คุณต้องจำไว้ว่า Postgis ใช้อัลกอริธึมระนาบ แต่สำหรับพื้นที่เล็ก ๆ การใช้การปฏิเสธแบบ Equi-distant นั้นไม่ได้แนะนำข้อผิดพลาดมากนัก


PostGIS สามารถทำการคำนวณ spheroidal โดยใช้GEOGRAPHYชนิดแทนที่จะเป็นGEOMETRYชนิด
fmark

1

ทำในภาษา PHP:

// สมมติว่าระดับความสูง = 0
$ earthR = 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;

/ *
#using ทรงกลม authalic
# หากใช้รูปวงรีขั้นตอนนี้จะแตกต่างกันเล็กน้อย
# แปลง geodetic 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 pear ติดตั้ง Math_Vector-0.7.0
sudo pear ติดตั้ง Math_Matrix-0.8.7
* /
// รวมลูกแพร์ :: 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 = ใหม่ Math_Vector3 (อาร์เรย์ ($ xA, $ yA, $ zA));
$ P2vector = ใหม่ Math_Vector3 (อาร์เรย์ ($ xB, $ yB, $ zB));
$ P3vector = ใหม่ Math_Vector3 (อาร์เรย์ ($ xC, $ yC, $ zC));

#from wikipedia: http://en.wikipedia.org/wiki/Trilateration
#transform เพื่อรับวงกลม 1 ที่จุดเริ่มต้น
#transform เพื่อรับวงกลม 2 บนแกน x

// CALC EX
$ P2minusP1 = Math_VectorOp :: substract ($ 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 :: 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 = ใหม่ Math_Vector3 (อาร์เรย์ ($ 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 = Math_Vector3 ใหม่ (อาร์เรย์ ($ 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 = ใหม่ Math_Vector ($ P3P1iex);
$ P3P1iex_length = $ l-> length ();
$ norm = Math_Vector3 ใหม่ (อาร์เรย์ ($ P3P1iex_length, $ P3P1iex_length, $ P3P1iex_length));
// echo "norm:". $ 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]);
// echo "d = $ d \ n";

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

#from วิกิพีเดีย
#plug และ 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 / $ ญ) * $ x);

แสดงเพียงหนึ่งกรณีเท่านั้นที่นี่
$ z = sqrt (pow ($ DistA, 2) - pow ($ x, 2) - pow ($ y, 2))

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

#triPt เป็นอาร์เรย์ที่มี ECEF x, y, z ของจุด trilateration
$ 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 :: เพิ่ม ($ P1vector, $ xex);
$ triPt = Math_VectorOp :: เพิ่ม ($ triPt, $ yey);
$ triPt = Math_VectorOp :: เพิ่ม ($ 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 กลับเป็น lat / long จาก ECEF
#convert เป็นองศา
$ lat = rad2deg (asin ($ triPt_z / $ earthR));
$ lon = rad2deg (atan2 ($ triPt_y, $ triPt_x));

echo $ lat. ',' $ lon;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.