[iOS] Google Maps SDK for iOS – 表示中の領域にのみマーカーを描画する

2014.08.29

前回に引き続きGoogle Maps SDK for iOSをさわっていきます。(SDKやAPIキーの準備などは前回の記事等を参考にしてください。)

地図上に描画するマーカーがあまりにも多い場合、地図が見づらくなり、描画処理が重たくなってしまいます。これを防ぐやり方にはいくつか方法がありますが、今回は、「現在画面上に表示している地図領域に存在するマーカーのみ表示する」方法を紹介します。

Viewport Marker Management

google developersのドキュメントで説明されている「Viewport Marker Management」という方法を参考に実装しました。

Viewport marker management is not a clustering technique, although it can be combined with any of the other clustering methods already listed.

A viewport marker manager works by getting the current bounds of the map that is visible to the user, then — based on the map's bounds — a query is sent to the server to get all the markers that lie within the bounds. As the user pans/zooms the map subsequent requests are sent to the server to get the new markers. All markers that are no longer in view are generally removed to improve map performance.

Too Many Markers! - Viewport Marker Management

実装

GMSMapViewprojectionプロパティ(GMSProjection)を持っており、GMSProjectionのcontainsCoordinate:メソッドを使用すると、特定の座標が表示領域内に存在するかを判定できます。

GMSMapViewDelegatemapView:idleAtCameraPosition:メソッド(マップのカメラ移動が停止する度に呼ばれる)が呼ばれたタイミングで、マーカーが表示領域内かどうかを判定し、マーカーの表示/非表示を更新できます。(mapView:didChangeCameraPosition:を使うとカメラが移動する度にマーカーの表示/非表示を更新できます。)

#pragma mark - private methods

/**
 *  markerが地図の可視領域に存在するか
 *
 *  @param marker `GMSMarker`
 *
 *  @return markerが地図の可視領域に存在する場合YES、そうでなければNO
 */
- (BOOL)containsMarkerInMap:(GMSMarker *)marker
{
    return [self.mapView.projection containsCoordinate:marker.position];
}

/**
 *  マーカーの表示を更新する
 */
- (void)reloadMarkers
{
    // 表示されているマーカーのうち、画面外になったマーカー : マーカー削除
    // 非表示のマーカーのうち、画面内になったマーカー : マーカー追加
    for (GMSMarker *marker in self.markers) {
        if (marker.map) {
            // 表示中のマーカー
            if ([self containsMarkerInMap:marker] == NO) {
                // マーカーが領域外になった
                marker.map = NO;
                NSLog(@"hidden %@ marker", marker.title);
            }
        } else {
            // 非表示のマーカー
            if ([self containsMarkerInMap:marker] == YES) {
                // マーカーが領域内になった
                // フラグを表示に変更
                marker.map = self.mapView;
                NSLog(@"display %@ marker", marker.title);
            }
        }
    }
}

#pragma mark - GMSMapViewDelegate methods

- (void)mapView:(GMSMapView *)mapView idleAtCameraPosition:(GMSCameraPosition *)position
{
    [self reloadMarkers];
}

プロジェクトファイルはこちらのリポジトリに置いてあります。