2011年9月19日月曜日

地図の表示(MapKit)

MapKitはメルカトル図法をサポートする。

MapKitは3つの座標系をサポートする。

1. 地図座標(球状表現の緯度/経度)
座標 CLLocationCoordinate2D (緯度と経度)
領域 MKCoordinateSpan  及び MKCoordinate Region

2. 地図点(メルカトル図法のX、Y)
地図点 MKMapPoint
領域 MKMapSize,  MKMapRect

3. 点(UIView)
カスタムコンテンツの描画前に点にマッピングされる必要がある。
  点:CGPoint
     領域:CGSize, CGRect


座標系の変換

地図座標
convertCoordinate: toPointToView: (MKMapView) 
convertRegion: toRectToView: (MKMapView)
地図座標
地図点
MKMapPointForCoordinate
地図点
地図座標
MKCoordinateForMapPoint
MKCoordinateRegionForMapRect
地図点
pointForMapPoint: (MKOverlayView) 
rectForMapRect: (MKOverlayView)
地図座標
convertPoint: toCoordinateFromView: (MKMapView) 
convertRect: toRegionFromView: (MKMapView)
地図点
mapPointForPoint: (MKOverlayView) 
mapRectForRect: (MKOverlayView)

MKMapView.regionの構造体であるMKCoordinateRegionのスパン(span)は矩形の幅と高さの値 に似ているが、地図座標で指定されるため、単位は度、分、秒となる。

緯度1度は約111キロメートルに相当するが、経度の長さは緯度によって変わる。赤道では、経度1度は約111キロメートルに相当するが、極ではゼロになる。スパンをメートル単位で指定したい場合は、 MKCoordinateRegionMakeWithDistanceを使用して、度数ではなくメートル値で領域データ構造体 を作成する。

位置を変更するには、centerCoordinateプロパティを設定するか、[mapView setCenterCoordinate:centerCoordinate animated:YES];等で指定する。

領域割り当てる場合は、regionプロパティを設定するか、[mapView setRegion:region animated:YES];等で指定する。

// 縮小する

MKCoordinateRegion theRegion = myMapView.region;
theRegion.span.longitudeDelta *= 2.0; 
theRegion.span.latitudeDelta *= 2.0; 
[myMapView setRegion:theRegion animated:YES]; 


地図上でのユーザの現在位置表示:
mapView.showUserLocation = YES;

MKUserLocation注釈オブジェクトが地図に追加されると、カスタム注釈が追加されたときと同様 にデリゲートによってそのことが報告されます。カスタム注釈ビューをユーザの位置に関連付けた い場合は、デリゲートオブジェクトのmapView:viewForAnnotation:メソッドからそのビューを返 す必要があります。デフォルトの注釈ビューを使用する場合は、このメソッドからはnilを返す必 要があります。 

地図に対するユーザの対話操作への応答:
MKMapViewDelegateプロトコルに準拠したオブジェクトで次の種 類のイベントに応答する。
■ 地図の可視領域の変更
■ ネットワークからの地図タイルの読み込み
■ ユーザの位置の変更
■ 注釈とオーバーレイに関連する変更

地図の注釈:
地図に固定のコンテンツを追加して地図と一緒にスクロールさせたい場合は、注 釈とオーバーレイを作成しなければなりません。 



地図に注釈を表示するには、アプリケーションは2つのオブジェクトを指定する必要があります。
  • MKAnnotationプロトコルに準拠し、注釈のデータを管理するオブジェクト(注釈オブジェク ト)。
  • 地図の表面に注釈の可視表現を描画する(MKAnnotationViewクラスから派生した)ビュー(注 釈ビュー)。 


注釈ビューを提供するにはMap Viewデリゲートオブジェクトを使用します。
1. 注釈オブジェクトの定義
MKPointAnnotationクラス(タイトル、サブタイトル)またはMKAnnotationプロトコルに準拠したカスタムオブジェクト

2. 注釈ビューの定義
静止画像の場合、MKAnnotationViewクラスのimageプロパティに画像を割り当てる。
ピンの場合、MKPinAnnotationViewクラスを使用する。
それ意外の場合、MKAnnotationViewクラスをサブクラス化する。

3. MapViewデリゲートにmapView:viewForAnnotation:メソッドを実装する。
既存の注釈ビューをキューから取り出し、なければ新規の注釈ビューを作成する。

4. addAnnotation: またはaddAnnotationsでMapViewに注釈オブジェクトを追加する。

間引いた注釈の追加、または追加した注釈の間引きディベロッパ自身が対応しなくてはならない

静止画像の実装例:

MKAnnotationView* aView = [[[MKAnnotationView alloc] initWithAnnotation:annotation
                                  reuseIdentifier:@"MyCustomAnnotation"] autorelease];
aView.image = [UIImage imageNamed:@"myimage.png"];
aView.centerOffset = CGPointMake(10, -20);

標準的な注釈ビューは、デリゲートのmapView:viewForAnnotation:メソッドで作成します。

注釈ビューが必要になると、Map ViewはそのデリゲートオブジェクトのmapView:viewForAnnotation: メソッドを呼び出します。 nilを返す場合、ピン注釈ビューが使用される。ピン注釈ビュー以外を使用する場合、使用するビューをそこで作成する。


AnnotationはTableViewと同じくキューに入れて表示させることもできる
(地図の倍率が変わらない場合、該当する)


- (MKAnnotationView *)mapView:(MKMapView *)mapView
                      viewForAnnotation:(id <MKAnnotation>)annotation
{
//
これがユーザの位置の場合は、単にnilを返す
if ([annotation isKindOfClass:[MKUserLocation class]])
return nil;
// カスタム注釈を処理する
if ([annotation isKindOfClass:[MyCustomAnnotation class]])
{
//
まず、既存のピン注釈ビューをキューから取り出すことを試みる 
MKPinAnnotationView* pinView = (MKPinAnnotationView*)[mapView
dequeueReusableAnnotationViewWithIdentifier:@"CustomPinAnnotationView"];
        if (!pinView)
{
//
既存のピン注釈ビューが利用できない場合は、新しいビューを作成する
pinView = [[[MKPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:@"CustomPinAnnotation"]
                             autorelease];
            pinView.pinColor = MKPinAnnotationColorRed;
            pinView.animatesDrop = YES;
            pinView.canShowCallout = YES;
// 詳細ディスクロージャボタンを吹き出しに追加する 
   UIButton* rightButton = [UIButton buttonWithType: UIButtonTypeDetailDisclosure];
            [rightButton addTarget:self action:@selector(myShowDetailsMethod:)
                               forControlEvents:UIControlEventTouchUpInside];
            pinView.rightCalloutAccessoryView = rightButton;
        }
        else
            pinView.annotation = annotation;
        return pinView;
    }
return nil; 

注釈オブジェクトの間引き:
mapView:regionWillChangeAnimated:メソッドと mapView:regionDidChangeAnimated:メソッドを実装して、地図の拡大縮小レベルの変更を検出する。 

iOS ユーザ位置の取得

iOSでユーザ位置の取得をするには、Core Location フレームワークを使用します。

Core Location フレームワークでは、電話基地局、Wifiポイント、GPSを利用してすべてのデバイスで位置を取得することができます(「標準位置情報サービス」)。

その他に、省電力で位置の取得と変更の通知を受けることのできる「大幅変更位置情報サービス」、境界線の横断を検知できる「領域観測」があり、これらはiOS4以降でサポートされます。
(定期更新が必要な場合、できるだけ「大幅変更位置情報サービス」を使用する。)

実装にあたっては、CoreLocation.frameworkをTargetのBuild PhasesのLink Binary With Librariesから追加します。また、ヘッダファイルに、#import <CoreLocation/CoreLocation.h>を追加します。

<必要に応じ、Info.plistにUIRequiredDeviceCapabilitiesを追加し、location-servicesを指定もしくはGPSのみの場合、gpsを追加するが、位置情報サービスがなくても動作する場合は指定をしない>


以下により実装する。
1.位置情報サービスの利用可否の確認
CLLocationManagerのlocationSercicesEnabledメソッドを呼び出す(iOS4)
    YESの場合、開始可能。NOの場合、位置情報サービスを開始しようとすると位置情報サービスを有効にするかどうかユーザに確認を求めてくる。

 if ([CLLocationManager locationServicesEnabled]) {

}
else {

}

2.大幅変更位置情報サービスの開始
if(locationManager == nil){
          locationManager = [[CLLocationManager alloc] init];
     }
    locationManager.delegate = self;
    [locationManager startMonitoringSignificantLocationChanges];

3.サービスからの位置データの受信
  デリゲートによる場合:
     -(void)locationManager:(CLLocationManger *)manager
       didUpdateToLocaton:(CLLocaton *)newLocation
       fromLocation:(CLLocation *) oldLocation{

        NSDate* eventDate = newLocation.timestamp;
        NSTimeInterval howRecent = [eventDate timeIntervalSiceNow];
        if (abs(howRecent) < 15.0) {
            // 新しいイベントでは更新をOFFにする?           
       }
      newLocation.coordinate.latitude ....
      newLocation.coordinate.longitude .....
    }

デリゲートによらない場合
locationManager.location  (CLLocation *)  //位置
    locationManger.heading (CLHeading *)  //方位

   location.coordinate (CLLocationCoordinate2D)  // 座標
       coordinate.latitude // 緯度 (double)
      coordinate.longitude  // 軽度 (double)

    その他、高度、精度、タイムスタンプ、スピード、コース等が得られる。

4. 位置情報の反映
   [mapView setCenterCoordinate:locationManager.location.coordinate animated:YES];


5. サービスの停止
   [locationManager stopMonitoringSignificantLocationChanges];

うぉ、動いたな。