2012-05-11 11 views
16

Muszę sprawdzić, czy lokalizacja użytkownika należy do MKCoordinateRegion. Byłem zaskoczony, że nie znalazłem prostej funkcji do tego, coś takiego: CGRectContainsCGPoint (rect, point).Jak sprawdzić, czy MKCoordinateRegion zawiera CLLocationCoordinate2D bez używania MKMapView?

znalazłem następujący fragment kodu:

CLLocationCoordinate2D topLeftCoordinate = 
    CLLocationCoordinate2DMake(region.center.latitude 
           + (region.span.latitudeDelta/2.0), 
           region.center.longitude 
           - (region.span.longitudeDelta/2.0)); 


    CLLocationCoordinate2D bottomRightCoordinate = 
    CLLocationCoordinate2DMake(region.center.latitude 
           - (region.span.latitudeDelta/2.0), 
           region.center.longitude 
           + (region.span.longitudeDelta/2.0)); 

     if (location.latitude < topLeftCoordinate.latitude || location.latitude > bottomRightCoordinate.latitude || location.longitude < bottomRightCoordinate.longitude || location.longitude > bottomRightCoordinate.longitude) { 

    // Coordinate fits into the region 

    } 

Ale nie jestem pewien, czy to jest dokładna jako dokumentacja nie określa dokładnie, w jaki sposób obliczany jest obszar prostokąta.

Musi to być prostszy sposób. Czy przeoczyłem jakąś funkcję w dokumentacji struktury MapKit?

Odpowiedz

16

W przypadku nie ma nikogo innego mylić z szerokościach i longitues, tutaj jest testowany, roztwór roboczy:

MKCoordinateRegion region = self.mapView.region; 

CLLocationCoordinate2D location = user.gpsposition.coordinate; 
CLLocationCoordinate2D center = region.center; 
CLLocationCoordinate2D northWestCorner, southEastCorner; 

northWestCorner.latitude = center.latitude - (region.span.latitudeDelta/2.0); 
northWestCorner.longitude = center.longitude - (region.span.longitudeDelta/2.0); 
southEastCorner.latitude = center.latitude + (region.span.latitudeDelta/2.0); 
southEastCorner.longitude = center.longitude + (region.span.longitudeDelta/2.0); 

if (
    location.latitude >= northWestCorner.latitude && 
    location.latitude <= southEastCorner.latitude && 

    location.longitude >= northWestCorner.longitude && 
    location.longitude <= southEastCorner.longitude 
    ) 
{ 
    // User location (location) in the region - OK :-) 
    NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| IN!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude); 

}else { 

    // User location (location) out of the region - NOT ok :-(
    NSLog(@"Center (%f, %f) span (%f, %f) user: (%f, %f)| OUT!", region.center.latitude, region.center.longitude, region.span.latitudeDelta, region.span.longitudeDelta, location.latitude, location.longitude); 
} 
+1

Wątpię, aby to zadziałało: 1. Dlaczego powinien on być.latitude> = northWestCorner.latitude? Czy nie powinien to być sounthEastCorner.latitude? 2. Co należy zrobić, jeśli obliczona minimalna długość geograficzna wynosi -2,0, maksymalna długość geograficzna to 2,0, a długość lokalizacji to 359,0? – lichen19853

+1

@ lichen19853 ma rację, że nie powiedzie się, gdy testowany jest pod kątem 360 stopni. Zobacz poniżej moją odpowiedź na nieco bardziej poprawne rozwiązanie. – MarekR

15

Możesz skonwertować swoją lokalizację do punktu o numerze MKMapPointForCoordinate, a następnie użyć MKMapRectContainsPoint w widoku mapy visibleMapRect. To jest całkowicie poza zasięgiem mojej głowy. Daj znać czy działa.

+0

Całkowicie przytłaczające jest zainicjowanie całego MKMapView i skonfigurowanie go tylko na tak prostą kontrolę. Muszę to obliczyć poza dowolnym kontrolerem widoku. – Lukasz

+0

Przepraszam, myślałem, że pracujesz z mapview już w miejscu. Jeśli masz tylko ten region, będziesz musiał polegać na nim, by być dokładnym. Jak myślisz, dlaczego region nie jest dobry? Skąd masz region? –

+0

Region jest OK. Po prostu nie jestem pewien, czy sprawdzam poprawnie. Dokumentacja MKCoordinateRegion nie precyzuje dokładnie, w jaki sposób rozpiętość szerokości i długości geograficznej buduje prostokąt obszaru. – Lukasz

13

jestem delegowania tę odpowiedź jako przyjęte rozwiązanie nie jest ważny w moim zdaniem. Ta odpowiedź również nie jest idealna, ale obsługuje przypadek, gdy współrzędne zawijają się wokół granic 360 stopni, co jest wystarczające, aby nadawać się w mojej sytuacji.

+ (BOOL)coordinate:(CLLocationCoordinate2D)coord inRegion:(MKCoordinateRegion)region 
{ 
    CLLocationCoordinate2D center = region.center; 
    MKCoordinateSpan span = region.span; 

    BOOL result = YES; 
    result &= cos((center.latitude - coord.latitude)*M_PI/180.0) > cos(span.latitudeDelta/2.0*M_PI/180.0); 
    result &= cos((center.longitude - coord.longitude)*M_PI/180.0) > cos(span.longitudeDelta/2.0*M_PI/180.0); 
    return result; 
} 
+0

To powinno być akceptowane rozwiązanie. Funkcja cos() zajmuje się problemem od 0 do 360 stopni. Mimo że wykonuje on nieliniową skalę na odległość, to jest porównywany do równie skalowanej delty, więc działa jak urok. – Brainware

+0

To zadziałało dla mnie w małym, dobrze zdefiniowanym prostokątnym regionie w moim mieście. Nie mogę potwierdzić ogólnego przypadku. – Verticon

5

Użyłem tego kodu, aby określić, czy współrzędne znajdują się w obrębie okręgu kołowego (współrzędna z promieniem wokół niego).

- (BOOL)location:(CLLocation *)location isNearCoordinate:(CLLocationCoordinate2D)coordinate withRadius:(CLLocationDistance)radius 
{ 
    CLCircularRegion *circularRegion = [[CLCircularRegion alloc] initWithCenter:location.coordinate radius:radius identifier:@"radiusCheck"]; 

    return [circularRegion containsCoordinate:coordinate]; 
} 
6

Pozostałe odpowiedzi mają wady. Przyjęta odpowiedź jest trochę gadatliwa i kończy się niepowodzeniem w pobliżu międzynarodowej linii danych. Odpowiedź cosinus jest możliwa do zastosowania, ale kończy się niepowodzeniem w przypadku bardzo małych regionów (ponieważ delta cosinus jest sinusem, który dąży do zera blisko zera, co oznacza, że ​​przy mniejszych różnicach kątowych spodziewamy się zerowej zmiany). Ta odpowiedź powinna działać poprawnie we wszystkich sytuacjach i jest prostsza.

Swift:

/* Standardises and angle to [-180 to 180] degrees */ 
class func standardAngle(var angle: CLLocationDegrees) -> CLLocationDegrees { 
    angle %= 360 
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle 
} 

/* confirms that a region contains a location */ 
class func regionContains(region: MKCoordinateRegion, location: CLLocation) -> Bool { 
    let deltaLat = abs(standardAngle(region.center.latitude - location.coordinate.latitude)) 
    let deltalong = abs(standardAngle(region.center.longitude - location.coordinate.longitude)) 
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong 
} 

Cel C:

/* Standardises and angle to [-180 to 180] degrees */ 
+ (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle { 
    angle %= 360 
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle 
} 

/* confirms that a region contains a location */ 
+ (BOOL)region:(MKCoordinateRegion*)region containsLocation:(CLLocation*)location { 
    CLLocationDegrees deltaLat = fabs(standardAngle(region.center.latitude - location.coordinate.latitude)) 
    CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude)) 
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong 
} 

Metoda ta nie powiedzie się w regionach, które zawierają albo słup jednak, ale sam system zawiedzie z biegunów współrzędnych. W przypadku większości aplikacji rozwiązanie to powinno wystarczyć. (Należy zauważyć, nie testowane w celu C)

+0

yep objc kod nie kompiluje –

+1

W przypadku metody standardowej dla przypadku, gdy znormalizowany kąt> 180: nie należy zwracać wartości 360 - kąt zamiast 360 - 180? – Verticon

1

Owen Godfrey, kod celem C CZY NIE pracy, to jest dobre Kod: nie działa na cel C, to jest dobre Kod:

/* Standardises and angle to [-180 to 180] degrees */ 
- (CLLocationDegrees)standardAngle:(CLLocationDegrees)angle { 
    angle=fmod(angle,360); 
    return angle < -180 ? -360 - angle : angle > 180 ? 360 - 180 : angle; 
} 

-(BOOL)thisRegion:(MKCoordinateRegion)region containsLocation:(CLLocation *)location{ 
    CLLocationDegrees deltaLat =fabs([self standardAngle:(region.center.latitude-location.coordinate.latitude)]); 
    CLLocationDegrees deltaLong =fabs([self standardAngle:(region.center.longitude-location.coordinate.longitude)]); 
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >=deltaLong; 
} 
    CLLocationDegrees deltalong = fabs(standardAngle(region.center.longitude - location.coordinate.longitude)); 
    return region.span.latitudeDelta >= deltaLat && region.span.longitudeDelta >= deltalong; 
} 

Dzięki!

0

oparciu o rozwiązania Łukasza, ale w Swift, w przypadku ktoś może wykorzystać SWIFT:

func isInRegion (region : MKCoordinateRegion, coordinate : CLLocationCoordinate2D) -> Bool { 

    let center = region.center; 
    let northWestCorner = CLLocationCoordinate2D(latitude: center.latitude - (region.span.latitudeDelta/2.0), longitude: center.longitude - (region.span.longitudeDelta/2.0)) 
    let southEastCorner = CLLocationCoordinate2D(latitude: center.latitude + (region.span.latitudeDelta/2.0), longitude: center.longitude + (region.span.longitudeDelta/2.0)) 

    return (
     coordinate.latitude >= northWestCorner.latitude && 
     coordinate.latitude <= southEastCorner.latitude && 

     coordinate.longitude >= northWestCorner.longitude && 
     coordinate.longitude <= southEastCorner.longitude 
    ) 
} 
0

miałem problem z samych obliczeń.Podoba mi się koncepcja zaproponowana przez Owena Godfreya: here, choć nawet Fernando here nie zauważył, że szerokość geograficzna jest różnie uformowana niż długość geograficzna i ma inny zakres. Aby wyjaśnić moją propozycję, opublikuję ją za pomocą testów, abyś mógł to sprawdzić sam.

import XCTest 
import MapKit 

// MARK - The Solution 

extension CLLocationDegrees { 

    enum WrapingDimension: Double { 
     case latitude = 180 
     case longitude = 360 
    } 

    /// Standardises and angle to [-180 to 180] or [-90 to 90] degrees 
    func wrapped(diemension: WrapingDimension) -> CLLocationDegrees { 
     let length = diemension.rawValue 
     let halfLenght = length/2.0 
     let angle = self.truncatingRemainder(dividingBy: length) 
     switch diemension { 
     case .longitude: 
      //  return angle < -180.0 ? 360.0 + angle : angle > 180.0 ? -360.0 + angle : angle 
      return angle < -halfLenght ? length + angle : angle > halfLenght ? -length + angle : angle 
     case .latitude: 
      //  return angle < -90.0 ? -180.0 - angle : angle > 90.0 ? 180.0 - angle : angle 
      return angle < -halfLenght ? -length - angle : angle > halfLenght ? length - angle : angle 
     } 
    } 
} 

extension MKCoordinateRegion { 
    /// confirms that a region contains a location 
    func contains(_ coordinate: CLLocationCoordinate2D) -> Bool { 
     let deltaLat = abs((self.center.latitude - coordinate.latitude).wrapped(diemension: .latitude)) 
     let deltalong = abs((self.center.longitude - coordinate.longitude).wrapped(diemension: .longitude)) 
     return self.span.latitudeDelta/2.0 >= deltaLat && self.span.longitudeDelta/2.0 >= deltalong 
    } 
} 

// MARK - Unit tests 

class MKCoordinateRegionContaingTests: XCTestCase { 

    func testRegionContains() { 
     var region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 0), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)) 
     var coords = CLLocationCoordinate2DMake(0, 0) 
     XCTAssert(region.contains(coords)) 

     coords = CLLocationCoordinate2DMake(0.5, 0.5) 
     XCTAssert(region.contains(coords)) 

     coords = CLLocationCoordinate2DMake(-0.5, 0.5) 
     XCTAssert(region.contains(coords)) 
     coords = CLLocationCoordinate2DMake(0.5, 0.5000001) 
     XCTAssert(!region.contains(coords)) // NOT Contains 
     coords = CLLocationCoordinate2DMake(0.5, -0.5000001) 
     XCTAssert(!region.contains(coords)) // NOT Contains 
     coords = CLLocationCoordinate2DMake(1, 1) 
     XCTAssert(!region.contains(coords)) // NOT Contains 

     region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(0, 180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)) 
     coords = CLLocationCoordinate2DMake(0, 180.5) 
     XCTAssert(region.contains(coords)) 
     coords.longitude = 179.5 
     XCTAssert(region.contains(coords)) 
     coords.longitude = 180.5000001 
     XCTAssert(!region.contains(coords)) // NOT Contains 
     coords.longitude = 179.5000001 
     XCTAssert(region.contains(coords)) 
     coords.longitude = 179.4999999 
     XCTAssert(!region.contains(coords)) // NOT Contains 

     region = MKCoordinateRegionMake(CLLocationCoordinate2DMake(90, -180), MKCoordinateSpan(latitudeDelta: 1, longitudeDelta: 1)) 
     coords = CLLocationCoordinate2DMake(90.5, -180.5) 
     XCTAssert(region.contains(coords)) 

     coords = CLLocationCoordinate2DMake(89.5, -180.5) 
     XCTAssert(region.contains(coords)) 

     coords = CLLocationCoordinate2DMake(90.50000001, -180.5) 
     XCTAssert(!region.contains(coords)) // NOT Contains 

     coords = CLLocationCoordinate2DMake(89.50000001, -180.5) 
     XCTAssert(region.contains(coords)) 

     coords = CLLocationCoordinate2DMake(89.49999999, -180.5) 
     XCTAssert(!region.contains(coords)) // NOT Contains 
    } 

    func testStandardAngle() { 
     var angle = 180.5.wrapped(diemension: .longitude) 
     var required = -179.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = 360.5.wrapped(diemension: .longitude) 
     required = 0.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = 359.5.wrapped(diemension: .longitude) 
     required = -0.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = 179.5.wrapped(diemension: .longitude) 
     required = 179.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = 90.5.wrapped(diemension: .latitude) 
     required = 89.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = 90.5000001.wrapped(diemension: .latitude) 
     required = 89.4999999 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = -90.5.wrapped(diemension: .latitude) 
     required = -89.5 
     XCTAssert(self.areAngleEqual(angle, required)) 

     angle = -90.5000001.wrapped(diemension: .latitude) 
     required = -89.4999999 
     XCTAssert(self.areAngleEqual(angle, required)) 
    } 

    /// compare doubles with presition to 8 digits after the decimal point 
    func areAngleEqual(_ a:Double, _ b:Double) -> Bool { 
     let presition = 0.00000001 
     let equal = Int(a/presition) == Int(b/presition) 
     print(String(format:"%14.9f %@ %14.9f", a, equal ? "==" : "!=", b)) 
     return equal 
    } 
}