Obliczanie prawidłowej długości geograficznej, gdy jest ponad | 180 |?

11

Usiłuję opracować „formułę” w celu poprawienia wartości w całym języku.

Korzystam z ulotki vue, ale kiedy patrzysz poza „pierwszym” światem, dostajesz duże liczby. Ponad +180 lub poniżej -180.

Na przykład: kiedy przesuję się do Ameryki w prawo (kierunek wschodni), otrzymam wartość 215. W mojej głowie po prostu poprawiłbym to 215-360=-145

To samo dotyczy przesuwania się na wschodnią Rosję w lewo (kierunek zachodni) i otrzymuję na przykład -222. Teraz muszę obliczyć-222+360=138

Ponieważ jednak świat jest nieokreślony, użytkownik może przesuwać się do ósmego świata i musiałem dostosować wartości.

Czy można obliczyć odpowiednią długość geograficzną? (a kolejnym wymaganiem jest, gdy użytkownik jest w pierwszym świecie, 24 lng powinno nadal wynosić 24 lng.

Shadrix
źródło

Odpowiedzi:

16

Musisz wielokrotnie dodawać (lub odejmować) 360 do swojej wartości, aż znajdzie się w zakresie -180 - 180. Zwykle więc para pętli takich jak:

lon = -187;
while(lon < -180){
  lon +=360;
}
while (lon > 180){
  lon -= 360;
}
Ian Turton
źródło
znaki na odwrót? Powinno być lon + = 360 w pierwszym przypadku.
JimT,
4
możesz to zrobić za pomocą tylko jednej pętli, while (Math.abs(lon) > 180) { lon -= Math.sign(lon) * 360 }ale nie udzielam jej jako odpowiedzi, ponieważ twoja wersja faktycznie pasuje do wyjaśnienia, podczas gdy moja wersja jest tylko optymalizacją, która prawdopodobnie nie robi żadnej różnicy. Zachowuję to jako komentarz tylko jako przypomnienie, że rzeczy można robić na wiele sposobów, niektóre bardziej zoptymalizowane niż inne.
Andrei,
2
Nie sądzę, żebym kiedykolwiek użył tego, ponieważ używa 2 wywołań funkcji na pętlę i tylko jedna z moich pętli by się kiedykolwiek wykonała. Prawdopodobnie nie robi to różnicy w tym przykładzie, ale to moje uprzedzenie
Ian Turton
chociaż wyglądają jak funkcje, funkcje matematyczne w JavaScript powinny być bardziej podobne do operatorów z pełnymi symbolami. W tym sensie możemy również zobaczyć + - a nawet <jako funkcje. Edycja: Miałem tutaj rozwiązanie, które tak naprawdę nie zadziałało
Andrei
2
Nie można zrobić lon %= 180?
Pozew Fund Moniki w sprawie
15

Odpowiedź, która pozwala uniknąć poleceń warunkowych i wywołań funkcji:

longitude = (longitude % 360 + 540) % 360 - 180

Napisałem szybki znak microbench na https://jsperf.com/longitude-normalization, a kod warunkowy wydaje się być szybszy (w Chrome na moim komputerze) dla „rozsądnych” zakresów wartości wejściowych. Ogólnie rzecz biorąc, prawdopodobnie nie powinieneś martwić się z góry wydajnością w takich małych obliczeniach, zwiększając czytelność i spójność z resztą bazy kodu.

Prawdopodobnie ważniejsze w tym przypadku jest pytanie, czy Twój kod może kiedykolwiek spotkać skrajne wartości wejściowe (1e10, Infinity itp.). Jeśli tak, implementacja zapętlenia może kończyć się bardzo powolnym lub cichym zawieszeniem programu. Może się to zdarzyć w obliczeniach przeprowadzonych w pobliżu biegunów, np. Próba przesuwania wschodu lub zachodu na pewną odległość (a nie kąt) od bieguna może łatwo doprowadzić do nieskończonej długości geograficznej.

Joe Lee-Moyet
źródło
1
Ciekawy. Ścigajmy się w warunkowym jmp z podziałem FP. Hmmm zastanawiam się.
Joshua,
1
@Joshua Nie możesz użyć skoku warunkowego. Musisz użyć wielu skoków warunkowych , czyli pętli. (Plus pętla zawiera dodatkowy zmiennoprzecinkowy, co nie jest wolne.) Ile iteracji potrzebuje pętla, zależy od danych wejściowych. Musisz więc wiedzieć coś o danych, aby spojrzeć na wydajność. Jeśli zdecydowana większość znajduje się w pobliżu żądanego zakresu i wymaga kilku iteracji, z pewnością pętla dodawania może być szybsza, ale nie jest tak oczywista, jak sugeruje sarkazm.
jpmc26,
1
@ jpmc26: W tym przypadku oczekiwanie na obejście pętli więcej niż raz jest głupie.
Joshua
1
Nie było sarkazmu. Właściwie nie wiem, w którą stronę by to spadło.
Joshua
1
@Joshua tak, ja też nie byłem pewien :). Dodałem więcej do odpowiedzi na temat wydajności (i potencjalnej awarii kodu pętli)
Joe Lee-Moyet
5

Jednowarstwowy:

normalized = remainder(longitude, 360);

Objaśnienie: Chcesz wiedzieć, co pozostanie po zignorowaniu pełnych obrotów (360 °).

Ten proces nazywa się normalizacją.

Przykład (cpp.sh)

Na podstawie
źródło
1
Czy nie dałoby to wartości [0, 360), a nie [-180, 180], jak zażądała Shadrix?
Facet z kapeluszem
@TheGuywithTheHat Sprawdź ten przykład: cpp.sh/7uy2v
Na podstawie
Ach, nie wiedziałem, że to C ++. W kontekście JavaScriptu Shadrix interpretowałem remainderjako modulus. Moduł w JS dałby [0, 360).
Facet z kapeluszem
1
Nie sądzę, żeby to działało. Musisz odjąć 360 iff wynik> 180. Innym problemem, który właśnie zrozumiałem z JavaScriptem jest to, że modulo jest symetryczny w poprzek 0, np. -1 % 3Wynosi -1, a nie 2, co byłoby konieczne, aby działał tutaj. remainderjest świetnym rozwiązaniem C ++, ale niestety w JS nie ma funkcji / operatora, który byłby na tyle podobny, aby był użyteczny.
Facet z kapeluszem
0

Inna opcja: longitude = atan2 (cos (long), sin (long))

Andres
źródło
1
To nie wydaje się dobrym pomysłem. Jest to bardzo trudne do zrozumienia, kosztowne obliczeniowo i potencjalnie narażone na błędy zaokrąglania.
David Richerby,
0

Jeśli używany język programowania obsługuje operator% (mod) na liczbach zmiennoprzecinkowych (takich jak Python i Ruby), zalecam użycie tego. W przeciwnym razie niektóre inne języki (np. C i C ++) pozwalają na użycie fmod ().

(Niezależnie od tego, którego operatora modowego używasz, upewnij się z wyprzedzeniem, że wykona on operacje modowe na liczbach zmiennoprzecinkowych i że zawsze da ci odpowiedzi nieujemne. W przeciwnym razie otrzymasz paskudną niespodziankę, gdy wielu punkty lat / lon są nieprawidłowe.)

Użyj tego w ten sposób:

# Put the longitude in the range of [0,360):
longitude %= 360

# Put the longitude in the range of [-180,180):
if longitude >= 180:
    longitude -= 360

Jeśli wolisz to wszystko zrobić w jednym wierszu:

# Put the longitude in the range of [-180,180):
longitude = (longitude + 180) % 360 - 180

Te podejścia nie mają pętli, więc znormalizują wartości długości geograficznej bez konieczności wielokrotnego dodawania lub odejmowania, bez względu na to, ile razy Twoja obserwacja krążyła wokół Ziemi.

Edytować:

Hmmm ... Właśnie zauważyłem, że JavaScript nie radzi sobie %z wartościami ujemnymi, tak jak myślałem.

W takim przypadku wypróbuj ten jedno-liniowy:

longitude = (longitude + 36180) % 360 - 180

36180Dodajemy to 36000 + 180. 36.000 jest przeniesienie wartości ujemnej do dodatniej domenie, a 180 jest przesunięcie go tak, że gdy jest modded przez 360, to będzie w przedziale [0360) . Na - 180część przesuwa go z powrotem do zakresu [-180,180).

Oto kolejna linijka, która nie polega na wystarczającej wielkości 36 000:

longitude = (longitude % 360 + 360 + 180) % 360 - 180

longitude % 360 + 360Część zapewni pobyty w wartości dodatniej domenie kiedy to później przez modded 360. W + 180części przesunięcia go na tak, że gdy później dostaje 180 odejmowana od niej (z - 180), to będzie w pożądanym przedziale [-180,180).

J L
źródło
1
Uwaga: C, C ++ fmod(longitude, 360)-> (-360,0 ... +360,0) i ilongitude % 360-> [-359 ... +359].
chux - Przywróć Monikę
@chux - nie wiedziałem o tym, więc właśnie to przetestowałem i wydaje się, że masz rację. Dziękuję za zwrócenie na to uwagi.
JL