2009-08-19 8 views
15

위도/경도 좌표 주변의 최소 경계 사각형을 결정하는 알고리즘이 있습니까?위도/경도 좌표를 수집 할 최소 경계 사각형을 결정하는 알고리즘

좌표가 너무 멀리 떨어져 있지 않으므로 평평한 지구를 사용하는 것이 좋습니다. 가짜 코드는 괜찮습니다.하지만 누군가 Objective-C에서이 작업을 수행했다면 더 좋을 것입니다. 내가하려는 것은지도에 표시 될 점의 수를 기준으로지도의 확대/축소 수준을 설정하는 것입니다.

+1

,하지만 당신은 그것을 가능 알아야한다 - 이론적으로 적어도 - 나열된 모든 답변이 훌륭하게 실패합니다. 태평양의 한 개 이상의 다각형을 고려해보십시오 (왼쪽 아래의 X는 +170, 오른쪽 상단의 X는 -170). 바운딩 박스를 적합하게 만들려고 머리를 녹일 것입니다. 이 기사 : http://www.stonybrook.edu/libmap/coordinates/seriesa/no2/a2.htm (Global Gotchas 섹션)은 문제를 해결할 수 없으며 해결할 필요가 없다는 것을 의미합니다. – tomfumb

답변

11

그러면 위 왼쪽 지점 에 대한 위도/경도가 가장 작고 오른쪽 하단 지점에 가장 큰 위도/경도가 표시됩니다. 당신이 원하는 무엇을, 당신은 아마 북위 롱의 최소 및 최대 값을 찾아 당신의 사각형의 경계로 사람들을 사용할 수있는 경우

double minLat = 900; 
double minLon = 900; 
double maxLat = -900; 
double maxLon = -900; 
foreach(Point point in latloncollection) 
{ 
    minLat = Math.min(minLat, point.lat); 
    minLon = Math.min(minLon, point.lon); 
    maxLat = Math.max(maxLat, point.lat); 
    maxLon = Math.max(maxLon, point.lon); 
} 
+0

위도와 경도 절대 값이 900을 초과하지 않는다는 것을 알고 있다고해도, 목록의 첫 번째 점에 대한 최소값과 최대 값을 시작한 후 더 나은 값을 찾으려는 것이 더 좋을 것이라고 생각합니다. 리스트의 2 번째의 아이템 – mbritto

0

는 당신이이 경우에 당신은 많은 작업을 수행 할 STL을 사용할 수 있습니다, 대신 ++ 목표 - C를 사용할 수 있습니다 당신을 위해 무거운 :

#include <vector> 
#include <algorithm> 

std::vector<float> latitude_set; 
std::vector<float> longitude_set; 

latitude_set.push_back(latitude_a); 
latitude_set.push_back(latitude_b); 
latitude_set.push_back(latitude_c); 
latitude_set.push_back(latitude_d); 
latitude_set.push_back(latitude_e); 

longitude_set.push_back(longitude_a); 
longitude_set.push_back(longitude_b); 
longitude_set.push_back(longitude_c); 
longitude_set.push_back(longitude_d); 
longitude_set.push_back(longitude_e); 

float min_latitude = *std::min_element(latitude_set.begin(), latitude_set.end()); 
float max_latitude = *std::max_element(latitude_set.begin(), latitude_set.end()); 

float min_longitude = *std::min_element(longitude_set.begin(), longitude_set.end()); 
float max_longitude = *std::max_element(longitude_set.begin(), longitude_set.end()); 
+0

일반 C++에도 라이브러리가 있습니다. 또한이 코딩을 정적으로 어떻게 코딩하는 것이 좋은 생각일까요? – Foredecker

+0

이것은 표준 호환 C++입니다. 위도 및 경도 벡터의 정적 채우기는 예제에 대한 것입니다 ... 원하는대로 벡터에 항목을 추가 할 수 있습니다. – fbrereto

+0

더 간단한 ObjC 코드가 더 가벼울 때 무거운 들어 올림 각도를 볼 수 있을지 확신하지 못합니다. 어떤 방법 으로든 잘라내는 것은 모든 포인트 값을보고있는 것입니다. –

0

당신이 가장 오른쪽, 맨 위 가장 왼쪽, 그리고 가장 아래 값을 가져하기 만하면됩니다. 정렬을 통해이 작업을 매우 쉽게 수행 할 수 있습니다. 세트가 너무 크지 않으면 매우 비싸지 않을 것입니다.

compareLatitude:compareLongitude:이라고하는 위도/경도 메서드를 제공하면 훨씬 쉽습니다.

CGFloat north, west, east, south; 
[latLongCollection sortUsingSelector:@selector(compareLongitude:)]; 
west = [[latLongCollection objectAtIndex:0] longitude]; 
east = [[latLongCollection lastObject] longitude]; 
[latLongCollection sortUsingSelector:@selector(compareLatitude:)]; 
south = [[latLongCollection objectAtIndex:0] latitude]; 
north = [[latLongCollection lastObject] latitude]; 

좌표 컬렉션이 NSMutableArray라고 가정하면 정상적으로 작동합니다.

+0

내 배열은 MKAnnotation 객체의 NSMutableArray입니다. 나는 비교를하기 위해 선택기 메소드를 구현하는 가장 좋은 방법을 생각해야 할 것이다. 목록을 수동으로 반복하는 것만 큼 다르지 않습니다. 그러나 이것은 좀 더 "우아한"것입니다. –

10

이것은 내 앱 중 하나에서 사용하는 방법입니다. 영업지도에 설정하는 경계 사각형을 사용하고 싶어하기 때문에

- (void)centerMapAroundAnnotations 
{ 
    // if we have no annotations we can skip all of this 
    if ([[myMapView annotations] count] == 0) 
     return; 

    // then run through each annotation in the list to find the 
    // minimum and maximum latitude and longitude values 
    CLLocationCoordinate2D min; 
    CLLocationCoordinate2D max; 
    BOOL minMaxInitialized = NO; 
    NSUInteger numberOfValidAnnotations = 0; 

    for (id<MKAnnotation> a in [myMapView annotations]) 
    { 
     // only use annotations that are of our own custom type 
     // in the event that the user is browsing from a location far away 
     // you can omit this if you want the user's location to be included in the region 
     if ([a isKindOfClass: [ECAnnotation class]]) 
     { 
      // if we haven't grabbed the first good value, do so now 
      if (!minMaxInitialized) 
      { 
       min = a.coordinate; 
       max = a.coordinate; 
       minMaxInitialized = YES; 
      } 
      else // otherwise compare with the current value 
      { 
       min.latitude = MIN(min.latitude, a.coordinate.latitude); 
       min.longitude = MIN(min.longitude, a.coordinate.longitude); 

       max.latitude = MAX(max.latitude, a.coordinate.latitude); 
       max.longitude = MAX(max.longitude, a.coordinate.longitude); 
      } 
      ++numberOfValidAnnotations; 
     } 
    } 

    // If we don't have any valid annotations we can leave now, 
    // this will happen in the event that there is only the user location 
    if (numberOfValidAnnotations == 0) 
     return; 

    // Now that we have a min and max lat/lon create locations for the 
    // three points in a right triangle 
    CLLocation* locSouthWest = [[CLLocation alloc] 
           initWithLatitude: min.latitude 
           longitude: min.longitude]; 
    CLLocation* locSouthEast = [[CLLocation alloc] 
           initWithLatitude: min.latitude 
           longitude: max.longitude]; 
    CLLocation* locNorthEast = [[CLLocation alloc] 
           initWithLatitude: max.latitude 
           longitude: max.longitude]; 

    // Create a region centered at the midpoint of our hypotenuse 
    CLLocationCoordinate2D regionCenter; 
    regionCenter.latitude = (min.latitude + max.latitude)/2.0; 
    regionCenter.longitude = (min.longitude + max.longitude)/2.0; 

    // Use the locations that we just created to calculate the distance 
    // between each of the points in meters. 
    CLLocationDistance latMeters = [locSouthEast getDistanceFrom: locNorthEast]; 
    CLLocationDistance lonMeters = [locSouthEast getDistanceFrom: locSouthWest]; 

    MKCoordinateRegion region; 
    region = MKCoordinateRegionMakeWithDistance(regionCenter, latMeters, lonMeters); 

    MKCoordinateRegion fitRegion = [myMapView regionThatFits: region]; 
    [myMapView setRegion: fitRegion animated: YES]; 

    // Clean up 
    [locSouthWest release]; 
    [locSouthEast release]; 
    [locNorthEast release]; 
} 
+0

좋은 코드 조각, 공유 주셔서 감사합니다. – Goles

+1

Butch Anton의 getDistanceFrom : 메시지가 iPhone OS 3.2부터 제공되지 않습니다. 코드 은 이제 distanceFromLocation :을 대신 사용해야합니다. – jessecurry

1
public BoundingRectangle calculateBoundingRectangle() 
    { 
     Coordinate bndRectTopLeft = new Coordinate(); 
     Coordinate bndRectBtRight = new Coordinate(); 

     // Initialize bounding rectangle with first point 
     Coordinate firstPoint = getVertices().get(0); 
     bndRectTopLeft.setLongitude(firstPoint.getLongitude()); 
     bndRectTopLeft.setLatitude(firstPoint.getLatitude()); 
     bndRectBtRight.setLongitude(firstPoint.getLongitude()); 
     bndRectBtRight.setLatitude(firstPoint.getLatitude()); 

     double tempLong; 
     double tempLat; 
     // Iterate through all the points 
     for (int i = 0; i < getVertices().size(); i++) 
     { 
      Coordinate curNode = getVertices().get(i); 

      tempLong = curNode.getLongitude(); 
      tempLat = curNode.getLatitude(); 
      if (bndRectTopLeft.getLongitude() > tempLong) bndRectTopLeft.setLongitude(tempLong); 
      if (bndRectTopLeft.getLatitude() < tempLat) bndRectTopLeft.setLatitude(tempLat); 
      if (bndRectBtRight.getLongitude() < tempLong) bndRectBtRight.setLongitude(tempLong); 
      if (bndRectBtRight.getLatitude() > tempLat) bndRectBtRight.setLatitude(tempLat); 

     } 

     bndRectTopLeft.setLatitude(bndRectTopLeft.getLatitude()); 
     bndRectBtRight.setLatitude(bndRectBtRight.getLatitude()); 

     // Throw an error if boundaries contains poles 
     if ((Math.toRadians(topLeft.getLatitude()) >= (Math.PI/2)) || (Math.toRadians(bottomRight.getLatitude()) <= -(Math.PI/2))) 
     { 
      // Error 
      throw new Exception("boundaries contains poles"); 
     } 
     // Now calculate bounding x coordinates 
     // Calculate it along latitude circle for the latitude closure to the 
     // pole 
     // (either north or south). For the other end the loitering distance 
     // will be slightly higher 
     double tempLat1 = bndRectTopLeft.getLatitude(); 
     if (bndRectBtRight.getLatitude() < 0) 
     { 
      if (tempLat1 < (-bndRectBtRight.getLatitude())) 
      { 
       tempLat1 = (-bndRectBtRight.getLatitude()); 
      } 
     } 

     bndRectTopLeft.setLongitude(bndRectTopLeft.getLongitude()); 
     bndRectBtRight.setLongitude(bndRectBtRight.getLongitude()); 
     // What if international date line is coming in between ? 
     // It will not affect any calculation but the range for x coordinate for the bounding rectangle will be -2.PI to +2.PI 
     // But the bounding rectangle should not cross itself 
     if ((Math.toRadians(bottomRight.getLongitude()) - Math.toRadians(topLeft.getLongitude())) >= (2 * Math.PI)) 
     { 
      // Throw some error 
      throw new Exception("Bounding Rectangle crossing itself"); 
     } 

     return new BoundingRectangle(bndRectTopLeft, bndRectBtRight); 
    } 

이 ...

2

을 지역 횡단 기둥 경우 예외를 처리하는 알고리즘이 있다는 사실을 고려해야 할 필요가 위도와 경도는 구 좌표계이며지도는 2 차원 좌표계를 사용합니다. 지금까지 게시 된 솔루션 중 아무 것도이 점을 고려하지 않아 잘못된 경계 사각형으로 끝나지 만 다행히도 WWDC 2013의이 샘플 코드에있는 "MapKit의 새로운 기능"인 MKMapPointForCoordinate 메서드를 사용하여 유효한 솔루션을 만드는 것은 매우 쉽습니다. 세션 비디오.

MKMapRect MapRectBoundingMapPoints(MKMapPoint points[], NSInteger pointCount){ 
    double minX = INFINITY, maxX = -INFINITY, minY = INFINITY, maxY = -INFINITY; 
    NSInteger i; 
    for(i = -; i< pointCount; i++){ 
     MKMapPoint p = points[i]; 
     minX = MIN(p.x,minX); 
     minY = MIN(p.y,minY); 
     maxX = MAX(p.x,maxX); 
     maxY = MAX(p.y,maxY); 
    } 
    return MKMapRectMake(minX,minY,maxX - minX,maxY-minY); 
} 


CLLocationCoordinate2D london = CLLocationCoordinate2DMake(51.500756,-0.124661); 
CLLocationCoordinate2D paris = CLLocationCoordinate2DMake(48.855228,2.34523); 
MKMapPoint points[] = {MKMapPointForCoordinate(london),MKMapPointForCoordinate(paris)}; 
MKMapRect rect = MapRectBoundingMapPoints(points,2); 
rect = MKMapRectInset(rect, 
    -rect.size.width * 0.05, 
    -rect.size.height * 0.05); 
MKCoordinateRegion coordinateRegion = MKCoordinateRegionForMapRect(rect); 

원하는 경우 특수 효과의 NSArray에서 작동하도록 메소드를 쉽게 변경할 수 있습니다. 예 : 경도 -178 가지고, -175, +

: 쓴 @malhal

- (MKCoordinateRegion)regionForAnnotations:(NSArray*)anns{ 
    MKCoordinateRegion r; 
    if ([anns count] == 0){ 
     return r; 
    } 

    double minX = INFINITY, maxX = -INFINITY, minY = INFINITY, maxY = -INFINITY; 
    for(id<MKAnnotation> a in anns){ 
     MKMapPoint p = MKMapPointForCoordinate(a.coordinate); 
     minX = MIN(p.x,minX); 
     minY = MIN(p.y,minY); 
     maxX = MAX(p.x,maxX); 
     maxY = MAX(p.y,maxY); 
    } 
    MKMapRect rect = MKMapRectMake(minX,minY,maxX - minX,maxY-minY); 
    rect = MKMapRectInset(rect, 
          -rect.size.width * 0.05, 
          -rect.size.height * 0.05); 
    return MKCoordinateRegionForMapRect(rect); 
} 
0

가 정확한지, 모든 해답은 여기에 여기에 잘못과 예입니다 : 여기 내 응용 프로그램에서 사용하고있는 방법은 175, +178. 다른 답변에 따르면, 그 둘레의 가장 작은 경계 상자는 -178 (서쪽) : +178 (동쪽)이며 전체 세계입니다.이것은 사실이 아닙니다. 지구가 둥글다면 뒤에서 보면 +175 (서쪽) : -175 (동쪽)의 더 작은 경계 상자를 갖게됩니다.

이 문제는 경도가 -180/+ 180에 가까울 때 발생합니다. 내 두뇌는 위도에 대해 생각하려고 애쓰지만, 문제가있는 경우 Google지도가 "돌아 다니지"않는 극지방에 있으므로 문제가되지 않습니다 (폴 이후). 여기

는 예를 들어 용액 (커피 스크립트)입니다 :이 더 이상 당신의 최대 관심사 (2 년 후)입니다 확신

# This is the object that keeps the mins/maxes 
corners = 
    latitude: 
    south: undefined 
    north: undefined 
    longitude: 
    normal: 
     west: undefined 
     east: undefined 
    # This keeps the min/max longitude after adding +360 to negative ones 
    reverse: 
     west: undefined 
     east: undefined 

points.forEach (point) -> 
    latitude = point.latitude 
    longitude = point.longitude 
    # Setting latitude corners 
    corners.latitude.south = latitude if not corners.latitude.south? or latitude < corners.latitude.south 
    corners.latitude.north = latitude if not corners.latitude.north? or latitude > corners.latitude.north 
    # Setting normal longitude corners 
    corners.longitude.normal.west = longitude if not corners.longitude.normal.west? or longitude < corners.longitude.normal.west 
    corners.longitude.normal.east = longitude if not corners.longitude.normal.east? or longitude > corners.longitude.normal.east 
    # Setting reverse longitude corners (when looking from the other side) 
    longitude = if longitude < 0 then longitude + 360 else longitude 
    corners.longitude.reverse.west = longitude if not corners.longitude.reverse.west? or longitude < corners.longitude.reverse.west 
    corners.longitude.reverse.east = longitude if not corners.longitude.reverse.east? or longitude > corners.longitude.reverse.east 

# Choosing the closest corners 
# Extreme examples: 
# Same:   -174 - -178 = +186 - +182 (both eastgtive) 
# Better normal: +2 - -4 < 176 - +2 (around the front) 
# Better reverse: +182 - +178 < +178 - -178 (around the back) 
if corners.longitude.normal.east - corners.longitude.normal.west < corners.longitude.reverse.east - corners.longitude.reverse.west 
    corners.longitude = corners.longitude.normal 
else 
    corners.longitude = corners.longitude.reverse 
    corners.longitude.west = corners.longitude.west - 360 if corners.longitude.west > 180 
    corners.longitude.east = corners.longitude.east - 360 if corners.longitude.east > 180 

# Now: 
# SW corner at: corners.latitude.south/corners.longitude.west 
# NE corner at: corners.latitude.north/corners.longitude.east 
관련 문제