マップアプリ、と言うとやはりGoogleマップを連想する人が多いかもしれません。
LeafletはOSSで地図をレンダリングすることができるソフトウェアです。つまり自分の好きなようにマップアプリを作ることができるようになります。
わざわざ自分で用意した地図を使う変わった人は世の中に多くないと思いますが、地図以外の用途でも使えるのがLeafletの良いところです。
Leafletで検索するとJavaScriptの記事がよく出てきます。今回は情報が比較的少ないFlutterで実装されたLeafletを使います。
セットアップ
例によってセットアップはテンポ良くいきます。まずFlutterのプロジェクトを作成してください。
pubspec.yaml に下記2つを記述してpub getします。
flutter_map: ^0.14.0
latlong2: ^0.8.1
flutter_mapのREADMEにある通りのコードを書いていくとこのような画面になります。
とても簡単に表示できました。しかし、今回はマップを表示したいわけではないのでここからゴリゴリコードを書いていきます。
前提知識
地図で表示されている1枚の画像はTileという単位で考えます。つまり地図全体は大量のTileの集合で表示されています。そして上記のスクショの地図は1枚の大きな画像があるわけではありません。
Leafletの場合、1枚あたりの画像サイズはデフォルトで256pxというキリの良い数字が設定されています。加えて、地図を一段階ズーム(拡大)した時には異なる画像サイズが読み込まれます。こちらは後ほどサンプル画像をお見せした方が分かりやすいと思います。
また、LeafletのTileの読み込み方法はhttp or httpsのURL形式とアプリ内の画像を読み込む方法があります。また、前者はキャッシュを使った読み込み方法があるため設定によって通信負荷を削減することも可能になります。
画像の準備
表示には当然、画像がないと話になりません。拡大するパターンを三段階にしたいので拡大倍率毎に3パターン必要です。
こんな感じで画像を作ってみたのですが先ほどの説明のイメージがつきましたでしょうか。
Tileのサイズは倍率が変わっても256pxのままにします。
左に表示している4枚で1セットになっている画像が最も倍率が小さめ(引きで見た状態)です。
次に真ん中の画像は256pxのTileに対して綺麗に収まってるので1Tile=1画像(等倍と言うのは違うかも?)です。
右の画像は256pxのTileに対して半分のサイズになっています。見た感じでは四分の一じゃん!と思うかもしれませんが、縦横のサイズをそれぞれ半分にするのでこれで正解です(面積は四分の一ですね)
作成した画像をそれぞれ./assetsに置いてpubspec.yamlに読み込みを記述します。
flutter:
uses-material-design: true
assets:
- ./assets/
画像は左から
zoom2_topleft.png
zoom2_topright.png
zoom2_bottomleft.png
zoom2_bottomright.png
zoom3.png
zoom4.png
とします。
実装
画面を先に。
ズームした時に内部的にはサイズ違いの画像が入れ替わっているのですが、とてもなめらかです。
実装はこんな感じです。
Gistで貼り付けました。
順に追っていくと、
zoom: 3,
minZoom: 2,
maxZoom: 4
と言うのは拡大パターンは3種類なので、2〜4の3段階としました。
zoomのサイズを一段階あげるとbaseX / baseYはそれぞれ2倍になっていきます。zoom4では画像が4枚で1画像となるため読み込み方が少し複雑になっています。左上の座標が5, 5の場合、それぞれ左上5, 5、右上6, 5、左下6, 5、右下6, 6になります。
画像を表示しているImage.assetの部分はInkWell等でラップするとx, yの値を確認する時に便利です。
今回はアプリ内の画像を読み込んでいるためAssetTileProviderを使っていますが、NetworkTileProviderやCustomTileProviderなどあります。
そしてマップといえば緯度経度はどうなんだと気になったかもしれませんが、今回の内容では触れずとも進めることができたので触れません。
緯度経度(LatLng)をTileのX, Yに変換したり、その逆も可能ですが計算が複雑かつ完全に理解できてないのでまたそのうち(p_-)