주어진 위도 / 경도 위치에 대한 경계 상자를 계산하는 방법은 무엇입니까?


101

위도와 경도로 정의 된 위치를 지정했습니다. 이제 그 지점에서 10km 이내의 경계 상자를 계산하고 싶습니다.

경계 상자는 latmin, lngmin 및 latmax, lngmax로 정의되어야합니다.

panoramio API 를 사용하려면이 항목이 필요합니다 .

누군가 thos 포인트를 얻는 방법의 공식을 알고 있습니까?

편집 : 여러분은 lat & lng를 입력으로 취하고 경계 상자를 latmin & lngmin 및 latmax & latmin으로 반환하는 공식 / 함수를 찾고 있습니다. mysql, php, c #, javascript는 괜찮지 만 의사 코드도 괜찮습니다.

편집 : 2 점의 거리를 보여주는 솔루션을 찾고 있지 않습니다.


어딘가에서 지오 데이터베이스를 사용하는 경우 반드시 경계 상자 계산이 통합되어 있습니다. 예를 들어 PostGIS / GEOS의 소스를 확인할 수도 있습니다.
Vinko Vrsalovic

답변:


60

나는 주어진 위도에서 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))

4
제안 된 CPAN 라이브러리의 문서에서 지적했듯이 이것은 halfSide <= 10km에 대해서만 의미가 있습니다.
Federico A. Ramponi

1
이것은 극 근처에서 작동합니까? latMin <-pi (남극의 경우) 또는 latMax> pi (북극의 경우)로 끝나는 것처럼 보이기 때문에 그렇지 않은 것 같습니다. 나는 당신이 극의 halfSide 안에있을 때 극에서 멀리 떨어진 쪽과 극 근처의 극에 대해 정상적으로 계산 된 모든 경도와 위도를 포함하는 경계 상자를 반환해야한다고 생각합니다.
Doug McClean

1
다음은 JanMatuschek.de에있는 사양의 PHP 구현입니다. github.com/anthonymartin/GeoLocation.class.php
Anthony Martin

2
이 답변의 C # 구현을 아래에 추가했습니다.
Ε Г И І И О

2
@ FedericoA.Ramponi 여기 haldSideinKm은 무엇입니까? 이해가 안 돼요 ...이 논쟁에서 무엇을 전달해야하는지,지도에서 두 지점 사이의 반경 또는 무엇을 전달해야합니까?

53

경계 좌표를 찾는 방법에 대한 기사를 썼습니다.

http://JanMatuschek.de/LatitudeLongitudeBoundingCoordinates

이 기사에서는 공식에 대해 설명하고 Java 구현도 제공합니다. (또한 최소 / 최대 경도에 대한 Federico의 공식이 부정확 한 이유도 보여줍니다.)


4
GeoLocation 클래스의 PHP 포트를 만들었습니다. 여기에서 찾을 수 있습니다 : pastie.org/5416584
Anthony Martin

1
지금은 github에 그것을 업로드 한 : github.com/anthonymartin/GeoLocation.class.php
앤서니 마틴에게

1
이 질문에 대한 답이 있습니까? 시작점이 1 개만 있으면이 코드 에서처럼 대원 거리를 계산할 수 없으며 두 개의 위도, 긴 위치가 필요합니다.
mdoran3844

C # 변형에 잘못된 코드가 있습니다. 예 : public override string ToString(), 이러한 전역 메서드를 한 가지 목적으로 만 재정의하는 것은 매우 나쁩니다. 다른 메서드를 추가하는 것이 좋습니다. 그런 다음 응용 프로그램의 다른 부분에서 사용할 수있는 표준 메서드를 재정의하는 것이 좋습니다. 정확한 gis가 아닙니다 ...

다음은 Jan의 GeoLocaiton 클래스의 PHP 포트에 대한 업데이트 된 링크입니다. github.com/anthonymartin/GeoLocation.php
앤서니 마틴

34

여기에 관심있는 사람을 위해 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));
}

1
고마워요. 수작업으로 코드를 테스트해야했고, 이에 대한 단위 테스트를 작성하는 방법을
몰랐지만

여기에 haldSideinKm은 무엇입니까? 이해가 안 돼요 ...이 논쟁에서 무엇을 전달해야하는지,지도에서 두 지점 사이의 반경 또는 무엇을 전달해야합니까?

@GeloVolro : 원하는 경계 상자의 절반 길이입니다.
Ε Г И І И О

1
반드시 자신 만의 MapPoint 클래스를 작성할 필요는 없습니다. System.Device.Location에는 위도와 경도를 매개 변수로 사용하는 GeoCoordinate 클래스가 있습니다.
Lawyerson

1
이것은 아름답게 작동합니다. C # 포트에 정말 감사드립니다.
Tom Larcher

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 2015

이 코드는 작동하지 않습니다. degLat.degToRad함수가 아닙니다
user299709

코드는 내가 작업중인 프로젝트에 넣고 " degToRad기능이 아님"오류가 발생 하기 전까지 Node와 Chrome에서있는 그대로 작동했습니다 . 이유를 찾지 못했지만 Number.prototype.이와 같은 유틸리티 함수에 대한 좋은 생각이 아니기 때문에이를 일반 로컬 함수로 변환했습니다. 또한 반환 된 상자가 [LAT, LNG, LAT, LNG] 대신 [LNG, LAT, LNG, LAT]이라는 점에 유의해야합니다. 혼란을 피하기 위해 이것을 사용할 때 반환 함수를 수정했습니다.
KernelDeimos

9

매우 대략적인 견적이 필요했기 때문에 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

정확하지는 않지만 편리합니다.


실제로 정확하지는 않지만 여전히 유용하고 구현하기가 매우 쉽습니다.
MV.

6

타원체 공식을 찾고 있습니다.

코딩을 시작하기에 가장 좋은 곳은 CPAN의 Geo :: Ellipsoid 라이브러리를 기반으로합니다. 테스트를 생성하고 결과와 결과를 비교할 수있는 기준을 제공합니다. 이전 직장에서 PHP 용 유사한 라이브러리의 기반으로 사용했습니다.

Geo :: Ellipsoid

location방법을 살펴보십시오 . 두 번 전화하면 bbox가 있습니다.

사용중인 언어를 게시하지 않았습니다. 이미 사용 가능한 지오 코딩 라이브러리가있을 수 있습니다.

아, 그리고 지금까지 이해하지 못했다면 Google지도는 WGS84 타원체를 사용합니다.


5

@Jan Philip Matuschek의 훌륭한 설명의 일러스트 (이것이 아닌 그의 답변에 찬성표를 보내주세요; 원래 답변을 이해하는 데 약간의 시간이 걸렸기 때문에 이것을 추가하고 있습니다)

가장 가까운 이웃 찾기를 최적화하는 경계 상자 기술은 거리 d에서 점 P에 대해 최소 및 최대 위도, 경도 쌍을 유도해야합니다. 이것들 밖에있는 모든 점들은 확실히 점으로부터 d보다 더 큰 거리에 있습니다. 여기서 주목해야 할 한 가지는 Jan Philip Matuschek 설명에서 강조된대로 교차 위도 계산입니다. 교차 위도는 점 P의 위도가 아니라 약간 오프셋됩니다. 이것은 거리 d에 대한 점 P에 대한 정확한 최소 및 최대 경계 경도를 결정하는 데 자주 놓치지 만 중요한 부분이며 검증에도 유용합니다.

P의 (교차 위도, 경도)에서 (위도, 경도) 사이의 하 베사 인 거리는 거리 d와 같습니다.

여기 파이썬 요점 https://gist.github.com/alexcpn/f95ae83a7ee0293a5225

여기에 이미지 설명 입력


5

다음은 위도를 kms로 변환하는 자바 스크립트를 사용하는 간단한 구현 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;
}

4

이 작업을 수행하기 위해 찾은 PHP 스크립트를 수정했습니다. 이를 사용하여 한 지점 (예 : 20km) 주변의 상자 모서리를 찾을 수 있습니다. 내 구체적인 예는 Google Maps API에 대한 것입니다.

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


-1 OP가 찾는 것은 : 기준점 (위도, 경도)과 거리가 주어지면 기준점에서 <= "거리"떨어진 모든 점이 상자 밖에 있지 않도록 가장 작은 상자를 찾습니다. 상자의 모서리가 기준점에서 "거리"떨어져 있으므로 너무 작습니다. 예 : 정북으로 "거리"인 지점이 상자 밖에 있습니다.
John Machin 2012

글쎄, 우연히, 그것은 정확히 내가 필요했던 것입니다. 그래서 확실히 대답하지 않는 경우에도, 감사 질문 :)
사이먼 스타 인 버거

글쎄, 누군가를 도울 수있어서 기쁘다!
리처드

1

나는 정적 LAT, LONG 포인트의 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 부분으로 나누면 위치 지점을 생략하는 것처럼 보입니다.

경계 상자 점을 모두 얻은 후 위도에 따라 점 대 점 거리를 계산하는 함수가 있으면 고정 점에서 특정 거리 반경에있는 점만 쉽게 얻을 수 있습니다. 여기 내가 한 일이 있습니다. 몇 가지 추가 단계가 필요하다는 것을 알고 있지만 도움이되었습니다.

-- 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 웹 사이트에서 World Map을 열고 위도와 경도가 필요한 지정된 위치로 이동하는 것은 매우 간단합니다.

그런 다음 주소 표시 줄에서 위도와 경도를 찾았습니다 (예 :이 주소).

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>

0

여기에 관심이 있다면 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));
}
?>

0

Phyton 구현에 대해 @Fedrico A.에게 감사드립니다. 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

위의 모든 답변은 부분적으로 만 정확합니다 . 특히 호주와 같은 지역에서는 항상 극을 포함하고 10km에서도 매우 큰 직사각형을 계산합니다.

특히 http://janmatuschek.de/LatitudeLongitudeBoundingCoordinates#UsingIndex 에있는 Jan Philip Matuschek의 알고리즘 은 호주의 거의 모든 지점에 대해 (-37, -90, -180, 180)에서 매우 큰 직사각형을 포함했습니다. 이는 데이터베이스의 대규모 사용자에게 영향을 미치며 거의 절반의 국가에있는 모든 사용자에 대해 거리를 계산해야합니다.

Rochester Institute of TechnologyDrupal API Earth Algorithm이 극과 다른 곳에서 더 잘 작동하고 구현하기가 훨씬 쉽다는 것을 발견했습니다.

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 열이있는 Markers 테이블의 경우 공식은 다음과 같습니다.

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

Go에서 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),
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.