Leafletの拡張
Leafletには、文字通り数百ものプラグインが存在します。これらはLeafletの機能を拡張します。一般的な方法の場合もあれば、非常にユースケース特化型の場合もあります。
非常に多くのプラグインが存在する理由の一つに、Leafletが容易に拡張できることが挙げられます。このチュートリアルでは、最も一般的に使用される拡張方法について説明します。
このチュートリアルは、以下の内容をよく理解していることを前提としています。
- JavaScript
- DOM操作
- オブジェクト指向プログラミング(クラス、インスタンス、継承、メソッド、プロパティなどの概念の理解)
Leafletアーキテクチャ
Leaflet 1.0.0の簡略化されたUMLクラス図を見てみましょう。60以上のJavaScriptクラスがあるので、図は少し大きくなります。L.ImageOverlay
でズーム可能な画像を作成できます。
この例をスタンドアロンで参照してください。 |
技術的な観点から見ると、Leafletはさまざまな方法で拡張できます。
- 最も一般的な方法:
L.Class.extend()
を使用して、L.Layer
、L.Handler
、またはL.Control
の新しいサブクラスを作成する。- レイヤーは、マップの移動/ズーム時に移動します。
- ハンドラーは非表示であり、ブラウザイベントを解釈します。
- コントロールは固定されたインターフェース要素です。
L.Class.include()
を使用して、既存のクラスにさらに機能を追加する。- 新しいメソッドとオプションを追加する。
- いくつかのメソッドを変更する。
addInitHook
を使用して、追加のコンストラクタコードを実行する。
L.Class.include()
を使用して、既存のクラスの一部(クラスメソッドの動作の置き換え)を変更する。
このチュートリアルでは、Leaflet 1.0.0でのみ使用可能なクラスとメソッドの一部について説明しています。以前のバージョン用のプラグインを開発する場合は注意してください。
L.Class
JavaScriptはやや奇妙な言語です。真のオブジェクト指向言語ではなく、むしろプロトタイプ指向言語です。このため、歴史的にJavaScriptでは、古典的なOOPの意味でのクラス継承が困難でした。
Leafletは、クラス継承を容易にするL.Class
を使用してこの問題を回避しています。
最新のJavaScriptではES6クラスを使用できますが、Leafletはそれらを基盤として設計されていません。
L.Class.extend()
Leaflet内の任意のもののサブクラスを作成するには、.extend()
メソッドを使用します。これは、キーと値のペアを持つプレーンオブジェクトを1つのパラメーターとして受け取ります。各キーはプロパティまたはメソッドの名前であり、各値はプロパティの初期値、またはメソッドの実装です。
var MyDemoClass = L.Class.extend({
// A property with initial value = 42
myDemoProperty: 42,
// A method
myDemoMethod: function() { return this.myDemoProperty; }
});
var myDemoInstance = new MyDemoClass();
// This will output "42" to the development console
console.log( myDemoInstance.myDemoMethod() );
クラス、メソッド、プロパティの名前付けには、次の規則に従ってください。
- 関数、メソッド、プロパティ、ファクトリの名前は
lowerCamelCase
にする必要があります。 - クラス名は
UpperCamelCase
にする必要があります。 - プライベートプロパティとメソッドはアンダースコア(
_
)で始まります。これによりプライベートになるわけではなく、開発者が直接使用しないことを推奨するだけです。
L.Class.include()
クラスが既に定義されている場合、既存のプロパティ/メソッドを再定義したり、.include()
を使用して新しいプロパティ/メソッドを追加したりできます。
MyDemoClass.include({
// Adding a new property to the class
_myPrivateProperty: 78,
// Redefining a method
myDemoMethod: function() { return this._myPrivateProperty; }
});
var mySecondDemoInstance = new MyDemoClass();
// This will output "78"
console.log( mySecondDemoInstance.myDemoMethod() );
// However, properties and methods from before still exist
// This will output "42"
console.log( mySecondDemoInstance.myDemoProperty );
L.Class.initialize()
OOPでは、クラスはコンストラクタメソッドを持ちます。LeafletのL.Class
では、コンストラクタメソッドは常にinitialize
という名前になります。
クラスに特定のoptions
がある場合は、コンストラクタでL.setOptions()
を使用して初期化することをお勧めします。このユーティリティ関数は、提供されたオプションとクラスのデフォルトオプションをマージします。
var MyBoxClass = L.Class.extend({
options: {
width: 1,
height: 1
},
initialize: function(name, options) {
this.name = name;
L.setOptions(this, options);
}
});
var instance = new MyBoxClass('Red', {width: 10});
console.log(instance.name); // Outputs "Red"
console.log(instance.options.width); // Outputs "10"
console.log(instance.options.height); // Outputs "1", the default
Leafletはoptions
プロパティを特別な方法で処理します。親クラスで使用可能なオプションは、子クラスによって継承されます。
var MyCubeClass = MyBoxClass.extend({
options: {
depth: 1
}
});
var instance = new MyCubeClass('Blue');
console.log(instance.options.width); // Outputs "1", parent class default
console.log(instance.options.height); // Outputs "1", parent class default
console.log(instance.options.depth); // Outputs "1"
子クラスが親のコンストラクタを実行してから独自のコンストラクタを実行することは非常に一般的です。Leafletでは、これはL.Class.addInitHook()
を使用して実現されます。このメソッドは、クラスのinitialize()
の直後に実行される初期化関数を「フック」するために使用できます。
MyBoxClass.addInitHook(function(){
this._area = this.options.width * this.options.length;
});
initialize()
が呼び出された後(setOptions()
を呼び出します)に実行されます。これは、initフックの実行時にthis.options
が存在し、有効であることを意味します。
addInitHook
には、メソッド名を使用し、メソッド引数を埋め込むことができる代替構文があります。
MyCubeClass.include({
_calculateVolume: function(arg1, arg2) {
this._volume = this.options.width * this.options.length * this.options.depth;
}
});
MyCubeClass.addInitHook('_calculateVolume', argValue1, argValue2);
親クラスのメソッド
親クラスのメソッドを呼び出すには、親クラスのプロトタイプにアクセスしてFunction.call(…)
を使用します。これは、たとえばL.FeatureGroup
のコードで見ることができます。
L.FeatureGroup = L.LayerGroup.extend({
addLayer: function (layer) {
…
L.LayerGroup.prototype.addLayer.call(this, layer);
},
removeLayer: function (layer) {
…
L.LayerGroup.prototype.removeLayer.call(this, layer);
},
…
});
親のコンストラクタを呼び出すのは同様の方法で行いますが、ParentClass.prototype.initialize.call(this, …)
を使用します。
ファクトリ
ほとんどのLeafletクラスには、対応するファクトリ関数があります。ファクトリ関数はクラスと同じ名前を持ちますが、UpperCamelCase
ではなくlowerCamelCase
です。
function myBoxClass(name, options) {
return new MyBoxClass(name, options);
}
命名規則
Leafletプラグインのクラスに名前を付ける際は、次の命名規則に従ってください。
- プラグインでグローバル変数を公開しないでください。
- 新しいクラスがある場合は、
L
名前空間に直接配置します(L.MyPlugin
)。 - 既存のクラスを継承する場合は、サブプロパティにします(
L.TileLayer.Banana
)。