Calcul de la zone limite à une certaine distance d'une coordonnée lat/long en Java

étant donné une coordonnée (lat, long), j'essaie de calculer une boîte de limite carrée qui est une distance donnée (par exemple 50km) loin de la coordonnée. Donc comme entrée j'ai lat, longue et longue distance et comme Sortie je voudrais deux coordonnées, une étant le coin sud-ouest (en bas à gauche) et l'autre étant le coin nord-est (en haut à droite). J'ai vu quelques réponses ici qui essayent de répondre à cette question en Python, mais je suis à la recherche d'une implémentation Java en particulier.

juste pour être clair, j'ai l'intention d'utiliser l'algorithme sur terre seulement et donc je n'ai pas besoin d'accommoder un rayon variable.

il n'a pas besoin d'être extrêmement précis (+/-20% c'est très bien) et il ne sera utilisé que pour calculer les limites des boîtes sur de petites distances (pas plus de 150km). Je suis donc heureux de sacrifier un peu de précision pour un algorithme efficace. Toute aide est très appréciée.

Edit: j'aurais du être plus clair, je suis vraiment après un carré, pas un cercle. Je comprends que la distance entre le centre d'un carré et divers points le long du périmètre du carré n'est pas une valeur constante comme il est avec un cercle. Je suppose que ce que je veux dire est un carré où si vous dessinez une ligne du centre à l'un des quatre points sur le périmètre qui résulte en une ligne perpendiculaire à un côté du périmètre, alors ces 4 lignes ont la même longueur.

33
demandé sur Bryce Thomas 2009-11-06 20:30:49

7 réponses

j'ai écrit un article sur la recherche des coordonnées limites:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

l'article explique les formules et fournit également une implémentation Java. (Il montre également pourquoi la formule de IronMan pour la longitude min/max est inexacte.)

53
répondu Jan Philip Matuschek 2010-05-26 13:15:31
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);

bien que je recommande aussi JTS.

13
répondu IronMan 2011-07-08 09:34:44
import com.vividsolutions.jts.geom.Envelope;

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

Maintenant env contient votre enveloppe. Ce n'est pas vraiment un "carré" (peu importe ce que cela signifie à la surface d'une sphère), mais ça devrait le faire.

il faut noter que la distance en degrés dépendra de la latitude du point central. A l'Équateur, 1 degré de latitude est d'environ 111 km, mais à New York, c'est seulement environ 75 km.

la chose vraiment cool est que vous pouvez jeter tous vos points dans un com.vividsolutions.jts.index.strtree.STRtree et ensuite l'utiliser pour calculer rapidement les points à l'intérieur de cette Enveloppe.

3
répondu novalis 2009-11-06 19:37:09

j'ai un script PHP qui fait ça. Donné un point de départ, il calcule les coins d'une boîte autour de lui à une distance particulière. Il est spécifiquement pour Google Maps, mais il pourrait fonctionner pour n'importe quoi d'autre:

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

1
répondu Richard 2011-11-19 16:17:00
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);

bien que je recommande aussi JTS.

cela calcule, mais Google Earth n'accepte pas et ne mappe pas le modèle 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);


 }

}

Il imprime

80.30172366789824---|80.30775033210176---|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.30503833321018</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
répondu susheel 2016-11-10 09:07:27

Toutes les réponses précédentes ne sont que partiellement correct. Spécialement dans les régions comme L'Australie, ils comprennent toujours pole et calculent un très grand rectangle même pour 10kms.

spécialement l'algorithme de Jan Philip Matuschek à http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex inclus un très grand rectangle de (-37, -90, -180, 180) pour presque chaque point en Australie. Cela frappe un grand nombre d'utilisateurs dans la base de données et à distance doivent être calculées pour tous les utilisateurs dans près de la moitié du pays.

j'ai trouvé que l' l'API Drupal Terre Algorithme par l'Institut de Technologie de Rochester fonctionne mieux autour de pole ainsi qu'ailleurs et est beaucoup plus facile à mettre en œuvre.

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

Utiliser earth_latitude_range et earth_longitude_range à partir de l'algorithme ci-dessus pour calculer le rectangle de délimitation

Voici L'implémentation est 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;
    }

Et formule de calcul de distance documentée par google maps pour calculer la distance

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

pour chercher par kilomètres au lieu de miles, remplacer 3959 par 6371. pour (Lat, Lng) = (37, -122) et un tableau de repères avec les colonnes lat et le gnl, la formule est la suivante:

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;
1
répondu Sacky San 2017-08-30 02:47:22

Voici une solution simple que j'ai utilisée pour générer des coordonnées de bounding box que j'utilise avec GeoNames citieJSON API pour se rapprocher des grandes villes à partir d'une coordonnée décimale gps.

c'est une méthode Java de mon dépôt GitHub: FusionTableModifyJava

j'avais une position décimale GPS et j'avais besoin de trouver la plus grande ville/état "proche" de cette position. J'avais besoin d'une boîte de délimitation relativement précise pour passer aux noms géographiques de citizensjson. webservice pour récupérer la plus grande ville dans cette boîte bondissante. Je passe l'emplacement et le" rayon " qui m'intéresse (en km) et il me renvoie les coordonnées décimales Nord, Sud, Est, Ouest nécessaires pour passer à citizensjson.

(j'ai trouvé ces ressources utiles en faisant ma recherche:

Calculez la distance, le relèvement et plus entre les points de Latitude/Longitude.

Longitude-Wikipedia)

ce n'est pas super exact, mais assez précis pour que je l'utilise pour:

    // Compute bounding Box coordinates for use with Geonames API.
    class BoundingBox
    {
        public double north, south, east, west;
        public BoundingBox(String location, float km)
        {
             //System.out.println(location + " : "+ km);
            String[] parts = location.replaceAll("\s","").split(","); //remove spaces and split on ,

            double lat = Double.parseDouble(parts[0]);
            double lng = Double.parseDouble(parts[1]);

            double adjust = .008983112; // 1km in degrees at equator.
            //adjust = 0.008983152770714983; // 1km in degrees at equator.

            //System.out.println("deg: "+(1.0/40075.017)*360.0);


            north = lat + ( km * adjust);
            south = lat - ( km * adjust);

            double lngRatio = 1/Math.cos(Math.toRadians(lat)); //ratio for lng size
            //System.out.println("lngRatio: "+lngRatio);

            east = lng + (km * adjust) * lngRatio;
            west = lng - (km * adjust) * lngRatio;
        }

    }
0
répondu Greg Kendall 2018-06-05 18:12:49