Вычисление ограничивающей рамки на определенном расстоянии от координаты широты/долготы в Java

StackOverflow https://stackoverflow.com/questions/1689096

Вопрос

Учитывая координату (широта, долгота), я пытаюсь вычислить квадратную ограничивающую рамку, которая находится на заданном расстоянии (например,50 км) от координаты.Итак, на входе у меня есть широта, длина и расстояние, а на выходе мне нужны две координаты;один — юго-западный (нижний левый) угол, а другой — северо-восточный (верхний правый) угол.Я видел здесь пару ответов, которые пытаются ответить на этот вопрос на Python, но я ищу, в частности, реализацию Java.

Для ясности: я собираюсь использовать алгоритм только на Земле, поэтому мне не нужно учитывать переменный радиус.

Он не должен быть очень точным (+/-20% вполне подойдет) и будет использоваться только для расчета ограничивающих рамок на небольших расстояниях (не более 150 км).Поэтому я рад пожертвовать некоторой точностью ради эффективного алгоритма.Буду признателен за любую оказанную помощь.

Редактировать:Мне следовало бы выразиться яснее: мне действительно нужен квадрат, а не круг.Я понимаю, что расстояние между центром квадрата и различными точками по периметру квадрата не является постоянной величиной, как в случае с кругом.Я предполагаю, что я имею в виду квадрат, в котором, если вы проведете линию от центра к любой из четырех точек периметра, в результате чего получится линия, перпендикулярная стороне периметра, тогда эти 4 линии будут иметь одинаковую длину.

Это было полезно?

Решение

Я написал статью о поиске ограничивающих координат:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

В статье объясняются формулы, а также представлена ​​реализация на Java.(Это также показывает, почему формула IronMan для определения минимальной/максимальной долготы неточна.)

Другие советы

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

Хотя я бы еще рекомендовал JTS.

import com.vividsolutions.jts.geom.Envelope;

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

Теперь env содержит ваш конверт.На самом деле это не «квадрат» (что бы это ни значило на поверхности сферы), но так и должно быть.

Обратите внимание, что расстояние в градусах будет зависеть от широты центральной точки.На экваторе 1 градус широты составляет около 111 км, а в Нью-Йорке — всего около 75 км.

Самое классное, что вы можете сбросить все свои очки в com.vividsolutions.jts.index.strtree.STRtree а затем используйте его для быстрого расчета точек внутри этого конверта.

Все предыдущие ответы верны лишь частично..Особенно в таких регионах, как Австралия, они всегда включают столб и рассчитывают очень большой прямоугольник даже на 10 км.

В частности, алгоритм Яна Филипа Матушека http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex включал очень большой прямоугольник (-37, -90, -180, 180) почти для каждой точки Австралии.Это затрагивает большое количество пользователей в базе данных, и расстояние приходится рассчитывать для всех пользователей почти в половине страны.

Я обнаружил, что Алгоритм Drupal API Earth от Рочестерского технологического института лучше работает вокруг столба, а также в других местах, и его гораздо проще реализовать.

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

Использовать earth_latitude_range и earth_longitude_range из приведенного выше алгоритма расчета ограничивающего прямоугольника

Вот реализация 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;
    }

И используйте формула расчета расстояния, документированная на картах Google рассчитать расстояние

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

Для поиска в километрах вместо миль замените 3959 на 6371.Для (Lat, Lng) = (37, -122) и таблицы маркеров со столбцами lat и lng., формула:

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;

Вот простое решение, которое я использовал для создания координат ограничительной рамки, которые я использую с GeoNames cityJSON API чтобы добраться до близлежащих крупных городов по десятичной координате GPS.

Это метод Java из моего репозитория GitHub: FusionTableModifyJava

У меня было десятичное местоположение по GPS, и мне нужно было найти самый большой город/штат «рядом» с этим местом.Мне нужен был относительно точный ограничивающий прямоугольник, который можно было бы передать веб-сервису cityJSON GeoNames и получить самый большой город в этом ограничительном прямоугольнике.Я передаю местоположение и интересующий меня «радиус» (в км), и он возвращает десятичные координаты севера, юга, востока и запада, необходимые для передачи в городаJSON.

(Эти ресурсы оказались полезными для моего исследования:

Рассчитайте расстояние, направление и многое другое между точками широты и долготы.

Долгота — Википедия)

Он не очень точен, но достаточно точен для того, для чего я его использовал:

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

    }
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top