주어진 LAT/LNG 위치에 대한 경계 상자를 계산하는 방법은 무엇입니까?
문제
위도와 경도에 의해 정의 된 위치를 제공했습니다. 이제 그 지점의 10km 내에 경계 상자를 계산하고 싶습니다.
경계 박스는 Latmin, Lngmin 및 Lngmax Latmax로 정의되어야합니다.
사용하려면이 물건이 필요합니다 Panoramio API.
누군가가 토스 포인트를 얻는 방법의 공식을 알고 있습니까?
편집하다: 사람들은 Lat & lng를 입력으로 취하고 Latmin & Lngmin 및 Latmax & Latmin으로 경계 상자를 반환하는 공식/기능을 찾고 있습니다. MySQL, PHP, C#, JavaScript는 괜찮지 만 의사 코드도 괜찮습니다.
편집하다: 나는 2 포인트의 거리를 보여주는 솔루션을 찾고 있지 않습니다.
해결책
나는 주어진 위도에서 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 구현도 제공합니다. (또한 Min/Max 경도에 대한 Federico의 공식이 부정확 한 이유를 보여줍니다.)
여기서 나는 Federico A. Ramponi의 답변을 관심있는 사람을 위해 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));
}
나는 거리와 한 쌍의 좌표가 주어지면 사각형 경계 박스의 4 가지 좌표를 반환하는 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()
];
};
타원체 공식을 찾고 있습니다.
코딩을 시작하는 가장 좋은 곳은 CPAN의 Geo :: Ellipsoid 라이브러리를 기반으로합니다. 테스트를 만들고 결과를 결과와 비교할 수있는 기준을 제공합니다. 이전 고용주에서 PHP를위한 유사한 라이브러리의 기초로 사용했습니다.
살펴보십시오 location
방법. 두 번 전화하면 Bbox가 있습니다.
당신은 당신이 사용하고있는 언어를 게시하지 않았습니다. 이미 지오 코딩 라이브러리가있을 수 있습니다.
아, 그리고 지금까지 그것을 이해하지 못했다면, Google지도는 WGS84 타원체를 사용합니다.
매우 대략적인 견적이 필요했기 때문에 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
정확하지는 않지만 편리합니다.
나는 이것을 발견 한 PHP 스크립트를 조정했다. 그것을 사용하여 한 지점 주위의 상자 모서리를 찾을 수 있습니다 (예 : 20km 아웃). 내 구체적인 예는 Google Maps API에 대한 것입니다.
다음은 위도 학위를 KMS로 변환하는 것을 기반으로하는 JavaScript를 사용한 간단한 구현입니다. 1 degree latitude ~ 111.2 km
.
주어진 위도와 경도에서 너비가 10km 인지도의 범위를 계산하고 있습니다.
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의 점 P에 대해 최소 및 최대 위도, 경도 쌍을 도출해야합니다. 이 외부에 떨어지는 모든 지점은 지점에서 D보다 큰 거리에 있습니다. 여기서 주목해야 할 것은 Jan Philip Matuschek 설명에서 강조된대로 교차 위도의 계산입니다. 교차의 위도는 P 점 P의 위도가 아니라 약간 상쇄됩니다. 이는 거리의 P 점 P에 대한 올바른 최소 및 최대 경계 경도를 결정하는 데 종종 누락되었지만 중요한 부분입니다. 이는 검증에도 유용합니다.
p의 (위도, 경도)에서 (위도, 경도) 사이의 haversine 거리는 거리 d와 같습니다.
여기 파이썬 요점 https://gist.github.com/alexcpn/f95ae83a7ee0293a5225
나는 정적 위도, 긴 지점의 srcrad 반경 내의 모든 점을 찾기위한 부수적 인 문제로 경계 박스 문제를 연구하고있었습니다. 사용하는 계산이 상당히 많이있었습니다
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가 주어진 지점에서 포인트 거리를 계산하는 함수가있는 경우, 고정 지점에서 특정 거리 반경 인 포인트 만 얻기가 쉽습니다. 여기 내가 한 일이 있습니다. 몇 가지 추가 단계가 필요하다는 것을 알고 있지만 도움이되었습니다.
-- 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 웹 사이트에서 Open World Map으로 이동하여 위도와 경도가 필요한 지정된 위치로 이동하는 것은 매우 간단합니다.
그런 다음이 주소에서 주소 표시 줄에서 위도와 경도를 찾았습니다.
http://www.panoramio.com/map#lt=32.739485&ln=70.491211&z=9&k=1&tab=1&pl=all
lt = 32.739485 => 위도 ln = 70.491211 => 경도
이 Panoramio JavaScript API 위젯은 LAT/Long 쌍 주위에 경계 상자를 만듭니다. 그런 다음 해당 경계에서 모든 사진을 반환합니다.
다른 유형의 Panoramio JavaScript API 위젯으로 배경색을 변경할 수 있습니다. 예와 코드가 여기에 있습니다.
그것은 분위기를 작곡하는 데 보여주지 않습니다. 출판 후 쇼를 보여줍니다.
<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>
여기서 나는 Federico A. Ramponi의 답변을 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 구현에 대해서는 객관적인 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은 클래스로 대체되어야합니다. 여기에서 공유하는 데 사용했습니다.
위의 모든 답변은 부분적으로 만 정확합니다. 특히 호주와 같은 지역에서는 항상 극을 포함하고 10km의 경우에도 매우 큰 사각형을 계산합니다.
특히 Jan Philip Matuschek의 알고리즘 http://janmatuschek.de/latitudelongitudeboundingcoordinates#usingindex 호주의 거의 모든 지점에 대해 (-37, -90, -180, 180)의 매우 큰 사각형이 포함되었습니다. 이로 인해 데이터베이스의 대규모 사용자가 발생하며 거의 절반의 모든 사용자에 대해 거리를 계산해야합니다.
나는 그것을 발견했다 Rochester Institute of Technology의 Drupal API Earth 알고리즘 폴과 다른 곳에서도 더 잘 작동하며 구현하기가 훨씬 쉽습니다.
https://www.rit.edu/drupal/api/drupal/sites%21all%21modules%21location%21earth.inc/7.54
사용 earth_latitude_range
그리고 earth_longitude_range
경계 사각형 계산을위한 위의 알고리즘에서
그리고 사용하십시오 Google지도에서 문서화 한 거리 계산 공식 거리를 계산합니다
마일 대신 킬로미터를 검색하려면 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
여기에 Federico Ramponi의 답변이 있습니다. 참고 : 오류 확인 없음 :(
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),
}
}