Как рассчитать ограничительную рамку для данного местоположения lat / lng?
Я дал местоположение, определяемое широтой и долготой.
Теперь я хочу, чтобы вычислить ограничивающий прямоугольник, например, 10 километров от этой точки.
ограничивающая рамка должна быть определена как latmin, lngmin и latmax, lngmax.
Мне это нужно для того, чтобы использовать panoramio API.
кто-нибудь знает формулу, как получить эти очки?
Edit: Ребята я ищу формулу / функцию, которая принимает lat & СПГ в качестве входных данных и возвращает ограничивающий прямоугольник, как Латмин & lngmin и latmax & Латмин.
Mysql, php, c#, javascript в порядке, но и псевдокод должен быть в порядке.
Edit: я не ищу решение, которое показывает расстояние в 2 очка
15 ответов:
Я предлагаю локально аппроксимировать поверхность Земли как сферу с радиусом, заданным эллипсоидом 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))изменить: следующий код преобразует (Градусы, простые числа, секунд) в градусы + доли градуса, и наоборот (не проверено):
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))
Я написал статью о поиске ограничивающих координат:
http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates
статья объясняет формулы, а также предоставляет реализацию Java. (Это также показывает, почему формула Федерико для минимальной / максимальной долготы неточна.)
здесь я преобразовал ответ Федерико А. Рампони на 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)); }
Я написал функцию 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() ]; };
вы ищете формулу эллипсоида.
лучшее место, которое я нашел для начала кодирования, основано на библиотеке Geo::Ellipsoid из CPAN. Это дает вам базовый уровень для создания ваших тестов и сравнения ваших результатов с его результатами. Я использовал его в качестве основы для аналогичной библиотеки для PHP у моего предыдущего работодателя.
посмотри
locationметод. Позвоните дважды, и вы получите ваш вставку.вы не опубликовали, какой язык вы использовали. Возможно, у вас уже есть библиотека геокодирования.
О, и если вы еще не поняли этого, Google maps использует эллипсоид WGS84.
я адаптировал PHP-скрипт, который я нашел, чтобы сделать именно это. Вы можете использовать его, чтобы найти углы вокруг точки (скажем, 20 км). Мой конкретный пример для Google Maps API:
поскольку мне нужна была очень грубая оценка, поэтому, чтобы отфильтровать некоторые ненужные документы в запросе elasticsearch, я использовал следующую формулу:
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
не точно, но удобно.
вот простая реализация с использованием javascript, которая основана на преобразовании степени широты в kms, где
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; }
Иллюстрация @Jan Philip Matuschek отличное объяснение.(Пожалуйста, проголосуйте за его ответ, а не за это; я добавляю это, поскольку мне потребовалось немного времени для понимания исходного ответа)
метод ограничительной рамки оптимизации нахождения ближайших соседей должен был бы вывести минимальные и максимальные пары широты,долготы для точки P на расстоянии d . Все точки, которые выходят за их пределы, определенно находятся на расстоянии больше d от точки. Одна вещь, чтобы отметить здесь расчет широты пересечения, как подчеркивается в объяснении Яна Филиппа Матучека. Широта пересечения находится не на широте точки Р, а немного смещена от нее. Это часто упускаемая, но важная часть в определении правильной минимальной и максимальной ограничивающей долготы для точки P для расстояния d.это также полезно при проверке.
расстояние haversine между (широта пересечения, долгота высокая) и (широта,долгота) P равно на расстояние d.
Python gist здесь https://gist.github.com/alexcpn/f95ae83a7ee0293a5225
Я работал над проблемой ограничительной рамки в качестве побочной проблемы для нахождения всех точек в радиусе SrcRad статического LAT, длинной точки. Там было довольно много расчетов, которые используют
maxLon = $lon + rad2deg($rad/$R/cos(deg2rad($lat))); minLon = $lon - rad2deg($rad/$R/cos(deg2rad($lat)));чтобы вычислить границы долготы, но я обнаружил, что это не дает всех ответов, которые были необходимы. Потому что то, что вы действительно хотите сделать, это
(SrcRad/RadEarth)/cos(deg2rad(lat))Я знаю, я знаю, что ответ должен быть таким же, но я обнаружил, что это не так. Оказалось, что не убедившись, что я сначала делал (SRCrad / RadEarth), а затем делил на часть Cos, я оставлял некоторые точки местоположения.
после того, как вы получите все свои точки ограничительной рамки, если у вас есть функция, которая вычисляет расстояние от точки до точки, заданное lat, long легко получить только те точки, которые находятся на определенном расстоянии радиуса от фиксированной точки. Вот что я сделал. Я знаю, что это заняло несколько дополнительных шагов, но это помогло мне
-- 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 ;
это очень просто, просто перейдите на сайт 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 виджет создает ограничивающую рамку вокруг пары lat / long, а затем возвращает все фотографии с этими границами.
другой тип виджета Panoramio JavaScript API, в котором вы также можете изменить цвет фона с помощью пример и код здесь.
Он не отображается при составлении mood.It показать после публикации.
<div dir="ltr" style="text-align: center;" trbidi="on"> <script src="https://ssl.panoramio.com/wapi/wapi.js?v=1&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>
здесь я преобразовал ответ Федерико А. Рампони на 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)); } ?>
спасибо @Fedrico A. Для реализации Phyton я портировал его в класс Objective C category. Вот:
#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 должен быть заменен классом, я использовал его только для того, чтобы поделиться им здесь.
все вышеприведенные ответы только частично верны. Специально в зоне как Австралия, они всегда включают полюс и высчитывают очень большой прямоугольник даже для 10kms.
специально алгоритм Яна Филиппа Матучек в http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#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 maps для расчета расстояния
для поиска по километрам вместо миль, замените 3959 на 6371. For (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
вот ответ Федерико Рампони в 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), } }

Comments