いきなり日曜大工

好きなことを好きなだけやる

Leaflet 拡張基礎編 #1 Leaflet 拡張ことはじめ

Leaflet 工作はじめます

前回で本番用の地図環境が完成しました。今はまっさらな地図を拡大縮小して見れるだけなので、これからいろんな機能や情報をのせて Google Maps っぽくしていきます。

独自の機能がほしい

最初に地図に追加したい機能は URL に緯度経度とズームレベル情報を含める、Google Maps みたいなアレです。

https://www.google.co.jp/maps/@34.96976,135.756195,17z

Leaflet 本体にはこういう機能はないので、自作します。どうせなら今後も使いまわせるようにプラグインっぽくしよう。というわけで今回から Leaflet の拡張(独自クラスの作り方)について学び、続いてマイ拡張機能を作っていきます。

※ちなみに URL にハッシュフラグメントをつけるやり方であれば公式サイトの Pluginsleaflet-hash というものが紹介されています。

Leaflet 拡張ことはじめ

Leaflet はシンプル というポリシーなので、何か機能を追加したければプラグインを利用するか自分で書くことになりますが、Leaflet にはそのために拡張を簡単にするインタフェースが用意されています。

拡張ガイドを読む

とっかかりとして公式サイト Tutorials にある拡張ガイド(3つある)を読みます。

拡張のパターン

Extending Leaflet: Class Theory から、Leaflet の拡張は技術的に分類するとこんなふうに分けられるとあります(拙訳、不明な点は原文をご覧ください)。

  • 最たるもの: L.Layer, L.Handler, L.Control の新しいサブクラスを L.Class.extend() で作る
    • Layers – マップを動かしたりズームレベルを変更したときのレイヤーの挙動
    • Handlers – 見えないところのイベントを処理
    • Controls – 固定されたインタフェース要素
  • L.Class.include() で既存のクラスに機能を追加
    • 新しいメソッドやオプション(プロパティ)を追加
    • メソッドの改変
    • addInitHook を使ってコンストラクタで追加コードを実行
  • L.Class.include() を使った既存のクラスの変更(メソッドの役割の置き換え)

参考:Leaflet のクラス図

これによると、拡張をするときは L.Layer, L.Handler, L.Control の3つのクラスのサブクラスを作るケースが「最たる」ものとあります。つまり地図に何か機能を追加したくなったとき、この3つのどれかのサブクラスを作って実装することであらかた間に合う、ということのようです。拡張ガイドにも特にこの3つのクラスについて具体的な拡張の事例が紹介されています。

そういえばこれまで地図以外の画像を貼る練習ばっかりで Leaflet を実はあまりちゃんと触っていなかったような気もするので、この3つのクラスについて少々勉強して、拡張を念頭においたメモにしました。

L.Layer

L.Layer は地図(タイル)と地物(アイコンとか)のベースとなるクラスです。
Leaflet を使うとき、多くの場合は L.TileLayer で地図タイルを読み込み、L.Marker でその上にマーカーを置いて…とすると思いますが、この L.TileLayer も L.Marker も地図コンテナ L.Map の中に重ね合わせて配置される L.Layer のサブクラス、上でいうところの “Layers” です。
これら L.TileLayer や L.Marker などの挙動を独自のものにしたいときに、これらのサブクラスを作る(つまりそれは L.Layer の子孫)ということになります。
チュートリアルに L.TileLayer の拡張事例 があります。

L.Control

L.Control は、地図操作 UI(ズームコントローラのボタン)や、常に表示されるべき情報(地図タイル提供元のクレジット)などの画面上の決まった位置に固定される「要素(実体は DOM エレメント)」をつかさどるクラスです。
L.Control を拡張して作れそうな例には、地図操作 UI(フルスクリーン切替、レイヤー表示切替などのボタン)や、地物の詳細情報を表示させる情報パネル、メニューやヘルプなどの入口となる UI、検索窓など幅広い用途が考えられます。
L.Control の拡張事例: シンプルな例L.Class.extend() なし

L.Handler

L.Handler は、イベントのハンドリングを扱う汎用クラスです。画面上に用意された UI 以外に起因するイベントのハンドリングなどに使います。
例えば地図をキーボード操作したいときにリスナー登録をして入力を受け付けたりとか、今回作りたいと思っている地図操作に伴う URL 書き換えもこれを使うのがよさそうです。
L.Handler の拡張事例、PC だと何してるかわからないかも。

L.Class

では、継承を容易にする L.Class について再び Extending Leaflet: Class Theory から。

Leaflet が提供する多くのクラスは、基本クラス L.Class を 継承します。
この L.Class には拡張(継承や再定義)を簡単にするいくつかのメソッドが用意されているので、これを継承する各クラスも簡単に拡張が行える、という仕組みです。

L.Class の便利なメソッド

  • 既存のクラスを継承するときは L.Class.extend() を使う
  • L.Class.extend() で継承されるクラスのコンストラクタは L.Class.extend() に引数として渡すオブジェクトのプロパティ initialize に定義する
  • 既存のクラスの再定義(プロパティの追加または上書き)をするときは L.Class.include() を使う
  • 既存のクラスのコンストラクタを拡張(追加コードを実行)するときは L.Class.addInitHook() を使う

各メソッドの実際の使用例は原文についています。

サブクラス生成時のおやくそく

あとはサブクラスを作るにあたって

  • プロパティ(メソッド含)は lowerCamelCase で
  • プライベートなプロパティは _ から始めて
    (保証にはならないけど読む人に知らせようという意味で)
  • クラス名は 名前空間 L に含めて UpperCamelCase で
  • そのファクトリー関数はクラス名の lowerCamelCase で

などいろいろ書いてあります。
(自分は油断するとうっかり snake_case で書いてしまう人間であります)

まとめ

  • 基本クラス L.Class はクラスの継承・再定義を簡単にするメソッドをもっている
  • Leaflet のほとんどのクラスは L.Class を継承している(ので子孫クラスも同じく拡張が容易である)
  • Leaflet に新しい機能を実装するときは、多くの場合 L.Layer, L.Control, L.Handler のどれかを拡張することになる

今回はこのへんで。次回、実際に書いてみます。