Calcolo del riquadro di delimitazione a una certa distanza da una coordinata lat / long in Java

Data una coordinata (lat, long), sto provando a calcolare un riquadro di delimitazione quadrato che è una distanza data (ad esempio 50km) lontano dalla coordinata. Quindi come input ho lat, long e distance e come output vorrei due coordinate; uno è l’angolo sud-ovest (in basso a sinistra) e uno è l’angolo nord-est (in alto a destra). Ho visto un paio di risposte qui che cercano di rispondere a questa domanda in Python, ma sto cercando un’implementazione Java in particolare.

Per essere chiari, ho intenzione di utilizzare l’algoritmo solo sulla Terra e quindi non ho bisogno di ospitare un raggio variabile.

Non deve essere eccessivamente accurato (+/- 20% va bene) e verrà utilizzato solo per calcolare le caselle di delimitazione su piccole distanze (non più di 150 km). Quindi sono felice di sacrificare un po ‘di precisione per un algoritmo efficiente. Ogni aiuto è molto apprezzato.

Edit: Avrei dovuto essere più chiaro, sono davvero dopo un quadrato, non un cerchio. Capisco che la distanza tra il centro di un quadrato e vari punti lungo il perimetro del quadrato non è un valore costante come è con un cerchio. Immagino che cosa intendo sia un quadrato dove se si disegna una linea dal centro a uno qualsiasi dei quattro punti sul perimetro che risulta in una linea perpendicolare a un lato del perimetro, allora quelle 4 linee hanno la stessa lunghezza.

Ho scritto un articolo su come trovare le coordinate di delimitazione:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

L’articolo spiega le formule e fornisce anche un’implementazione Java. (Mostra anche perché la formula di IronMan per la longitudine minima / massima non è accurata.)

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); 

Anche se consiglierei anche JTS.

 import com.vividsolutions.jts.geom.Envelope; ... Envelope env = new Envelope(centerPoint.getCoordinate()); env.expandBy(distance_in_degrees); ... 

Adesso env contiene la tua busta. In realtà non è un “quadrato” (qualunque cosa significhi sulla superficie di una sfera), ma dovrebbe farlo.

Dovresti notare che la distanza in gradi dipenderà dalla latitudine del punto centrale. All’equatore, 1 grado di latitudine è di circa 111 km, ma a New York, è solo circa 75 km.

La cosa veramente interessante è che puoi lanciare tutti i tuoi punti in un com.vividsolutions.jts.index.strtree.STRtree e poi usarlo per calcolare rapidamente i punti all’interno di quella Busta.

Ho uno script PHP e un esempio che fa questo. Dato un punto di partenza, calcola gli angoli di una casella attorno ad una distanza particolare. È specifico per Google Maps, ma potrebbe funzionare per qualsiasi altra cosa:

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

 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); 

Anche se consiglierei anche JTS.

Questo calcola, ma Google Earth non accetta e non esegue il mapping del modello 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); } } 

Stampa

 80.30172366789824---|80.30775033210176---|26.450218964817754|---|26.444823035182242 

KML:

    United Nations Headquarters   26.447251203518224 26.447790796481772 80.30503833321018 80.30443566678983 0 30 absolute   128 -1 0 0    absolute  80.304737 26.447521 0.406173708576   0 0 0   10 10 10   un.dae    _01.jpg ../images/_01.jpg   _02.jpg ../images/_02.jpg   _04.jpg ../images/_04.jpg   _05.jpg ../images/_05.jpg   _06.jpg ../images/_06.jpg   _07.jpg ../images/_07.jpg   _08.jpg ../images/_08.jpg   _09.jpg ../images/_09.jpg      

Tutte le risposte precedenti sono solo parzialmente corrette . Specialmente in regioni come l’Australia, includono sempre la pole e calcolare un rettangolo molto grande anche per 10 km.

In particolare l’algoritmo di Jan Philip Matuschek su http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex includeva un rettangolo molto grande (-37, -90, -180, 180) per quasi tutti i punti in Australia. Ciò colpisce grandi utenti nel database e la distanza deve essere calcasting per tutti gli utenti in quasi la metà del paese.

Ho scoperto che Drupal API Earth Algorithm di Rochester Institute of Technology funziona meglio sia su Pole che altrove ed è molto più facile da implementare.

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

Usa earth_latitude_range e earth_latitude_range dall’algoritmo sopra per il calcolo del rettangolo di delimitazione

Ecco l’implementazione è 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 retLats = earth_latitude_range(lat, radius, distance); List 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 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 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 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 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; } 

E utilizzare la formula di calcolo della distanza documentata da Google Maps per calcolare la distanza

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

Per cercare per chilometri invece di miglia, sostituire 3959 con 6371. Per (Lat, Lng) = (37, -122) e una tabella Marcatori con colonne lat e lng , la formula è:

 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; 

Ecco una semplice soluzione che ho usato per generare coordinate di bounding box che uso con GeoNames citieJSON API per ottenere grandi città vicine da una coordinata decimale gps.

Questo è un metodo Java dal mio repository GitHub: FusionTableModifyJava

Avevo una posizione GPS decimale e avevo bisogno di trovare la città / stato più grande “vicino” a quella posizione. Avevo bisogno di un riquadro di delimitazione relativamente accurato per passare al servizio web delle città GeoNames di JSON per recuperare la città più grande di quel riquadro di delimitazione. Passo il luogo e il “raggio” a cui sono interessato (in km) e restituisce le coordinate decimali nord, sud, est, ovest necessarie per passare alle cittàJSON.

(Ho trovato queste risorse utili nel fare la mia ricerca:

Calcola distanza, rilevamento e altro tra i punti Latitudine / Longitudine.

Longitudine – Wikipedia )

Non è molto preciso ma abbastanza preciso per quello che stavo usando per:

  // 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; } }