読者です 読者をやめる 読者になる 読者になる

sos の 作業メモ

プログラミングや英会話学習、マイルや旅行、日常生活など。最近はWebFormなASP.NETのお守りがお仕事です。

日々の生活にhappyをプラスする|ハピタス Gポイント

その3: Map Objects (Google Maps SDK for iOS)

前回の続き

Map Objects

マップは、UIViewのサブクラスであるGMSMapViewで表示される。 これはGoogle Maps SDK for iOSの中で最も重要なオブジェクトであり、マーカーやpolylineのようなオブジェクトの追加や削除を管理するために必要なメソッドを提供している。

Introduction

Google Maps SDK for iOSを使えば、アプリでグーグルマップが使える。このマップは、Google Maps アプリと同じ見た目で、同じ機能をたくさん提供している。

さらに、iOSのUIに従った幅広いインタラクションをサポートしている。たとえば、タップやダブルタップに対するレスポンダを定義することで、マップとの相互のやりとりを設定できる。

マップの中で鍵となるのはGMSMapViewクラスであり、以下のような処理を自動的に行う。

  • Google Maps サービスへの接続
  • 地図イメージ(map tile)のダウンロード
  • 画面への地図の描画
  • パン(スクロール)やズーム(拡大縮小)等の様々なコントロールの表示
  • パンやズームの操作をされた時に地図を動かしたり拡大縮小を行う
  • 二本指ジェスチャで傾け(titing)られた際の地図の視角変更

sos 注: 二本指のrotateは書き忘れたのでしょうか?

これ以外にも、GMSMapViewクラスのプロパティやメソッドを通して、振る舞いや見た目を操作したり、マーカーやGround Overlayの追加と削除、地図種の変更やGMSCameraPositionクラスを使っての視点の変更などができる。

Add a Map

地図を追加するための基本的な手順

  1. Getting Strtedの手順を行って、SDKAPI Key、frameworkの設定などを行う
  2. AppDelegateの中で、 GMSServicesのprovideAPIKey:でAPI Keyをサービスに設定する
  3. ViewControllerを作成するか更新します。 このViewControllerが表示されたときに地図を表示させるためのloawViewメソッドを用意し、そこにコードを書くのが適切

    • 中心座標やズームレベルを指定したGMSCameraPositionオブジェクトを作る。GMSMApViewオブジェクトを生成するには、GSMCameraPositionオブジェクトを通して必要なパラメータを与えなければならない
    • GMSMapViewのmapWithFrame:メソッドを使ってオブジェクトを生成する。MapViewがViewControllerの唯一のViewの時は、フレームはCGRectZeroが使われ、自動的にリサイズされる。
    • GMSMapViewをView ControllerのViewとしてセットする。

      selfview = mapView; // みたいな感じ

以下はシンガポール中心部の地図を追加する例
#import "DemoViewController.h"
#import <GoogleMaps/GoogleMaps.h>
@implementation DemoViewController {
    GMSMapView *mapView_;
}
- (void)loadView {
    GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:1.285 longitude:103.848 zoom:12];
    mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
    self.view = mapView_;
}
@end

Map Types

Map Typeはいくつかの種類から選べる。 Map Typeは地図表示全体に適用される。 たとえば、地図といえば、通常、行政(国や自治体といった類)上の境界を示しているが、道路地図では都市部の全ての道路を表示する。 Google Maps SDK for iOSでは、以下の種類を提供している

  • Normal - kGMSTypeNormal

    一般的な道路地図。人工的な建築物や川等の主要なものが表示される。ラベル(建物や川の名称)も表示される。これはGoogle Maps for iOSアプリのデフォルトの地図種である。

  • Hybrid - kGMSTypeHybrid

    衛星写真に道路地図を追加したもの。名称のラベルも表示される。Google Maps for IOSアプリでも Satellite viewに切り替えることで表示可能。

  • Satellite - kGMSTypeSatellite

    衛星写真。道路や名称のラベルは表示されない。Google Maps for iOSアプリではこのモードは選べない。

  • Terrain - kGMSTypeTerrain

    地形データ。地図には、色や等高線、名称ラベル。影も含まれている。ある程度の道路と名称ラベルも表示される。

  • None - kGMSTypeNone

    地図は表示されない。このモードは、Tile Layler (Tile Overlay)を使用するときに便利である。

Change the Map Type

Map Typeを変更するには、GMSMapView.mapType プロパティに値をセットする。例えば、Satelliteに変更するなら以下。

mapView_.mapType = kGMSTypeSatellite;

Indoor Maps

ズームレベルが高い(縮尺率が小さい)時、Google Maps SDKは、空港やショッピングモール、大きな商業施設、駅などの階層化された屋内の平面図を表示する。

この屋内マップは、Map Typeが"normal"で、ズームレベルが一定以上になると自動的に表示され、一定以下になると自動的に消える。

Indoor Mapを表示したくないときは、indoorEnabledプロパティを NOにセットするとよい。

mapView_.indoorEnabled = NO;

さらに、Floor Picker コントロールも無効にすることができる。

Adding Floor Plans

IndoorMapが有効な場所なら、平面図の追加も可能。

建物を目立たせたいが、平面図データが無いときは次の手段が使える

Accessibility

デフォルトでは、地図上のAccessibility Elementsは非表示となっている。 これを有効にしたい場合は、GMSMapViewのaccessibilityElementsHiddenプロパティをNOにすればよい。

これでAccessibilityElementsがOverlayオブジェクト(マーカーやInfoWindow,Polylineなど)のために生成されるようになる。

mapView_.accessibilityElementsHidden = NO; 

このプロパティは、非公式なUIAccessibilityプロトコルに従うもので、 Google Maps SDK for iOS では、デフォルトではYESに設定されている。

Map Events

SDKでは、カメラの位置変更イベントやマーカータップイベントのような、マップ上で発生したイベントを処理する方法が提供されている

これらのイベントを処理するには、GMSMapViewDelegateプロトコルを実装する。 View Controllerでマップを表示するなら、そこで実装するとよい。

#import <GoogleMaps/GoogleMaps.h>
#import <UIKit/UIKit.h>

@interface DemoViewController : UIViewController<GMSMapViewDelegate>
@end

GMSMapViewが作成されたときにこのview controllerをdelegateにセットしてやる。 GMSMapViewDelegateはoptionalなメソッドだけなので、特定のイベントを処理するには関連するメソッドを実装しなければならない。

下のコードは、マップ上でタップされた時イベントを処理するためmapView:didTapAtCoordinate: の実装例

- (void)loadView {
  GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:1.285 longitude:103.848 zoom:12];
  mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
  mapView_.delegate = self;
  self.view = mapView_;
}

- (void)mapView:(GMSMapView *)mapView
    didTapAtCoordinate:(CLLocationCoordinate2D)coordinate {
  NSLog(@"You tapped at %f,%f", coordinate.latitude, coordinate.longitude);
}

Camera position

GMSMapViewDelegateは、マップをレンダリングするときのカメラポジションの変更のイベントを捕まえることができる。これには3種の異なるイベントが存在する。

- (void) mapView: (GMSMapView *) mapView willMove: (BOOL) gesture

カメラポジションがこれから変更される時に呼ばれる。PanやTiltといった類のユーザーの自然な操作によるものならgestureはYES、プログラムによる変更ならNOとなる。

MyLocationボタンやコンパスボタンをユーザーが押した場合はNOになるかもしれない。

ジェスチャーとアニメーションが同時に発生した場合は、mapView:idleAtCameraPosition:が呼ばれる前に、複数回呼ばれる可能性がある。

- (void) mapView: (GMSMapView ) mapView didChangeCameraPosition: (GMSCameraPosition ) position

willMoveの後、ジェスチャーやアニメーションの間に、移動中のカメラポジションが繰り返し通知される。

- (void) mapView: (GMSMapView) mapView idleAtCameraPosition: (GMSCameraPosition) position

カメラポジションが確定し、GMSMapViewが待機状態になった時に一度だけ呼ばれる。

アプリケーションは、このイベントをトリガーとしてMarkerやGMSMapView上に表示している内容をリフレッシュしたりする。

以下は、移動中はマーカーを消し、停止したらカメラポジションの住所をGeocodingで調べ、マーカーを置く例。

- (void)mapView:(GMSMapView *)mapView willMove:(BOOL)gesture {
  [mapView clear];
}

- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)cameraPosition {
  id handler = ^(GMSReverseGeocodeResponse *response, NSError *error) {
    if (error != nil) {
      GMSReverseGeocodeResult *result = response.firstResult;
      GMSMarker *marker = [GMSMarker markerWithPosition:cameraPosition.target];
      marker.title = result.addressLine1;
      marker.subtitle = result.addressLine2;
      marker.animated = YES;
      marker.map = mapView;
    }
  };
  [geocoder_ reverseGeocodeCoordinate:cameraPosition.target completionHandler:handler];
}

Other events

その他にもイベントを処理するメソッドがたくさんある。詳細はリファレンスのGMSMapViewDelegateを参照すること。

Disable map gestures

GMSMapView.settings(GMSUISettings)を利用してデフォルトのジェスチャーを無効にできる。 なお、ジェスチャを無効にしても、プログラムからの操作は制限されない。

  • scrollGestures - スワイプによるスクロールを許可するかどうか
  • zoomGestures - ダブルタップ/二本指タップ/ピンチによるズーム操作を許可するかどうか

    ちなみに、ダブルタップはその場所をズームインする

  • tiltGestures - 二本指での縦方向のスワイプによるTilt操作を許可するかどうか

  • rotateGestures - 二本指の回転による地図の回転操作を許可するかどうか

以下はスクロールとズームの操作を禁止する例

- (void)loadView {
  GMSCameraPosition *camera = [GMSCameraPosition cameraWithLatitude:1.285 longitude:103.848 zoom:12];
  mapView_ = [GMSMapView mapWithFrame:CGRectZero camera:camera];
  mapView_.settings.scrollGestures = NO;
  mapView_.settings.zoomGestures = NO;
  self.view = mapView_;
}

Map Controls

Google Maps SDK for iOSには、Google Maps アプリに似た組み込みのUIコントロールがある。 GMSUISettingクラスで、これらのコントロールの表示/非表示の設定もできる。 また、設定はすぐに反映される。

Compass

コンパスは地図の右上に表示される。カメラが0度(北)を向いている間はコンパスは表示されない。

ユーザーがコンパスをクリックすると、カメラは0度(北)を向き、コンパスはすぐに消える。

コンパスはデフォルトでは非表示なので、表示したければcompassButtonをYESにする必要がある。ただし、北向きの時は自動で消えるため、常にコンパスを表示した状態しておくのは不可能である。

mapView_.settings.compassButton = YES;

My Location button

MyLocationボタンは、myLocationButtonがYESの時に地図の右下に表示される。

ユーザーがこのボタンをクリックした時は、現在位置が判明しているならアニメーションでカメラポジションが移動する。

mapView_.settings.myLocationButton = YES;

Floor Picker

Floor Picker コントロールは、屋内マップが表示される際に、スクリーンの右下の知覚に表示される。2個以上の屋内マップが表示されているときは、Floor Pickerコントロールは、画面の中央により近い方の屋内マップに対応する。

Pickerコントロールが最初に表示される時、各建物はデフォルトのフロアが選択状態となり、pickerで選択することで別のフロアを選ぶことができる。

GMSUISettingsのindoorPickerプロパティで、Floor Pickerコントロールを無効にできる。

mapView_.settings.indoorPicker = NO;
 

Floor Pickerコントロールを無効にできるようになったのは便利ですね

次回へ続く