[Android] Googleマップに表示したマーカーをまとめるマーカークラスタリング

Android

Google Maps Android APIを利用してマップ系のアプリを作成し複数のマーカーを表示してみたけれど、ズームレベルによってはマーカーが密集して見づらくなってしまう、というようなことがあると思います。

そのようなときに使える、マーカークラスタリングについて紹介したいと思います。

Google Maps Android API Utility Library を使う

マーカークラスタリングを行うために、Google Maps Android API Utility Libraryをプロジェクトに追加します。

dependencies {
    compile 'com.google.maps.android:android-maps-utils:0.4.+'
}

ちなみにGoogle Maps Android API Utility Libraryでは、マーカークラスタリングの他にもGeoJson・KMLのインポート機能やヒートマップの表示機能などがあります。(詳細はこちら

マップにマーカーを表示する

せっかくなのでGeoJsonのインポート機能を使ってマーカーを表示してみます。以下のようなGeoJsonのサンプルデータを作成し、rawフォルダに配置します。

[geojson]

{
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Point",
        "coordinates": [
          139.77364003658295,
          35.69849264621099
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Point",
        "coordinates": [
          139.77295339107513,
          35.6987409627827
        ]
      }
    },
    {
      "type": "Feature",
      "properties": {},
      "geometry": {
        "type": "Point",
        "coordinates": [
          139.77405846118927,
          35.697965515486835
        ]
      }
    },
    ︙

次に、Google Maps Android API Utility LibraryにあるGeoJsonLayerを利用してGeoJsonを読み込み、addLayerToMap()を呼ぶとマップにマーカーが表示されます。

[MainActivity.java]

private GoogleMap mMap;

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    
    try {
        GeoJsonLayer layer = new GeoJsonLayer(mMap, R.raw.geojson, this);
        layer.addLayerToMap();
    } catch (IOException | JSONException e) {
        e.printStackTrace();
    }
    
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(35.69849264621099, 139.77364003658295), 16));
}

[実行例]

map_sample_old

マーカークラスタリング

公式の手順を参考に、マーカークラスタリングを実装します。(Google Maps Android Marker Clustering Utility

ClusterItemをimplementsしたクラスを作成

上記手順では、コンストラクタの引数が (double lat, double lng) となっていましたが、今回はGeoJsonを使用しているためGeoJsonPointを利用しました。

[MyItem.java]

public class MyItem implements ClusterItem {
    
    private final LatLng mPosition;
    
    public MyItem(GeoJsonPoint point) {
        mPosition = point.getCoordinates();
    }
    
    @Override
    public LatLng getPosition() {
        return mPosition;
    }
}

ClusterManagerを生成し、MyItemを追加する

先程のMainActivity.javaを以下のように変更しました。

[MainActivity.java]

@Override
public void onMapReady(GoogleMap googleMap) {
    mMap = googleMap;
    
    ClusterManager<MyItem> clusterManager = createClusterManager();
    mMap.setOnCameraIdleListener(clusterManager);
    mMap.setOnMarkerClickListener(clusterManager);
    mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(35.69849264621099, 139.77364003658295), 16));
}

private ClusterManager<MyItem> createClusterManager() {
    ClusterManager<MyItem> manager = new ClusterManager<>(this, mMap);
    
    try {
        GeoJsonLayer layer = new GeoJsonLayer(mMap, R.raw.geojson, this);
        Iterable<GeoJsonFeature> features = layer.getFeatures();
        for (GeoJsonFeature feature : features) {
            MyItem item = new MyItem((GeoJsonPoint) feature.getGeometry());
            manager.addItem(item);
        }
    } catch (IOException | JSONException e) {
        e.printStackTrace();
    }
    
    return manager;
}

実行すると、ズームを変更するとマーカーがまとまるようになっていると思います。

map_sample_new

おわりに

同様のライブラリがiOSでも提供されているので、Googleマップを採用するのであれば、同じような動作が実装可能だと思います。 Google Maps SDK for iOS Utility Library

参考

  • https://developers.google.com/maps/documentation/android-api/utility/?hl=ja
  • https://kagamikarasu.net/android/googlemap/geojson_clustermanager