33

Podając współrzędną (szerokość, długość), próbuję obliczyć prostokątną ramkę ograniczającą o określonej odległości (na przykład 50 km) od współrzędnej. Więc jako dane wejściowe mam lat, long i distance, a jako wyjście chciałbym dwóch współrzędnych; jeden z nich to południowo-zachodni (dolny-lewy) narożnik, a drugi północno-wschodni (górny prawy). Widziałem tutaj kilka odpowiedzi, które próbują rozwiązać to pytanie w Pythonie, ale ja w szczególności szukam implementacji Java.Obliczanie prostokąta ograniczającego w pewnej odległości od współrzędnej długiej/długiej w Javie

Dla jasności zamierzam użyć algorytmu tylko na Ziemi, więc nie muszę przyjmować zmiennego promienia.

To nie musi być bardzo dokładne (+/- 20% jest w porządku) i będzie używane do obliczania obwiedni na małych odległościach (nie więcej niż 150 km). Dlatego cieszę się, że poświęciłem trochę dokładności, by stworzyć skuteczny algorytm. Każda pomoc jest doceniana.

Edytuj: Powinienem być bardziej przejrzysty, naprawdę podążam za kwadratem, a nie kółkiem. Rozumiem, że odległość między środkiem kwadratu a różnymi punktami wzdłuż obwodu kwadratu nie jest wartością stałą, jak to jest z okręgiem. Sądzę, że chodzi mi o kwadrat, w którym jeśli narysujesz linię od środka do jednego z czterech punktów na obwodzie, który powoduje, że linia jest prostopadła do boku obwodu, wówczas te 4 linie mają tę samą długość.

+0

To jest tylko prosta trygonometria; Sugerowałbym usunięcie tagów Java i algorytmów. (Jeśli to nawet możliwe.) –

+0

oh, przepraszam, zrobiłem to teraz. –

+0

Należy rozważyć kulisty charakter ziemi. Jakiej odpowiedzi potrzebujesz, jeśli pozycją wejściową jest biegun północny? – MarkJ

Odpowiedz

51

napisałem artykuł o znalezieniu obwiedni współrzędne:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

Artykuł wyjaśnia formuł, a także dostarcza implementację Javy. (Pokazuje również, dlaczego formuła Ironman jest na min/max długości jest niedokładna.)

+0

Dziękuję za napisanie tego; Szczególnie doceniam szczegóły implementacji SQL. –

+2

Jestem zdezorientowany tym, dlaczego te przybliżenia sferyczne są traktowane jako standardowe rozwiązanie. Istnieje tutaj obrzydliwa ilość funkcji trygonometrycznych. Dodatkowo, wszystkie urządzenia GPS wytwarzają współrzędne długie/długie oparte na elipsoidzie WGS84 - Po prostu nie można użyć przybliżenia sferycznego, jeśli chcesz uzyskać całkowitą dokładność ... Dopasowanie tych równań w przybliżeniu sferycznym do elipsoidy spowoduje niewyobrażalną potworność . Wówczas wydaje się, że należy wtedy wykonać obliczenia i całki długości łuku w przestrzeni 3D. –

+0

@StephanKlein Obwiednia to pole, które z całą pewnością pokrywa wszystkie współrzędne w odległości, ale nie wszystkie współrzędne, które obejmuje, są równe lub mniejsze od odległości. Obwiednia jest jak prostokąt wokół koła.Obejmuje on wszystkie punkty w okręgu, ale przy krawędziach obejmuje również punkty poza okręgiem. Ale tylko z prostokątem (prostym) możesz wykonać proste porównanie '<' and '>' w instrukcji SQL, dlatego pełny wybór SQL pokazany na stronie najpierw sprawdza, czy punkt znajduje się w ramce ograniczającej (szybko!), A następnie sprawdza czy to naprawdę jest równe lub poniżej pożądanej odległości (wolno!) – Mecki

3
import com.vividsolutions.jts.geom.Envelope; 

... 
Envelope env = new Envelope(centerPoint.getCoordinate()); 
env.expandBy(distance_in_degrees); 
... 

Teraz env zawiera kopertę. W rzeczywistości nie jest to "kwadrat" (cokolwiek to znaczy na powierzchni kuli), ale powinien.

Należy pamiętać, że odległość w stopniach zależy od szerokości punktu środkowego. Na równiku 1 stopień szerokości to około 111 km, ale w Nowym Jorku to tylko około 75 km.

Naprawdę fajną rzeczą jest to, że można wrzucić wszystkie swoje punkty w numer com.vividsolutions.jts.index.strtree.STRtree, a następnie użyć go do szybkiego obliczenia punktów wewnątrz tej koperty.

12
double R = 6371; // earth radius in km 

double radius = 50; // km 

double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 

double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 

double y1 = lat + Math.toDegrees(radius/R); 

double y2 = lat - Math.toDegrees(radius/R); 

Mimo że chciałbym również polecić WST.

+7

Co oznacza skrót "JTS"? – Gili

+0

Java Topology Suite, jest to API do operacji związanych z geometrią. – prettyvoid

1
double R = 6371; // earth radius in km 
double radius = 50; // km 
double x1 = lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 
double x2 = lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat))); 
double y1 = lat + Math.toDegrees(radius/R); 
double y2 = lat - Math.toDegrees(radius/R); 

Chociaż polecam również WST.

To oblicza, ale Google Earth nie akceptuje i nie mapuje modelu 3D.

/* 
* To change this template, choose Tools | Templates 
* and open the template in the editor. 
*/ 

package assetmap; 




public class Main { 

public double degrees; 
public double pi= 3.1416; 
public static double lon=80.304737; 
public static double lat=26.447521; 
public static double x1,x2,y1,y2; 


public static void main(String[] args) { 

double R = 6371; // earth radius in km 26.447521 

double radius = 0.300; // km 

x1 = (lon - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)))); 

x2 = (lon + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)))); 

y1 = (lat + Math.toDegrees(radius/R)); 

y2 = (lat - Math.toDegrees(radius/R)); 


System.out.println(x1+"---|"+x2+"---|"+y1+"|---|"+y2); 


} 

} 

Drukuje

80.30172366789824---|80.176---|26.450218964817754|---|26.444823035182242 

KML:

<?xml version="1.0" encoding="UTF-8"?> 
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom"> 
<Placemark> 
    <name>United Nations Headquarters</name> 
    <Region> 
     <LatLonAltBox> 
      <north>26.447251203518224</north> 
      <south>26.447790796481772</south> 
      <east>80.18</east> 
      <west>80.30443566678983</west> 
      <minAltitude>0</minAltitude> 
      <maxAltitude>30</maxAltitude> 
      <altitudeMode>absolute</altitudeMode> 
     </LatLonAltBox> 
     <Lod> 
      <minLodPixels>128</minLodPixels> 
      <maxLodPixels>-1</maxLodPixels> 
      <minFadeExtent>0</minFadeExtent> 
      <maxFadeExtent>0</maxFadeExtent> 
     </Lod> 
    </Region> 
    <Model id="model_1"> 
     <altitudeMode>absolute</altitudeMode> 
     <Location> 
      <longitude>80.304737</longitude> 
      <latitude>26.447521</latitude> 
      <altitude>0.406173708576</altitude> 
     </Location> 
     <Orientation> 
      <heading>0</heading> 
      <tilt>0</tilt> 
      <roll>0</roll> 
     </Orientation> 
     <Scale> 
      <x>10</x> 
      <y>10</y> 
      <z>10</z> 
     </Scale> 
     <Link> 
      <href>un.dae</href> 
     </Link> 
     <ResourceMap> 
      <Alias> 
       <targetHref>_01.jpg</targetHref> 
       <sourceHref>../images/_01.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_02.jpg</targetHref> 
       <sourceHref>../images/_02.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_04.jpg</targetHref> 
       <sourceHref>../images/_04.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_05.jpg</targetHref> 
       <sourceHref>../images/_05.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_06.jpg</targetHref> 
       <sourceHref>../images/_06.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_07.jpg</targetHref> 
       <sourceHref>../images/_07.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_08.jpg</targetHref> 
       <sourceHref>../images/_08.jpg</sourceHref> 
      </Alias> 
      <Alias> 
       <targetHref>_09.jpg</targetHref> 
       <sourceHref>../images/_09.jpg</sourceHref> 
      </Alias> 
     </ResourceMap> 
    </Model> 
</Placemark> 
</kml> 
1

Mam skrypt PHP i przykład, który to robi. Biorąc pod uwagę punkt początkowy, oblicza rogi skrzynki wokół niej na określoną odległość.To specjalnie dla Google Maps, ale to może działać na coś innego:

http://www.richardpeacock.com/blog/2011/11/draw-box-around-coordinate-google-maps-based-miles-or-kilometers

+0

Wiem, że jest to niewiarygodnie stara odpowiedź, ale pomogło to wiele, dziękuję bardzo! – acupajoe

+1

Cieszę się, że pomogło komuś! – Richard

1

Wszystkie poprzednie odpowiedzi są tylko częściowo poprawne. Szczególnie w regionie takim jak Australia, zawsze zawierają biegun i obliczyć bardzo duży prostokąt nawet na 10kms.

Specjalnie algorytm Jana Philipa Matuscheka pod numerem http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex zawiera bardzo duży prostokąt od (-37, -90, -180, 180) dla niemal każdego punktu w Australii. To trafia do dużych użytkowników w bazie danych, a odległość musi być obliczona dla wszystkich użytkowników w prawie połowie kraju.

okazało się, że Drupal API Ziemia Algorytm przez Rochester Institute of Technology działa lepiej wokół bieguna, a także gdzie indziej i jest znacznie łatwiejsze do wdrożenia.

https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54

Zastosowanie earth_latitude_range i earth_longitude_range z powyższego algorytmu do obliczania obwiedni prostokąt

Oto implementacja jest Java

/** 
* Get bouding rectangle using Drupal Earth Algorithm 
* @see https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54 
* @param lat 
* @param lng 
* @param distance 
* @return 
*/ 
default BoundingRectangle getBoundingRectangleDrupalEarthAlgo(double lat, double lng, int distance) { 
    lng = Math.toRadians(lng); 
    lat = Math.toRadians(lat); 
    double radius = earth_radius(lat); 
    List<Double> retLats = earth_latitude_range(lat, radius, distance); 
    List<Double> retLngs = earth_longitude_range(lat, lng, radius, distance); 
    return new BoundingRectangle(retLats.get(0), retLats.get(1), retLngs.get(0), retLngs.get(1)); 
} 


/** 
* Calculate latitude range based on earths radius at a given point 
* @param latitude 
* @param longitude 
* @param distance 
* @return 
*/ 
default List<Double> earth_latitude_range(double lat, double radius, double distance) { 
     // Estimate the min and max latitudes within distance of a given location. 

     double angle = distance/radius; 
     double minlat = lat - angle; 
     double maxlat = lat + angle; 
     double rightangle = Math.PI/2; 
     // Wrapped around the south pole. 
     if (minlat < -rightangle) { 
     double overshoot = -minlat - rightangle; 
     minlat = -rightangle + overshoot; 
     if (minlat > maxlat) { 
      maxlat = minlat; 
     } 
     minlat = -rightangle; 
     } 
     // Wrapped around the north pole. 
     if (maxlat > rightangle) { 
     double overshoot = maxlat - rightangle; 
     maxlat = rightangle - overshoot; 
     if (maxlat < minlat) { 
      minlat = maxlat; 
     } 
     maxlat = rightangle; 
     } 
     List<Double> ret = new ArrayList<>(); 
     ret.add((minlat)); 
     ret.add((maxlat)); 
     return ret; 
    } 

/** 
* Calculate longitude range based on earths radius at a given point 
* @param lat 
* @param lng 
* @param earth_radius 
* @param distance 
* @return 
*/ 
default List<Double> earth_longitude_range(double lat, double lng, double earth_radius, int distance) { 
     // Estimate the min and max longitudes within distance of a given location. 
     double radius = earth_radius * Math.cos(lat); 

     double angle; 
     if (radius > 0) { 
     angle = Math.abs(distance/radius); 
     angle = Math.min(angle, Math.PI); 
     } 
     else { 
     angle = Math.PI; 
     } 
     double minlong = lng - angle; 
     double maxlong = lng + angle; 
     if (minlong < -Math.PI) { 
     minlong = minlong + Math.PI * 2; 
     } 
     if (maxlong > Math.PI) { 
     maxlong = maxlong - Math.PI * 2; 
     } 

     List<Double> ret = new ArrayList<>(); 
     ret.add((minlong)); 
     ret.add((maxlong)); 
     return ret; 
    } 

/** 
* Calculate earth radius at given latitude 
* @param latitude 
* @return 
*/ 
default Double earth_radius(double latitude) { 
     // Estimate the Earth's radius at a given latitude. 
     // Default to an approximate average radius for the United States. 
     double lat = Math.toRadians(latitude); 

     double x = Math.cos(lat)/6378137.0; 
     double y = Math.sin(lat)/(6378137.0 * (1 - (1/298.257223563))); 

     //Make sure earth's radius is in km , not meters 
     return (1/(Math.sqrt(x * x + y * y)))/1000; 
    } 

i użyć formuły obliczania odległości udokumentowany przez google maps obliczyć odległość

https://developers.google.com/maps/solutions/store-locator/clothing-store-locator#outputting-data-as-xml-using-php

Aby wyszukać według kilometrów zamiast mil zastąpić 3959 z 6371. Dla Lat (LNG) = (37, -122) i tabeli markerów z kolumnami łac i LNG, formuła jest:

SELECT id, (3959 * acos(cos(radians(37)) * cos(radians(lat)) * cos(radians(lng) - radians(-122)) + sin(radians(37)) * sin(radians(lat)))) AS distance FROM markers HAVING distance < 25 ORDER BY distance LIMIT 0 , 20; 
+0

Przestrzegaj kodu earth_radius. Upewnij się, że promień Ziemi jest w km, a nie w metrach: powrót (1/(Math.sqrt (x * x + y * y)))/1000; –

Powiązane problemy