ふらっとFlutter

Flutterでのアプリ開発についてメモしていきます

Google Mapsで画面に見えている領域の範囲内だけマーカーを表示する(Flutter)

Google Mapsでは対象の場所や施設をわかりやすく表示するためにマーカーが使われます。  
単純なマーカーの表示方法は以前の記事で紹介しました。

flutterdev.hatenablog.com

  今回は少し応用で、「画面に表示されている領域のマーカーだけ」を表示する方法です。

なぜ必要なのか?

イメージしやすいように、実際のアプリを例に説明します。
わたしが開発している「ダムめぐりガイド ビーバー」では、全国にあるダムがマップにマーカーとして表示されます。

大量のマーカーを表示したい場合の話

単純に「ダムのデータ」を「マーカー」に変換して追加すれば良いようにも思えますが、ダムは全国に1,000以上あり、実際にはマーカーが多すぎてレンダリングエラーになってしまいます。

仮にデータ数が少ない場合でもユーザーが見えている範囲外にマーカーを表示することに意味はないため、パフォーマンス的にも損になるでしょう。

そこで、「画面に見えている範囲」を取得して、そこで表示すべき情報のみをマーカーにしたいというのがモチベーションです。

 

画面に表示されている範囲を取得する

それでは実装していきましょう。領域を取得するには getVisibleRegion() を呼びます。

Completer<GoogleMapController> _controller = Completer();

// ...

var googleMap = await _controller.future;
var region = await googleMap.getVisibleRegion();

ここで得られる regionLatLngBounds 型の値で、簡潔にいうと次のようなクラスです。

class LatLngBounds {
  // 南西の座標
  final LatLng southwest;

  // 北東の座標
  final LatLng northeast;
}

つまり、日本地図でいうところの「左下」「右上」の座標が得られます。
この値を使って表示すべき情報をフィルタしていきましょう。

見えている範囲だけマーカーを表示する

最初に出したダムの例で考えてみます。

まず、ダムは次のクラスで定義されているとします。

class Dam {
  String name;
  double latitude;
  double longitude;
}

全国のすべてのダムは dams という変数に入っているとします。

List<Dam> dams = [ ... ];   // 全国のダム情報が入っている

この時、画面内のダムだけを抽出するには次の実装になります。

List<CardDam> visibleDams = dams.where((Dam dam) {
  if (dam.latitude < region.southwest.latitude ||
      dam.latitude > region.northeast.latitude) {
    return false;
  } else if (dam.longitude < region.southwest.longitude ||
      dam.longitude > region.northeast.longitude) {
    return false;
  } else {
    return true;
  }
}).toList();

// いま表示すべき情報だけに絞られた
print(visibleDams);
}

これで「いま表示すべき情報」だけに絞られました。

画面の移動に応じて再描画する

多くの場合、マップはドラッグ可能で別のエリアに移動することができると思います。
上記で得たのはあくまで「その時点で表示すべき情報」なので、中心の座標やズーム率が変われば再計算しなければいけません。

Google Mapsでは、カメラが移動した時のコールバックも用意されています。

GoogleMap(
  // ...
  onCameraMove: () => { print("表示領域を再計算する") },
);

onCameraMove を使ってこのように記述できます。

画面の最大/最小ズーム率を指定する

デフォルトではGoogle Mapsは日本全土を表示できてしまい、そうなると結局すべてのマーカーを表示せざるを得なくなります。
(他のSDKではクラスタ化といってマーカーをまとめる機能がありますが、Flutterでは利用できません)

そこで、ズーム率の最大/最小倍率を指定します。

GoogleMap(
  // ....
  minMaxZoomPreference: MinMaxZoomPreference(8, 15),
);

最大でもここまでしかズームアウトできない

最大ズーム率を仕様の許す範囲で設定し、大量のマーカーが同時に描画されないようにしましょう。

まとめ

大量のデータをマップにマーカー表示する方法について紹介しました。 UIの描画はコストも高いので、良いユーザー体験のためにもパフォーマンスは大事にしたいですね。