2012-05-11 8 views
16

ユーザーの所在地がMKCoordinateRegionに属しているかどうかを確認する必要があります。 私はこのために単純な関数を見つけないと驚いたCGRectContainsCGPoint(rect、point)のようなものです。私は地域の四角形を計算する方法を正確に指定されていない文書で正確であるかどうかわからないよ、MKCoordinateRegionにMKMapViewを使用せずにCLLocationCoordinate2Dが含まれているかどうかを確認する方法?

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 

    } 

しかし:

は、私は次のコードを発見しました。

これを行うには簡単な方法が必要です。 MapKitフレームワークのドキュメントでいくつかの機能を見落としたことはありますか?

答えて

16

緯度とlongituesと混同誰、ここでは、作業溶液がテストされている:受け入れられた解決策は、私の意見では有効でないと、私はこの答えを投稿してい

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

これはうまくいくとは思わない: 1.なぜlocation.latitude> = northWestCorner.latitudeですか?それはsounthEastCorner.latitudeではありませんか? 2.計算された最小経度が-2.0で、最大経度が2.0で、location.longitudeが359.0の場合はどうなりますか? – lichen19853

+1

@ lichen19853は、約360度のテストで失敗するということは間違いありません。もう少し正確な解決方法については、私の答えを見てください。 – MarekR

15

MKMapPointForCoordinateの位置に現在地を変換してから、のMKMapRectContainsPointを使用してください。これは私の頭の上から完全に外れています。それが動作するかどうか私に教えてください。場合

+0

MKMapView全体を初期化してそのような単純なチェックのためにセットアップするのは圧倒しています。私は任意のビューコントローラの外でこれを計算する必要があります。 – Lukasz

+0

申し訳ありませんが、あなたはすでにマップビューで作業していると思いました。あなたがその地域だけを持っているならば、正確であるためにそれに頼らざるを得ません。なぜこの地域はいいとは思いませんか?あなたはどこからその地域を手に入れましたか? –

+0

地域はOKです。私はそれを正しくチェックしているかどうか分かりません。 MKCoordinateRegionのドキュメントでは、緯度と経度がどのように領域矩形を構成するかを正確には指定していません。 – Lukasz

13

を。この答えは完璧ではありませんが、座標が360度の境界線を囲む場合に対応していますので、私の状況に適しています。

+ (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

これは容認された解決策であるべきです。 cos()関数は0〜360度の問題を処理します。距離上で非線形のスケールを実行しても、それは等しくスケーリングされたデルタと比較されるため、チャームのように機能します。 – Brainware

+0

これは私のために私の街の小さな、よく定義された長方形の領域のために働いた。私は一般的な事実を証明することはできません。 – Verticon

5

このコードを使用して、座標が円形領域(周囲の半径を持つ座標)内にあるかどうかを判断しました。

- (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

その他の回答にはすべて誤りがあります。受け入れられた答えは少し冗長で、国際的なデータラインの近くで失敗します。余弦応答は実行可能ですが、非常に小さい領域では失敗します(デルタ余弦はゼロ付近でゼロに向かう正弦波であるため、ゼロの変化が小さい角度の差を意味します)。

スウィフト:

/* 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 
} 

オブジェクティブ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 
} 

この方法はしかしポール、それ自体が極で失敗次に座標系のいずれかを含む領域で失敗しました。ほとんどのアプリケーションでは、この解決策で十分です。

+0

yep objcコードはコンパイルされません –

+1

standardAngleメソッドでは、正規化された角度> 180:の場合、戻り値は360 - 180ではなく360 - 角度であるべきですか? – Verticon

1

オーウェンGodfreyの(対物Cでテストされていない、注)、Objective-Cのコードdoesn't作業は、これは良いコードがある:

/* 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; 
} 
: は、Objective-Cのに失敗し、これは良好なコードであります

ありがとう!ルカシュ・ソリューションに基づいて

0

が、スウィフトに、誰もがスウィフトの使用することができます場合には:

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

を私は同じ計算に問題がありました。私はOwen Godfreyによって提案された構想が好きですhere、さらにフェルナンドhereは、緯度が経度とは異なるように包まれ、異なる範囲を持っているという事実を逃しました。私の提案を明確にするために、私はあなたの自己によってそれを確認することができるようにテストで投稿します。

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 
    } 
} 
関連する問題