Leaflet.PixiOverlay その1マーカーの表示


その1:Leaflet.PixiOverlay その1マーカーの表示 - Qiita
その2:Leaflet.PixiOverlay その2many-markers - Qiita

コードは以下に保存しました。
sugasaki/Leaflet.PixiOverlay.Trial

はじめに

LeafletとWebGLのライブラリであるPixi.jsの橋渡しをするLeaflet.PixiOverlayを使って、マーカーの表示をやってみます。

Leaflet.PixiOverlay

Leaflet.PixiOverlayはteralytics/Leaflet.D3SvgOverlay: Leaflet Plugin: D3 SVG Overlayからインスパイアされたプロジェクトのようです。

すごく沢山のマーカーを置いたり、ポリゴンを置いてもサクサク動作します。


拡大するとこんな沢山のマーカーでした。

環境作成

以下で作成したプロジェクトファイルをベースにします。
Leaflet はじめの一歩 - Qiita

pixi-js インストール

webGLのレンダリングにPixiJSを使っているので、まずはpixi-js インストール

> npm install pixi.js --save

PixiJS v4
pixijs/pixi.js: The HTML5 Creation Engine: Create beautiful digital content with the fastest, most flexible 2D WebGL renderer.

Index.htmlにスクリプトタグを追加

<script src="./node_modules/pixi.js/dist/pixi.min.js"></script>

leaflet-pixi-overlay インストール

leaflet-pixi-overlay インストール

> npm install leaflet-pixi-overlay --save

Index.htmlにスクリプトタグを追加

<script src="./node_modules/leaflet-pixi-overlay/L.PixiOverlay.min.js"></script>

Examples - Draw a marker

以下のGithubに書いてあるExamples→Draw a markerをやってみます。
manubb/Leaflet.PixiOverlay: Bring Pixi.js power to Leaflet maps

index.html

index.htmlを以下の内容にしました。

index.html
<html>
<head>
    <title>Leaflet</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="./node_modules/leaflet/dist/leaflet.css" />
</head>

<body style="height: 100%; margin: 0; overflow: hidden;">
    <div id="map" style="height: 100%; width: 100%;"></div>
</body>

<script src="./node_modules/leaflet/dist/leaflet.js"></script>
<script src="./node_modules/pixi.js/dist/pixi.min.js"></script>
<script src="./node_modules/leaflet-pixi-overlay/L.PixiOverlay.min.js"></script>
<script src="./js/index.js"></script>
</html>

スクリプト用のJSファイルを追加

スクリプト用のJSファイル(index.js)を追加します。
ファイルにDraw a markerの内容を打ち込みます。

index.js
var map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);

//Draw a marker
var loader = new PIXI.loaders.Loader();
loader.add('marker', 'img/marker-icon.png');
loader.load(function (loader, resources) {
    var markerTexture = resources.marker.texture;
    var markerLatLng = [51.5, -0.09];
    var marker = new PIXI.Sprite(markerTexture);
    marker.anchor.set(0.5, 1);

    var pixiContainer = new PIXI.Container();
    pixiContainer.addChild(marker);

    var firstDraw = true;
    var prevZoom;

    var pixiOverlay = L.pixiOverlay(function (utils) {
        var zoom = utils.getMap().getZoom();
        var container = utils.getContainer();
        var renderer = utils.getRenderer();
        var project = utils.latLngToLayerPoint;
        var scale = utils.getScale();

        if (firstDraw) {
            var markerCoords = project(markerLatLng);
            marker.x = markerCoords.x;
            marker.y = markerCoords.y;
        }

        if (firstDraw || prevZoom !== zoom) {
            marker.scale.set(1 / scale);
        }

        firstDraw = false;
        prevZoom = zoom;
        renderer.render(container);
    }, pixiContainer);

    pixiOverlay.addTo(map);
});

結果

マーカーが表示されました。

Leafletのマーカー機能よりもだいぶコード量が増えてますが、WebGLで描画するためにやむなしといったところでしょうか。

コード解説

loader

var loader = new PIXI.loaders.Loader();
loader.add('marker', 'img/marker-icon.png');
loader.load(function (loader, resources) {

loaderはコードみればだいたい分かりますが、img/marker-icon.png の読み込みが完了したら処理を行うという感じ

公式ヘルプ#loaders

This namespace contains APIs which extends the resource-loader module for loading assets, data, and other resources dynamically.
この名前空間(PIXI.loaders)は、assets、data、その他のリソースを動的にロードするための、resource-loaderモジュールを拡張するAPIが含まれています。

texture

var markerTexture = resources.marker.texture;
var markerLatLng = [51.5, -0.09];
var marker = new PIXI.Sprite(markerTexture);

markerTextureへ代入、緯度経度を設定、Spriteを作成する。

anchor

marker.anchor.set(0.5, 1);

anchorは公式ヘルプ#anchorを見ると原点の場所のようです。

The anchor sets the origin point of the texture. The default is 0,0 this means the texture's origin is the top left Setting the anchor to 0.5,0.5 means the texture's origin is centered Setting the anchor to 1,1 would mean the texture's origin point will be the bottom right corner
anchorはテクスチャの原点を設定します。 デフォルトは0,0です。これは、テクスチャの原点が左上であることを意味します。
アンカーを0.5, 0.5に設定すると、テクスチャの原点がセンタリングされることを意味します。アンカーを1,1に設定すると、テクスチャの原点が右下になります。

という事で、marker.anchor.set(0.5, 1) の場合には原点が、X:中央 Y:下 であり、マーカー の尖ったところを原点として設定しているということになります。

Container

公式ヘルプ#Container

A Container represents a collection of display objects. It is the base class of all display objects that act as a container for other objects.
コンテナは、表示オブジェクトのコレクションを表します。 これは他のオブジェクトのコンテナとして機能するすべての表示オブジェクトの基本クラスです。

コンテナを作って、表示したいオブジェクトをコンテナに突っ込めば表示してくれるようです。

フラグ類

    var firstDraw = true;
    var prevZoom;

L.pixiOverlay

以下の構文でoverlayレイヤーを作成します。

    //第1引数に処理関数を、第2引数にPIXIのコンテナを指定します。
    var pixiOverlay = L.pixiOverlay(function(utils) {
        // your drawing code here
    }, new PIXI.Container());

第1引数に渡した処理関数は、Leafletでズームやパンを行う度にコールされます。
ドラッグ中はコールされないです。
よって、

1回目の処理(初回処理)では、latLngToLayerPointでオーバーレイ座標系の位置を算出し、スケールをセットしている。

マーカーのサイズを常に倍率1にする為に、
Leafletのズーム率が変われば、markerスケールをセットしなおしている。以下の部分。

        if (firstDraw || prevZoom !== zoom) {
            marker.scale.set(1 / scale);  //マーカーのサイズを常に倍率1にする。
        }

最後にフラグを変更し、ズーム率を保存している。

render()でオーバーレイ上にあるオブジェクトの再描画を行っている。
renderer.render(container);
再描画を行わない場合、パンした場合に座標が追従しない。(オーバーレイ座標系の位置が変わらない)

latLngToLayerPoint(latLng, zoom?)

returns L.Point projected from L.LatLng in the coordinate system of the overlay.
Leaflet座標系LatLngからオーバーレイの座標系に投影されたL.Pointを返します。

var markerCoords = project(markerLatLng); の場合、
markerLatLng = [51.5, -0.09] → markerCoords[ x: 65503.232, y: 43589.11367487424]

pixiOverlay.addTo(map);

pixiOverlayの機能でLeaflet Mapに追加します。
map.addLayer(this);をラップしています。

manubb/Leaflet.PixiOverlay: Bring Pixi.js power to Leaflet maps

おわりに

コード解説は以上です。
コードは以下に保存しました。
sugasaki/Leaflet.PixiOverlay.Trial

このやり方がベスト解というわけではなく、Pixiを使って他にもやり方がありそうです。

冒頭の沢山のマーカーを表示するサンプルmany-markers.htmlではまた違ったやり方がなされています。

次回はmany-markers.htmlについて掘り下げていきます。

その2:Leaflet.PixiOverlay その2many-markers - Qiita