Как рассчитать ограничивающий прямоугольник для данного местоположения широты / долготы? - PullRequest
96 голосов
/ 26 октября 2008

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

Ограничительная рамка должна быть определена как latmin, lngmin и latmax, lngmax.

Мне нужен этот материал, чтобы использовать Panoramio API .

Кто-нибудь знает формулу, как получить эти очки?

Редактировать: Ребята, я ищу формулу / функцию, которая принимает lat & lng в качестве входных данных и возвращает ограничивающий прямоугольник как latmin & lngmin и latmax & latmin. Mysql, php, c #, javascript - это нормально, но с псевдокодом все должно быть в порядке.

Редактировать: Я не ищу решение, которое показывает мне расстояние 2 точки

Ответы [ 15 ]

0 голосов
/ 30 октября 2017

Вот ответ Федерико Рампони в Go. Примечание: без проверки ошибок: (

import (
    "math"
)

// Semi-axes of WGS-84 geoidal reference
const (
    // Major semiaxis (meters)
    WGS84A = 6378137.0
    // Minor semiaxis (meters)
    WGS84B = 6356752.3
)

// BoundingBox represents the geo-polygon that encompasses the given point and radius
type BoundingBox struct {
    LatMin float64
    LatMax float64
    LonMin float64
    LonMax float64
}

// Convert a degree value to radians
func deg2Rad(deg float64) float64 {
    return math.Pi * deg / 180.0
}

// Convert a radian value to degrees
func rad2Deg(rad float64) float64 {
    return 180.0 * rad / math.Pi
}

// Get the Earth's radius in meters at a given latitude based on the WGS84 ellipsoid
func getWgs84EarthRadius(lat float64) float64 {
    an := WGS84A * WGS84A * math.Cos(lat)
    bn := WGS84B * WGS84B * math.Sin(lat)

    ad := WGS84A * math.Cos(lat)
    bd := WGS84B * math.Sin(lat)

    return math.Sqrt((an*an + bn*bn) / (ad*ad + bd*bd))
}

// GetBoundingBox returns a BoundingBox encompassing the given lat/long point and radius
func GetBoundingBox(latDeg float64, longDeg float64, radiusKm float64) BoundingBox {
    lat := deg2Rad(latDeg)
    lon := deg2Rad(longDeg)
    halfSide := 1000 * radiusKm

    // Radius of Earth at given latitude
    radius := getWgs84EarthRadius(lat)

    pradius := radius * math.Cos(lat)

    latMin := lat - halfSide/radius
    latMax := lat + halfSide/radius
    lonMin := lon - halfSide/pradius
    lonMax := lon + halfSide/pradius

    return BoundingBox{
        LatMin: rad2Deg(latMin),
        LatMax: rad2Deg(latMax),
        LonMin: rad2Deg(lonMin),
        LonMax: rad2Deg(lonMax),
    }
}
0 голосов
/ 30 августа 2017

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

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

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

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

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

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

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;

Прочитайте мой подробный ответ на https://stackoverflow.com/a/45950426/5076414

0 голосов
/ 19 июля 2017

Спасибо @Fedrico A. за реализацию Phyton, я перенес его в класс Objective C. Вот:

#import "LocationService+Bounds.h"

//Semi-axes of WGS-84 geoidal reference
const double WGS84_a = 6378137.0; //Major semiaxis [m]
const double WGS84_b = 6356752.3; //Minor semiaxis [m]

@implementation LocationService (Bounds)

struct BoundsLocation {
    double maxLatitude;
    double minLatitude;
    double maxLongitude;
    double minLongitude;
};

+ (struct BoundsLocation)locationBoundsWithLatitude:(double)aLatitude longitude:(double)aLongitude maxDistanceKm:(NSInteger)aMaxKmDistance {
    return [self boundingBoxWithLatitude:aLatitude longitude:aLongitude halfDistanceKm:aMaxKmDistance/2];
}

#pragma mark - Algorithm 

+ (struct BoundsLocation)boundingBoxWithLatitude:(double)aLatitude longitude:(double)aLongitude halfDistanceKm:(double)aDistanceKm {
    double radianLatitude = [self degreesToRadians:aLatitude];
    double radianLongitude = [self degreesToRadians:aLongitude];
    double halfDistanceMeters = aDistanceKm*1000;


    double earthRadius = [self earthRadiusAtLatitude:radianLatitude];
    double parallelRadius = earthRadius*cosl(radianLatitude);

    double radianMinLatitude = radianLatitude - halfDistanceMeters/earthRadius;
    double radianMaxLatitude = radianLatitude + halfDistanceMeters/earthRadius;
    double radianMinLongitude = radianLongitude - halfDistanceMeters/parallelRadius;
    double radianMaxLongitude = radianLongitude + halfDistanceMeters/parallelRadius;

    struct BoundsLocation bounds;
    bounds.minLatitude = [self radiansToDegrees:radianMinLatitude];
    bounds.maxLatitude = [self radiansToDegrees:radianMaxLatitude];
    bounds.minLongitude = [self radiansToDegrees:radianMinLongitude];
    bounds.maxLongitude = [self radiansToDegrees:radianMaxLongitude];

    return bounds;
}

+ (double)earthRadiusAtLatitude:(double)aRadianLatitude {
    double An = WGS84_a * WGS84_a * cosl(aRadianLatitude);
    double Bn = WGS84_b * WGS84_b * sinl(aRadianLatitude);
    double Ad = WGS84_a * cosl(aRadianLatitude);
    double Bd = WGS84_b * sinl(aRadianLatitude);
    return sqrtl( ((An * An) + (Bn * Bn))/((Ad * Ad) + (Bd * Bd)) );
}

+ (double)degreesToRadians:(double)aDegrees {
    return M_PI*aDegrees/180.0;
}

+ (double)radiansToDegrees:(double)aRadians {
    return 180.0*aRadians/M_PI;
}



@end

Я проверил это и, кажется, работаю хорошо. Struct BoundsLocation следует заменить классом, я использовал его просто для того, чтобы поделиться им здесь.

0 голосов
/ 19 августа 2016

Здесь я преобразовал ответ Федерико А. Рампони в PHP, если кому-то интересно:

<?php
# deg2rad and rad2deg are already within PHP

# Semi-axes of WGS-84 geoidal reference
$WGS84_a = 6378137.0;  # Major semiaxis [m]
$WGS84_b = 6356752.3;  # Minor semiaxis [m]

# Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
function WGS84EarthRadius($lat)
{
    global $WGS84_a, $WGS84_b;

    $an = $WGS84_a * $WGS84_a * cos($lat);
    $bn = $WGS84_b * $WGS84_b * sin($lat);
    $ad = $WGS84_a * cos($lat);
    $bd = $WGS84_b * sin($lat);

    return sqrt(($an*$an + $bn*$bn)/($ad*$ad + $bd*$bd));
}

# Bounding box surrounding the point at given coordinates,
# assuming local approximation of Earth surface as a sphere
# of radius given by WGS84
function boundingBox($latitudeInDegrees, $longitudeInDegrees, $halfSideInKm)
{
    $lat = deg2rad($latitudeInDegrees);
    $lon = deg2rad($longitudeInDegrees);
    $halfSide = 1000 * $halfSideInKm;

    # Radius of Earth at given latitude
    $radius = WGS84EarthRadius($lat);
    # Radius of the parallel at given latitude
    $pradius = $radius*cos($lat);

    $latMin = $lat - $halfSide / $radius;
    $latMax = $lat + $halfSide / $radius;
    $lonMin = $lon - $halfSide / $pradius;
    $lonMax = $lon + $halfSide / $pradius;

    return array(rad2deg($latMin), rad2deg($lonMin), rad2deg($latMax), rad2deg($lonMax));
}
?>
0 голосов
/ 21 октября 2013

Это очень просто, просто перейдите на веб-сайт Panoramio, а затем откройте карту мира с веб-сайта Panoramio. Затем перейдите к указанному местоположению, для которого требуются широта и долгота.

Тогда вы нашли широту и долготу в адресной строке, например, по этому адресу.

http://www.panoramio.com/map#lt=32.739485&ln=70.491211&z=9&k=1&a=1&tab=1&pl=all

lt = 32.739485 => широта ln = 70,491211 => долгота

этот виджет Panoramio JavaScript API создает ограничивающий прямоугольник вокруг пары широта / длинна, а затем возвращает все фотографии с этими границами.

Другой вид виджета Panoramio JavaScript API, в котором вы также можете изменить цвет фона с примером , а код здесь .

Не отображается при создании настроения. Показывается после публикации.

<div dir="ltr" style="text-align: center;" trbidi="on">
<script src="https://ssl.panoramio.com/wapi/wapi.js?v=1&amp;hl=en"></script>
<div id="wapiblock" style="float: right; margin: 10px 15px"></div>
<script type="text/javascript">
var myRequest = {
  'tag': 'kahna',
  'rect': {'sw': {'lat': -30, 'lng': 10.5}, 'ne': {'lat': 50.5, 'lng': 30}}
};
  var myOptions = {
  'width': 300,
  'height': 200
};
var wapiblock = document.getElementById('wapiblock');
var photo_widget = new panoramio.PhotoWidget('wapiblock', myRequest, myOptions);
photo_widget.setPosition(0);
</script>
</div>
...