Calculating bounding box a certain distance away from a lat/long coordinate in Java

后端 未结 6 2075
半阙折子戏
半阙折子戏 2020-11-28 23:47

Given a coordinate (lat, long), I am trying to calculate a square bounding box that is a given distance (e.g. 50km) away from the coordinate. So as input I have lat, long a

相关标签:
6条回答
  • 2020-11-29 00:16

    Based on IronMan response:

    /**
     * Calculate the lat and len of a square around a point.
     * @return latMin, latMax, lngMin, lngMax
     */
    public static double[] calculateSquareRadius(double lat, double lng, double radius) {
        double R = 6371;  // earth radius in km
        double latMin = lat - Math.toDegrees(radius/R);
        double latMax = lat + Math.toDegrees(radius/R);
        double lngMin = lng - Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
        double lngMax = lng + Math.toDegrees(radius/R/Math.cos(Math.toRadians(lat)));
    
        return new double[] {latMin, latMax, lngMin, lngMax};
    }
    
    0 讨论(0)
  • 2020-11-29 00:17

    I wrote an article about finding the bounding coordinates:

    http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

    The article explains the formulae and also provides a Java implementation. (It also shows why IronMan's formula for the min/max longitude is inaccurate.)

    0 讨论(0)
  • 2020-11-29 00:21
    import com.vividsolutions.jts.geom.Envelope;
    
    ...
    Envelope env = new Envelope(centerPoint.getCoordinate());
    env.expandBy(distance_in_degrees); 
    ...
    

    Now env contains your envelope. It's not actually a "square" (whatever that means on the surface of a sphere), but it should do.

    You should note that the distance in degrees will depend on the latitude of the center point. At the equator, 1 degree of latitude is about 111km, but in New York, it's only about 75km.

    The really cool thing is that you can toss all your points into a com.vividsolutions.jts.index.strtree.STRtree and then use it to quickly calculate points inside that Envelope.

    0 讨论(0)
  • 2020-11-29 00:26

    Here is a simple solution that I used to generate bounding box coordinates that I use with GeoNames citieJSON API to get nearby big cities from a gps decimal coordinate.

    This is a Java method from my GitHub repository: FusionTableModifyJava

    I had a decimal GPS location and I needed to find the biggest city/state "near" that location. I needed a relatively accurate bounding box to pass to the citiesJSON GeoNames webservice to get back the biggest city in that bounding box. I pass the location and the "radius" I am interested in (in km) and it gives back the north, south, east, west decimal coordinates needed to pass to citiesJSON.

    (I found these resources useful in doing my research:

    Calculate distance, bearing and more between Latitude/Longitude points.

    Longitude - Wikipedia)

    It is not super accurate but accurate enough for what I was using it for:

        // 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 讨论(0)
  • 2020-11-29 00:34
    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);
    

    Although I would also recommend JTS.

    0 讨论(0)
  • 2020-11-29 00:39

    All of the previous answers are only partially correct. Specially in region like Australia, they always include pole and calculate a very large rectangle even for 10kms.

    Specially the algorithm by Jan Philip Matuschek at http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex included a very large rectangle from (-37, -90, -180, 180) for almost every point in Australia. This hits a large users in database and distance have to be calculated for all of the users in almost half the country.

    I found that the Drupal API Earth Algorithm by Rochester Institute of Technology works better around pole as well as elsewhere and is much easier to implement.

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

    Use earth_latitude_range and earth_longitude_range from the above algorithm for calculating bounding rectangle

    Here is the implementation is 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;
        }
    

    And use the distance calculation formula documented by google maps to calculate distance

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

    To search by kilometers instead of miles, replace 3959 with 6371. For (Lat, Lng) = (37, -122) and a Markers table with columns lat and lng, the formula is:

    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 讨论(0)
提交回复
热议问题