Googleでこれを検索すると多くの結果が得られると思いましたが、ほとんどの場合、ここのstackoverflowのように、質問は非常に具体的で、Googleマップ、一部のGIS、または楕円形の現実世界が含まれます。
私の場合、
- lat、long、およびalt値を持つオブジェクトがあります。ここで、latおよびlongは度単位の浮動小数点数であり、altは10進値としての浮動小数点数であり、球の中心からの距離を表します。したがって、分はありません(10°30'は10.5です)。
- オブジェクトは、x、y、zの3つの軸すべてで移動します。ここで、移動は現在の位置を基準にしています。
- 新しい緯度、経度、代替値を計算する必要があります
あまり考えずに、最初は次のように実装しました。
altにzの動きを追加し、仮想球の円周を計算して、単位(メートル)あたりの度数を取得しました。それで私は最初に新しい緯度を計算し、その後ずっと新しい緯度を計算しました。次々と値を計算するのは間違っていることは知っていましたが、私の「オブジェクトワールド」でのすべての計算が同じ方法で行われる限り、全体的な動作は問題ありません。オブジェクトが球全体を回るとき、長い値は変わらない、球の周りを半分回るのはx軸とy軸で異なる(x軸:-180〜180、y-)などのことを考えました。軸-90から90)とそのようなものとそれは働いた。
しかし、その後、赤道でメートルあたりの度数を計算したことや、他の緯度の値を考慮していないことに気付きました。その時点で、私は物事がもっと複雑であることに気づき、ウェブを検索し始めました。しかし、自分のニーズに合うアルゴリズムは見つかりませんでした。これは以前にも何度も行われたことがあると確信しているので、誰かが以前にこのトピックを扱ったことがあり、素晴らしい実装を教えてくれる場合に備えて、ここで質問しています:)。
私が見つけたのはこれです:
- 緯度/経度をメルカトル図法に変換するアルゴリズム
- 2つの緯度/経度の値のペアからの距離を計算するための半正矢関数
- 他の目的のための他の処方者
それは私を助けませんでした。
私が最も助けたのはこれでした:別の緯度/経度ポイントからメートルの距離を持つ緯度と経度を計算します
しかし、私はまだそれを完全には理解していなかったと思います。
- ラジアンのコースは何ですか?(私にはxとyの動きがあることに関して)
- 外挿法は、高度/地球の半径を考慮していませんか?
誰かがこれを手伝ってくれるといいですね!
(補足:私はこれをErlangで実装していますが、それは問題ではありません。どんな種類のアルゴリズムでも役に立ちます)
アップデート
上記の機能と以下の回答の機能を実装しました。テスト時に間違った値を取得しました。これは、実装を間違えたか、間違ったテストデータを計算したことが原因である可能性があります。どれどれ:
実装1:
% My own implementation that only works on the equator so far. Simple calculations as mentioned above.
テスト(赤道上)OK。
実装2:
% @doc calculates new lat+long+alt values for old values + movement vector
% https://stackoverflow.com/questions/5857523/calculate-latitude-and-longitude-having-meters-distance-from-another-latitude-lo
calc_position2(LastCurrLat, LastCurrLong, LastCurrAlt, MoveX, MoveY, MoveZ) ->
% first the new altitude
NewCurrAlt = LastCurrAlt + MoveZ,
% original algorithm: http://williams.best.vwh.net/avform.htm#LL
% lat=asin(sin(lat1)*cos(d)+cos(lat1)*sin(d)*cos(tc))
% dlon=atan2(sin(tc)*sin(d)*cos(lat1),cos(d)-sin(lat1)*sin(lat))
% lon=mod(lon1+dlon +pi,2*pi)-pi
% where:
% lat1, lon1 - start point in radians
% d - distance in radians
% tc - course in radians
% -> for the found implementation to work some value conversions are needed
CourseDeg = calc_course(MoveX, MoveY),
CourseRad = deg_to_rad(CourseDeg), % todo: cleanup: in course the calculated values are rad anyway, converting to deg is just an extra calculation
Distance = calc_distance(MoveX, MoveY),
DistanceDeg = calc_degrees_per_meter_at_equator(NewCurrAlt) * Distance,
DistanceRad = deg_to_rad(DistanceDeg),
Lat1Rad = deg_to_rad(LastCurrLat),
Lon1Rad = deg_to_rad(LastCurrLong),
LatRad = math:asin(math:sin(Lat1Rad) * math:cos(DistanceRad) + math:cos(Lat1Rad) * math:sin(DistanceRad) * math:cos(CourseRad)),
Dlon = math:atan2(math:sin(CourseRad) * math:sin(DistanceRad) * math:cos(Lat1Rad), math:cos(DistanceRad) - math:sin(Lat1Rad) * math:sin(LatRad)),
LonRad = remainder((Lon1Rad + Dlon + math:pi()), (2 * math:pi())) - math:pi(),
NewCurrLat = rad_to_deg(LatRad),
NewCurrLong = rad_to_deg(LonRad),
{NewCurrLat, NewCurrLong, NewCurrAlt}.
% some trigonometry
% returns angle between adjacent and hypotenuse, with MoveX as adjacent and MoveY as opposite
calc_course(MoveX, MoveY) ->
case MoveX > 0 of
true ->
case MoveY > 0 of
true ->
% tan(alpha) = opposite / adjacent
% arc tan to get the alpha
% erlang returns radians -> convert to degrees
Deg = rad_to_deg(math:atan(MoveY / MoveX));
false ->
Temp = 360 - rad_to_deg(math:atan((MoveY * -1) / MoveX)),
case Temp == 360 of
true ->
Deg = 0.0;
false ->
Deg = Temp
end
end;
false ->
% attention! MoveX not > 0 -> can be 0 -> div by zero
case MoveX == 0 of
true ->
case MoveY > 0 of
true ->
Deg = 90.0;
false ->
case MoveY == 0 of
true ->
Deg = 0.0;
false ->
Deg = 270.0
end
end;
false -> % MoveX < 0
case MoveY > 0 of
true ->
Deg = 180 - rad_to_deg(math:atan(MoveY / (MoveX * -1)));
false ->
Deg = 180 + rad_to_deg(math:atan((MoveY * -1) / (MoveX * -1)))
end
end
end,
Deg.
rad_to_deg(X) ->
X * 180 / math:pi().
deg_to_rad(X) ->
X * math:pi() / 180.
% distance = hypetenuse in Pythagorean theorem
calc_distance(MoveX, MoveY) ->
math:sqrt(math:pow(MoveX,2) + math:pow(MoveY,2)).
calc_degrees_per_meter_at_equator(Alt) ->
Circumference = 2 * math:pi() * Alt,
360 / Circumference.
% erlang rem only operates with integers
% https://stackoverflow.com/questions/9297424/stdremainder-in-erlang
remainder(A, B) ->
A_div_B = A / B,
N = round(A_div_B),
case (abs(N - A_div_B) == 0.5) of
true ->
A_div_B_Trunc = trunc(A_div_B),
New_N = case ((abs(A_div_B_Trunc) rem 2) == 0) of
true -> A_div_B_Trunc;
false ->
case (A_div_B >= 0) of
true -> A_div_B_Trunc + 1;
false -> A_div_B_Trunc - 1
end
end,
A - New_N * B;
false ->
A - N * B
end.
テスト:緯度/経度/高度(10°、10°、6371000m)のオブジェクト。動きなし(0m、0m、0m)。期待される:
{1.000000e+001,1.000000e+001,6.371000e+006}
しかし、戻ります:
{1.000000e+001,-3.500000e+002,6.371000e+006}
予想より360度少ない...
(0°、10°、6371000m)にあるオブジェクト、移動している(10m、0m、0m)。期待される:
{0.000000e+000,1.000009e+001,6.371000e+006}
一部の数字が表示されていないかどうかわからない。経度は10.000089932160591のようになります。とにかく-戻り値:
{8.993216e-005,-3.500000e+002,6.371000e+006}
今移動していますが、同じ間違った経度の値ですか?そして、Y軸上を移動しなかったのに、緯度の値が変更されましたか?
同じ位置で、現在5,000,000m東に移動している場合はどうでしょうか。
{0.000000e+000,5.496608e+001,6.371000e+006} % expected
{4.496608e+001,-3.500000e+002,6.371000e+006} % returned
実装3:
calc_position3(LastCurrLat, LastCurrLong, LastCurrAlt, MoveX, MoveY, MoveZ) ->
{CurrX, CurrY, CurrZ} = spherical_to_cartesian(LastCurrLat, LastCurrLong, LastCurrAlt),
NewX = CurrX + MoveX,
NewY = CurrY + MoveY,
NewZ = CurrZ + MoveZ,
{NewCurrLat, NewCurrLong, NewCurrAlt} = cartesian_to_spherical(NewX, NewY, NewZ),
{NewCurrLat, NewCurrLong, NewCurrAlt}.
spherical_to_cartesian(Lat, Lon, Alt) ->
X = Alt * math:cos(Lat) * math:cos(Lon),
Y = Alt * math:cos(Lat) * math:sin(Lon),
Z = Alt * math:sin(Lat),
{X, Y, Z}.
cartesian_to_spherical(X, Y, Z) ->
R = math:sqrt(math:pow(X,2) + math:pow(Y,2)),
Alt = math:sqrt(math:pow(X,2) + math:pow(Y,2) + math:pow(Z,2)),
Lat = math:asin(Z / Alt),
case R > 0 of
true ->
Lon = math:acos(X / R);
false -> % actually: if R == 0, but it can never be negative (see above)
Lon = 0
end,
{Lat, Lon, Alt}.
上記のようなテスト:
(10°、10°、6371000m)の物体、動きなし
{1.000000e+001,1.000000e+001,6.371000e+006} % expected
{-5.752220e-001,5.752220e-001,6.371000e+006} % returned
(0°、10°、6371000m)で、移動(10m、0m、0m)
{0.000000e+000,1.000009e+001,6.371000e+006} % expected
{0.000000e+000,2.566370e+000,6.370992e+006} % returned
(0°、10°、6371000m)で、移動(5000000m、0m、0m)
{0.000000e+000,5.496608e+001,6.371000e+006} % expected
{0.000000e+000,1.670216e+000,3.483159e+006} % returned
だから:私はいくつかのラジアンから度への変換または同様のものを逃しましたか?
PS:構文の強調表示が悪いので申し訳ありませんが、Erlangが利用できないようですので、シェルスクリプトを使用しました。少し読みやすくなります。