Як обчислити обмежувальне поле для заданого місця розташування лат / лінг?


101

Я дав місцеположення, визначене широтою та довготою. Тепер я хочу обчислити обмежувальну коробку, наприклад, за 10 кілометрів від цієї точки.

Обмежувальне поле слід визначати як latmin, lngmin та latmax, lngmax.

Мені потрібен цей матеріал, щоб використовувати API panoramio .

Хтось знає формулу про те, як отримати бали?

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

Редагувати: Я не шукаю рішення, яке показує мені відстань у 2 бали


Якщо ви десь використовуєте базу даних геоданих, у них, безумовно, інтегрований розрахунок обмежувального поля. Можна навіть зайти, наприклад, перевірити джерело PostGIS / GEOS.
Вінко Врсалович

Відповіді:


61

Я пропоную локально наблизити земну поверхню до сфери з радіусом, заданим еліпсоїдом WGS84 на заданій широті. Я підозрюю, що точне обчислення latMin та latMax вимагало б еліптичних функцій і не призвело б до значного збільшення точності (WGS84 - це саме наближення).

Моя реалізація випливає (написано в Python; я не перевіряв її):

# degrees to radians
def deg2rad(degrees):
    return math.pi*degrees/180.0
# radians to degrees
def rad2deg(radians):
    return 180.0*radians/math.pi

# 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]
def WGS84EarthRadius(lat):
    # http://en.wikipedia.org/wiki/Earth_radius
    An = WGS84_a*WGS84_a * math.cos(lat)
    Bn = WGS84_b*WGS84_b * math.sin(lat)
    Ad = WGS84_a * math.cos(lat)
    Bd = WGS84_b * math.sin(lat)
    return math.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
def 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*math.cos(lat)

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

    return (rad2deg(latMin), rad2deg(lonMin), rad2deg(latMax), rad2deg(lonMax))

EDIT: Наступний код перетворює (градуси, праймери, секунди) у градуси + частки градуса, і навпаки (не перевіряється):

def dps2deg(degrees, primes, seconds):
    return degrees + primes/60.0 + seconds/3600.0

def deg2dps(degrees):
    intdeg = math.floor(degrees)
    primes = (degrees - intdeg)*60.0
    intpri = math.floor(primes)
    seconds = (primes - intpri)*60.0
    intsec = round(seconds)
    return (int(intdeg), int(intpri), int(intsec))

4
Як зазначено в документації запропонованої бібліотеки CPAN, це має сенс лише для половинисторінок <= 10 км.
Федеріко А. Рампоні,

1
Це працює біля полюсів? Це не здається, тому що схоже, що він закінчується latMin <-pi (для південного полюса) або latMax> pi (для північного полюса)? Я думаю, що коли ви знаходитесь в половині сторони полюса, вам потрібно повернути обмежувальний ящик, який включає всі довготи і широти, обчислені зазвичай для сторони від стовпа та біля полюса збоку біля стовпа.
Дуг МакКлін

1
Ось реалізація PHP із специфікації, знайденої на JanMatuschek.de: github.com/anthonymartin/GeoLocation.class.php
Ентоні Мартін

2
Я додав реалізацію цієї відповіді на C # нижче.
Ε Г І І І О

2
@ FedericoA.Ramponi, що тут haldSideinKm? не розумію ... що я повинен пройти в цих суперечках, радіус між двома точками на карті чи що?

53

Я написав статтю про знаходження граничних координат:

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinate

У статті пояснюються формули, а також передбачено реалізацію Java. (Це також показує, чому формула Федеріко для довготи min / max є неточною.)


4
Я зробив порт PHP вашого класу GeoLocation. Його можна знайти тут: pastie.org/5416584
Ентоні Мартін

1
Я завантажив його зараз на github: github.com/anthonymartin/GeoLocation.class.php
Ентоні Мартін

1
Це навіть відповідає на питання? Якщо у нас є лише 1 вихідна точка, ми не можемо обчислити велику відстань кола, як це робиться в цьому коді, для цього потрібні два лати, довгі місця.
mdoran3844

у вашому варіанті C # є поганий код, наприклад: public override string ToString()дуже погано переосмислити такий глобальний метод лише для однієї мети, краще просто додати інший метод, а потім перевірити стандартний метод, який можна використовувати в інших частинах програми, не для gis точно ...

Ось оновлене посилання на PHP-порт класу GeoLocaiton
Anthony Martin

34

Тут я перетворив відповідь Федеріко А. Рампоні на C # для всіх, хто цікавиться:

public class MapPoint
{
    public double Longitude { get; set; } // In Degrees
    public double Latitude { get; set; } // In Degrees
}

public class BoundingBox
{
    public MapPoint MinPoint { get; set; }
    public MapPoint MaxPoint { get; set; }
}        

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

// 'halfSideInKm' is the half length of the bounding box you want in kilometers.
public static BoundingBox GetBoundingBox(MapPoint point, double halfSideInKm)
{            
    // Bounding box surrounding the point at given coordinates,
    // assuming local approximation of Earth surface as a sphere
    // of radius given by WGS84
    var lat = Deg2rad(point.Latitude);
    var lon = Deg2rad(point.Longitude);
    var halfSide = 1000 * halfSideInKm;

    // Radius of Earth at given latitude
    var radius = WGS84EarthRadius(lat);
    // Radius of the parallel at given latitude
    var pradius = radius * Math.Cos(lat);

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

    return new BoundingBox { 
        MinPoint = new MapPoint { Latitude = Rad2deg(latMin), Longitude = Rad2deg(lonMin) },
        MaxPoint = new MapPoint { Latitude = Rad2deg(latMax), Longitude = Rad2deg(lonMax) }
    };            
}

// degrees to radians
private static double Deg2rad(double degrees)
{
    return Math.PI * degrees / 180.0;
}

// radians to degrees
private static double Rad2deg(double radians)
{
    return 180.0 * radians / Math.PI;
}

// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m]
private static double WGS84EarthRadius(double lat)
{
    // http://en.wikipedia.org/wiki/Earth_radius
    var An = WGS84_a * WGS84_a * Math.Cos(lat);
    var Bn = WGS84_b * WGS84_b * Math.Sin(lat);
    var Ad = WGS84_a * Math.Cos(lat);
    var Bd = WGS84_b * Math.Sin(lat);
    return Math.Sqrt((An*An + Bn*Bn) / (Ad*Ad + Bd*Bd));
}

1
Дякую, ця робота для мене. Довелося перевірити код вручну, не знав, як написати одиничний тест для цього, але він дає точні результати на міру потрібної мені точності
mdoran3844

що тут haldSideinKm? не розумію ... що я повинен пройти в цих суперечках, радіус між двома точками на карті чи що?

@GeloVolro: Це половина довжини обмежувального поля, яке ви хочете.
Ε Г І І І О

1
Не обов’язково писати власний клас MapPoint. У System.Device.Location існує клас GeoCoordinate, який приймає параметри Latitude і Longitude.
Адвокат

1
Це прекрасно працює. Я дуже ціную порт C #.
Том

10

Я написав функцію JavaScript, яка повертає чотири координати квадратного обмежувального поля, задавши відстань і пару координат:

'use strict';

/**
 * @param {number} distance - distance (km) from the point represented by centerPoint
 * @param {array} centerPoint - two-dimensional array containing center coords [latitude, longitude]
 * @description
 *   Computes the bounding coordinates of all points on the surface of a sphere
 *   that has a great circle distance to the point represented by the centerPoint
 *   argument that is less or equal to the distance argument.
 *   Technique from: Jan Matuschek <http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates>
 * @author Alex Salisbury
*/

getBoundingBox = function (centerPoint, distance) {
  var MIN_LAT, MAX_LAT, MIN_LON, MAX_LON, R, radDist, degLat, degLon, radLat, radLon, minLat, maxLat, minLon, maxLon, deltaLon;
  if (distance < 0) {
    return 'Illegal arguments';
  }
  // helper functions (degrees<–>radians)
  Number.prototype.degToRad = function () {
    return this * (Math.PI / 180);
  };
  Number.prototype.radToDeg = function () {
    return (180 * this) / Math.PI;
  };
  // coordinate limits
  MIN_LAT = (-90).degToRad();
  MAX_LAT = (90).degToRad();
  MIN_LON = (-180).degToRad();
  MAX_LON = (180).degToRad();
  // Earth's radius (km)
  R = 6378.1;
  // angular distance in radians on a great circle
  radDist = distance / R;
  // center point coordinates (deg)
  degLat = centerPoint[0];
  degLon = centerPoint[1];
  // center point coordinates (rad)
  radLat = degLat.degToRad();
  radLon = degLon.degToRad();
  // minimum and maximum latitudes for given distance
  minLat = radLat - radDist;
  maxLat = radLat + radDist;
  // minimum and maximum longitudes for given distance
  minLon = void 0;
  maxLon = void 0;
  // define deltaLon to help determine min and max longitudes
  deltaLon = Math.asin(Math.sin(radDist) / Math.cos(radLat));
  if (minLat > MIN_LAT && maxLat < MAX_LAT) {
    minLon = radLon - deltaLon;
    maxLon = radLon + deltaLon;
    if (minLon < MIN_LON) {
      minLon = minLon + 2 * Math.PI;
    }
    if (maxLon > MAX_LON) {
      maxLon = maxLon - 2 * Math.PI;
    }
  }
  // a pole is within the given distance
  else {
    minLat = Math.max(minLat, MIN_LAT);
    maxLat = Math.min(maxLat, MAX_LAT);
    minLon = MIN_LON;
    maxLon = MAX_LON;
  }
  return [
    minLon.radToDeg(),
    minLat.radToDeg(),
    maxLon.radToDeg(),
    maxLat.radToDeg()
  ];
};

Цей код взагалі не працює. Я маю на увазі, навіть після виправлення очевидних помилок на кшталт, minLon = void 0;і maxLon = MAX_LON;це все ще не працює.
aroth

1
@aroth, я просто перевірив це і не виникло проблем. Пам'ятайте, що centerPointаргумент - це масив, що складається з двох координат. Наприклад, getBoundingBox([42.2, 34.5], 50)- void 0це вихід CoffeeScript для "невизначеного" і не впливатиме на здатність кодів до запуску.
asalisbury

цей код не працює. degLat.degToRadне є функцією
user299709

Код працював у Node та Chrome як є, поки я не вклав його в проект, над яким я працюю, і почав отримувати degToRadпомилки " не функція". Ніколи не з'ясував, чому, але Number.prototype.це не є хорошою ідеєю для такої функції утиліти, тому я перетворив їх у звичайні локальні функції. Також важливо зауважити, що повернене вікно - [СПГ, ЛАТ, СПГ, ЛАТ] замість [ЛАТ, СПГ, ЛАТ, СПГ]. Я змінив функцію повернення, коли використовував це, щоб уникнути плутанини.
KernelDeimos

9

Оскільки мені потрібна була дуже приблизна оцінка, щоб відфільтрувати деякі непотрібні документи в запиті на еластичний пошук, я застосував формулу нижче:

Min.lat = Given.Lat - (0.009 x N)
Max.lat = Given.Lat + (0.009 x N)
Min.lon = Given.lon - (0.009 x N)
Max.lon = Given.lon + (0.009 x N)

N = kms, необхідні для даного місця. Для вашого випадку N = 10

Не точно, але зручно.


Дійсно, не точний, але все ж корисний і дуже простий у виконанні.
МВ.

6

Ви шукаєте формулу еліпсоїда.

Найкраще місце, яке я знайшов для початку кодування, базується на бібліотеці Geo :: Ellipsoid від CPAN. Це дає вам базову лінію для створення своїх тестів і порівняння результатів з його результатами. Я використовував це як основу для подібної бібліотеки для PHP у мого попереднього роботодавця.

Гео :: Еліпсоїд

Погляньте на locationметод. Зателефонуйте двічі, і ви отримаєте свою скриньку.

Ви не опублікували мову, якою ви користуєтесь. Можливо, для вас уже є бібліотека геокодування.

О, і якщо ви цього ще не зрозуміли, в картах Google використовується еліпсоїд WGS84.


5

Ілюстрація чудового пояснення @Jan Philip Philip Matuschek. (Будь-ласка, проголосуйте його відповідь, не це; я додаю це, оскільки я зайняв трохи часу, щоб зрозуміти оригінальну відповідь)

Техніка обмежувального поля оптимізації пошуку найближчих сусідів потребує отримання мінімальної та максимальної широти, пар довготи для точки P на відстані d. Усі точки, які випадають поза ними, безумовно, на відстані, більшій за d від точки. Тут слід зауважити одне обчислення широти перетину, як підкреслено в поясненні Яна Філіпа Матущека. Широта перетину не знаходиться на широті точки P, але трохи зміщена від неї. Це часто пропущена, але важлива частина у визначенні правильної мінімальної та максимальної граничної довготи для точки P для відстані д. Це також корисно для перевірки.

Відстань між гаверсинами (широта перетину, висота довготи) до (широта, довгота) P дорівнює відстані d.

Пітонна суть тут https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

введіть тут опис зображення


5

Ось проста реалізація за допомогою javascript, яка заснована на перетворенні ступеня широти в км, куди 1 degree latitude ~ 111.2 km.

Я обчислюю межі карти із заданої широти та довготи із шириною 10 км.

function getBoundsFromLatLng(lat, lng){
     var lat_change = 10/111.2;
     var lon_change = Math.abs(Math.cos(lat*(Math.PI/180)));
     var bounds = { 
         lat_min : lat - lat_change,
         lon_min : lng - lon_change,
         lat_max : lat + lat_change,
         lon_max : lng + lon_change
     };
     return bounds;
}

4

Я адаптував сценарій PHP, який я знайшов, щоб зробити саме це. Ви можете використовувати його, щоб знайти кути коробки навколо точки (скажімо, 20 км назовні). Мій конкретний приклад - API API Карт Google:

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


-1 Що ОП шукає, це: задавши опорну точку (лат, лон) та відстань, знайдіть найменший ящик таким, що всі точки, що знаходяться <= "відстань" від опорної точки, не знаходяться поза полем. Ваша скринька має свої кути "відстань" від опорної точки і тому занадто мала. Приклад: точка, яка "відстань" на північ, знаходиться далеко за межами вашого поля.
Джон Махін

Ну, випадково, це саме те, що мені просто було потрібно. Тож дякую, навіть якщо це не зовсім відповідає на це питання :)
Саймон Штейнбергер,

Ну, я радий, що це може комусь допомогти!
Річард

1

Я працював над проблемою обмежувальної коробки як бічну проблему, щоб знайти всі точки в радіусі SrcRad статичної точки LAT, LONG. Існує досить багато розрахунків, які використовують

maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat)));
minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));

щоб обчислити межі довготи, але я знайшов це, щоб не дати всіх потрібних відповідей. Бо те, що ти насправді хочеш зробити, - це

(SrcRad/RadEarth)/cos(deg2rad(lat))

Я знаю, я знаю, що відповідь повинна бути однаковою, але я виявив, що це не так. Виявилося, що, не переконуючись, що я робив (SRCrad / RadEarth) спочатку, а потім ділившись на частину Cos, я випускав деякі точки розташування.

Якщо ви отримаєте всі ваші граничні точки, якщо у вас є функція, яка обчислює відстань від точки до точки, задану лат, довго можна легко отримати лише ті точки, які є певним радіусом відстані від нерухомої точки. Ось що я зробив. Я знаю, що було зроблено кілька додаткових кроків, але це допомогло мені

-- GLOBAL Constants
gc_pi CONSTANT REAL := 3.14159265359;  -- Pi

-- Conversion Factor Constants
gc_rad_to_degs          CONSTANT NUMBER := 180/gc_pi; -- Conversion for Radians to Degrees 180/pi
gc_deg_to_rads          CONSTANT NUMBER := gc_pi/180; --Conversion of Degrees to Radians

lv_stat_lat    -- The static latitude point that I am searching from 
lv_stat_long   -- The static longitude point that I am searching from 

-- Angular radius ratio in radians
lv_ang_radius := lv_search_radius / lv_earth_radius;
lv_bb_maxlat := lv_stat_lat + (gc_rad_to_deg * lv_ang_radius);
lv_bb_minlat := lv_stat_lat - (gc_rad_to_deg * lv_ang_radius);

--Here's the tricky part, accounting for the Longitude getting smaller as we move up the latitiude scale
-- I seperated the parts of the equation to make it easier to debug and understand
-- I may not be a smart man but I know what the right answer is... :-)

lv_int_calc := gc_deg_to_rads * lv_stat_lat;
lv_int_calc := COS(lv_int_calc);
lv_int_calc := lv_ang_radius/lv_int_calc;
lv_int_calc := gc_rad_to_degs*lv_int_calc;

lv_bb_maxlong := lv_stat_long + lv_int_calc;
lv_bb_minlong := lv_stat_long - lv_int_calc;

-- Now select the values from your location datatable 
SELECT *  FROM (
SELECT cityaliasname, city, state, zipcode, latitude, longitude, 
-- The actual distance in miles
spherecos_pnttopntdist(lv_stat_lat, lv_stat_long, latitude, longitude, 'M') as miles_dist    
FROM Location_Table 
WHERE latitude between lv_bb_minlat AND lv_bb_maxlat
AND   longitude between lv_bb_minlong and lv_bb_maxlong)
WHERE miles_dist <= lv_limit_distance_miles
order by miles_dist
;

0

Дуже просто, просто перейдіть на веб-сайт 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 => довгота

цей віджет JavaScript Panoramio JavaScript створює обмежувальне поле навколо лат / довгої пари, а потім повертає всі фотографії в цих межах.

Інший тип віджета API API Panoramio, в якому ви також можете змінити колір тла за допомогою прикладу та коду, тут .

Це не відображається в композиційному настрої. Це шоу після публікації.

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

0

Ось я перетворив відповідь Федеріко А. Рампоні на 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

Дякуємо @Fedrico A. за реалізацію Phyton, я переніс його у клас об’єктивного 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

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

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

Я виявив, що алгоритм Землі API Землі від Рочестерського технологічного інституту працює краще як на полюсі, так і в інших місцях, і це набагато простіше втілити.

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

Використовуйте earth_latitude_rangeі earth_longitude_rangeз наведеного вище алгоритму для обчислення обмежувального прямокутника

І використовуйте формулу обчислення відстані, задокументовану картами 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;

Прочитайте мою детальну відповідь на https://stackoverflow.com/a/45950426/5076414


0

Ось відповідь Федеріко Рампоні в «Го». Примітка: відсутність перевірки помилок :(

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),
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.