iOS7 で 位置情報サービス と マップ を使う！ 〜 LocationManager と MapView  〜


##はじめに

マップの表示と位置情報サービスの利用についてメモ。
CoreLocationと、MapKitは切っても切れない。

### 参考にさせて頂きました

「ジオフェンシング」とは？今知っておきたい旬キーワード！ ←解り易い
http://smmlab.aainc.co.jp/?p=9234

####公式ドキュメント

apple iOS development documents
マップについて
日本語版 https://developer.apple.com/jp/devcenter/ios/library/documentation/LocationAwarenessPG.pdf
英語版 https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/LocationAwarenessPG.pdf


####ルート検索と描画サンプル

iOS 7 の新機能のサンプルコード集『iOS7 Sampler』を公開しました。
http://d.hatena.ne.jp/shu223/20130924/1379990718

iOS 7から追加されたMKDirectionsで経路検索を試してみる
http://qiita.com/koogawa/items/d047a8056a0db5b05771

####ジオフェンシングサンプル

O2O実現の本命機能! iOSの「ジオフェンシング」を使ってみよう
http://news.mynavi.jp/articles/2013/01/23/geofencing/
※ただしiOS7未対応

## 位置情報の取得とジオフェンシングの実装

CLLocationManager
https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/CLLocationManager/CLLocationManager.html

### 位置情報サービス

// 標準位置情報サービス 開始
locationManager#startUpdatingLocation
// 標準位置情報サービス 停止
locationManager#stopUpdatingLocation

// 大幅変更位置情報サービス 開始
locationManager#startMonitoringSignificantLocationChanges
// 大幅変更位置情報サービス 停止
locationManager#stopMonitoringSignificantLocationChanges

// デリゲート　位置情報の通知 (標準/大幅変更で同一のメソッドに通知される)
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;

###  地理的領域観測（ジオフェンシング）

// 地理的領域観測サービス 開始
locationManager#startMonitoringForRegion:
// 地理的領域観測サービス 停止
locationManager#stopMonitoringForRegion:

//  デリゲート 境界線横断イベント進入通知
locationManager:didEnterRegion:
//  デリゲート 境界線横断イベント退出通知
locationManager:didExitRegion:

【重要】startしたら必ずstopすること！

### 使い方をメモ…


##地図の表示

※ストーリーボードからmapViewを追加して関連づけている前提

```
// mapViewの設定
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(kLATITUDE, kLONGITUDE);
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coordinate, 100, 100);
[self.mapView setDelegate:self];
[self.mapView setShowsBuildings:YES];
[self.mapView setShowsPointsOfInterest:YES];
[self.mapView setShowsUserLocation:YES];
[self.mapView setRegion:region];
```


位置情報を作成

```
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(kLATITUDE, kLONGITUDE);
```

coordinateを中心に、latitudinalMeters x longitudinalMeters  （下記は100m四方）で表示

```
MKCoordinateRegion region = MKCoordinateRegionMakeWithDistance(coordinate, 100, 100);
[self.mapView setRegion:region];
```

区画内の建物表示プロパティ、初期値NO

```
[self.mapView setShowsBuildings:YES];
```

コンビニなどランドマークの表示プロパティ、初期値NO

```
[self.mapView setShowsPointsOfInterest:YES];
```

ユーザの現在地表示プロパティ（表示のされ方は純正マップアプリを参照）初期値NO

```
[self.mapView setShowsUserLocation:YES];
```


## 位置情報サービスの利用

現在地の取得や距離の計測、領域観測が可能。

### 二点間の距離を測って縮尺を設定

例）現在地受信時に、特定の位置情報との二点間の距離を取得して、地図の縮尺を再指定する。


```C:HogeViewController.m
-(void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
    CLLocation * fromLocation = [locations objectAtIndex:0];

    // TODO: ここで現在地と目的地との距離を計測して、ちょうどいい縮尺で地図を表示する
    CLLocationDistance distance = [fromLocation distanceFromLocation:toLocation];

    // とりあえず適当に2倍とかにしている
    MKCoordinateRegion distCoordinateRegion = MKCoordinateRegionMakeWithDistance(fromLocation.coordinate, (distance*2), (distance*2));
    self.mapView.region = distCoordinateRegion; // 再セット

    // 必要に応じて更新を止める
    [self.locationManager stopUpdatingLocation];
}

```


###ジオフェンシング　実装サンプル


```C:HogeViewController.m
-(void)createGeofence
{
    // ジオフェンスの作成、観測の開始
    self.distCircularRegion = [[CLCircularRegion alloc]initWithCenter:self.distCoordinateRegion.center radius:50. identifier:@"目的地"];
    [self.locationManager startMonitoringForRegion:self.distCircularRegion];
    // 今回は領域を可視化するために●を描画する ※2
    MKCircle * circle = [MKCircle circleWithCenterCoordinate:self.distCoordinateRegion.center radius:50.];
    [self.mapView addOverlay:circle];
}

// 進入イベント 通知
-(void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region {
    // 入った。
    [self showDialog:[NSString stringWithFormat:@"[ <- Enter  %@", region.identifier]];
}

// 退出イベント 通知
-(void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region {
    // 出た。
    [self showDialog:[NSString stringWithFormat:@" [ -> Exit... %@", region.identifier]];
}

-(void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error {
    // しっぱい。
    NSLog(@"region.identifier=%@ error=%@",region.identifier,error);
}

// オーバーレイ描画イベント
- (MKOverlayRenderer *)mapView:(MKMapView *)mapView
            rendererForOverlay:(id<MKOverlay>)overlay
{
    // ※2 今回はサンプルなので可視化します
    if ([overlay isKindOfClass:[MKCircle class]]){
        MKCircleRenderer *renderer = [[MKCircleRenderer alloc] initWithOverlay:overlay];
        renderer.lineWidth = 1.0;
        renderer.strokeColor = [UIColor redColor];
        renderer.fillColor = [UIColor redColor];
        renderer.alpha = 0.1;
        return renderer;
    }
    return overlay;
```
identifierがかぶると、別のアプリでも通知が行くよう。
デバイス内で20個まで有効なものを登録出来て、それを超えると現在地から遠い物から削除される。

この円の境界線を行ったり来たりすると横断イベントが通知される訳ではなく、境界線を含んだ「緩衝距離」というのがあるので、 進入も退出も境界線を結構過ぎてから通知されてくるらしい。そうでないと、境界線上で移動などした場合に頻繁にイベントが発生してしまうから。場所や、電波状況、LocationManagerへの設定如何にもよりそうですが、屋外で徒歩だと数十〜100m行ったところで通知される、といった感じでした。※あくまでも体感…

一部なので適宜stopメソッド使ってください

※公式ドキュメント参照


とりあえずここまで。