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を出力として返す関数の数学です。

私はから、2点間の距離を計算する方法を理解http://www.movable-type.co.uk/scripts/latlong.html私はこれらのような3つの円であなたは重複の正確に一つのポイントを得ることを一般的な原理を理解しています。私がhaんでいるのは、この入力でそのポイントを計算するために必要な数学です。


以下に、3つの座標の中心を見つける数学を説明するページを示します。おそらく何らかの形で役立つかもしれません。< mathforum.org/library/drmath/view/68373.html >
ジョンブリングハースト

1
これは球/回転楕円体上にある必要がありますか、または平面アルゴリズムは大丈夫ですか?
fmark

1
答えを出すことはできませんが、正しい方向に向けることができると思います。3つの座標= 3つの中心点。3つの距離= 3つの円。交差する2つの円は、なし/ 1/2ソリューションの可能性があります。3つのサークルは、そのソリューションとして、なし/ 1 /または領域を持つことができます。3つの円の円公式を取得し、方程式/代数のシステムで解決します。
CrazyEnigma

実際、これを解決するためのシステムさえ必要ありません。1つまたは2つの可能性がありますが、距離の値があるため、正解を分離できます。
ジョージシルバ

1
+1これはいい質問です。最初は、Googleで簡単に解決策を見つけることができると考えましたが、明らかにそうではありませんでした。おそらく問題はより一般的に述べることができます。各点に距離だけでなく誤差もあるN点が与えられた場合、信頼楕円を見つけます。
カーククイケンドール

回答:


34

ウィキペディアStackOverflowで同じ質問/回答を見て回った後、私はそれを突き刺してギャップを埋めようと考えました。

最初に、どこで出力を取得したかはわかりませんが、間違っているようです。ArcMapでポイントをプロットし、指定された距離にバッファリングし、バッファー上で交差させてから、交差の頂点をキャプチャして解を得ました。提案された出力は緑色の点です。コールアウトボックスの値を計算しました。これは、ArcMapが交差点から派生したソリューションに与えた値の約3メートルです。

代替テキスト

ウィキペディアページの数学はそれほど悪くはありません。測地座標をデカルト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
同様の答えをまとめるつもりでしたが、今は必要ありません!私の賛成を得ます。
怒り

numpyが助けて!「triPt」が「triLatPt」に置き換えられるとコンパイルされますが、そうでない場合は37.4191023738 -121.960579208が返されます。良い仕事
-WolfOdrade

よくできました!地理座標系をローカルの[デカルト]座標系に置き換えても、これは機能しますか?
zengr

C言語でそれらのため++一緒に実際に素早く1のdomain..hacked pastebin.com/9Dur6RAP
raajを

2
ありがとう@wwnick!これをJavaScriptに移植しました(Node向けですが、ブラウザで簡単に変換できます)。gist.github.com/dav-/bb7103008cdf9359887f
DC_

6

私が素朴であるかどうかはわかりませんが、サイズごとに各ポイントをバッファリングしてから、正しい場所を取得する3つの円すべてを交差させますか?

空間APIを使用して交差を計算できます。例:

  • GeoScript
  • Javaトポロジスイート
  • NET Topology Suite
  • GEOS

1
まさに、その交点を得るための式に興味があります。
ビンコヴサロビッチ

空間APIを使用すると、純粋な数学を使用せずに実行できます。
ジョージシルバ

1
@GeorgeはそのようなAPIの例を提供できますか?
nohat

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

残念ながら、私はあなたのために答えを完了するのに時間が足りません、しかし、今、あなたは角度を知っています、あなたは2つの可能な場所を計算することができますx。次に、3番目の点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つの交点の戻り値(配列内にある必要があります)です。

これを最初の2つの円で行い、次に最初と最後の円で繰り返します。最初の反復の結果のいずれかが2番目の反復の結果と比較される場合(おそらく許容範囲内)、交点があります。特にプロセスにポイント以上を追加し始めるとき、それは素晴らしい解決策ではありませんが、方程式系を解くことなく見ることができる最も簡単な方法です。


コードの「e」と「k」とは何ですか?
ReinierDG

覚えていません:-) wwnickの答えは、3つの円だけがある場合に実装したいものに沿ったものです。
ウルフオドラデ

1

postgis(St_Intersection、St_buffer関数)から空間APIを使用できます。fmarkが気づいたように、Postgisは平面アルゴリズムを使用することも覚えておく必要がありますが、小さな領域では等距離射影を使用しても大きな誤差は生じません。


PostGISは、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;

/ *
#本物の球体を使用する
#楕円体を使用する場合、この手順は少し異なります
#測地緯度/経度をECEF xyzに変換
#1.緯度/経度をラジアンに変換する
#2.緯度/経度(ラジアン)を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 install Math_Vector-0.7.0
sudo pear install Math_Matrix-0.8.7
* /
// 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));

#ウィキペディアから:http://en.wikipedia.org/wiki/Trilateration
#transformで原点1の円を取得
#transformでx軸上の円2を取得

// CALC EX
$ P2minusP1 = Math_VectorOp :: substract($ P2vector、$ P1vector);
$ l = new Math_Vector($ P2minusP1);
$ P2minusP1_length = $ l-> length();
$ norm = new Math_Vector3(array($ P2minusP1_length、$ P2minusP1_length、$ P2minusP1_length));
$ d = $ norm; // 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 = new 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
//前にやる
$ d = floatval($ d-> _ tuple-> getData()[0]);
// echo "d = $ d \ n";

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

#ウィキペディアから
#上記の値を使用してプラグアンドチャグ
$ 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);

#ここに示されている1つのケースのみ
$ z = sqrt(pow($ DistA、2)-pow($ x、2)-pow($ y、2));

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

#triPtは、三辺測量点のECEF x、y、zを持つ配列です
$ 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から緯度/経度に戻す
#度に変換
$ 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.