Leaflet.PixiOverlay その２many-markers


その１：[Leaflet.PixiOverlay その１マーカーの表示 - Qiita](https://qiita.com/sugasaki/items/cc50fe09a98fcd219719#demo---draw-a-marker)
その２：[Leaflet.PixiOverlay その２many-markers - Qiita](https://qiita.com/sugasaki/items/9b6eb6ca70613b3d47e3)


# はじめに

LeafletとWebGLのライブラリであるPixi.jsの橋渡しをするLeaflet.PixiOverlayを使って、many-markersをやってみます。

[Leaflet.PixiOverlay](https://github.com/manubb/Leaflet.PixiOverlay)


![image](https://qiita-image-store.s3.amazonaws.com/0/49910/8d27446f-d05a-4eb5-d5e8-06a82e678b53.png)
拡大するとこんな沢山のマーカーでした。
![image](https://qiita-image-store.s3.amazonaws.com/0/49910/a921bf66-6dbf-b434-a6d2-198b198ad9a1.png)


# many-markers

Githubに置いてある[many-markers.html](https://github.com/manubb/Leaflet.PixiOverlay/blob/master/docs/many-markers.html)をやってみます。

## 関連JSのインストール

* bezier-easing
many-markersを動かすにはbezier-easingが必要なようです。[gre/bezier-easing](https://github.com/gre/bezier-easing)

以下のコマンドでインストールします。
`> npm i bezier-easing --save`

* MarkerContainer.js
Githubリポジトリの[docs/js/MarkerContainer.js](https://github.com/manubb/Leaflet.PixiOverlay/blob/master/docs/js/MarkerContainer.js)も必要になるのでプロジェクトにコピーし配置します。


## index.html

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

```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="./node_modules/bezier-easing/dist/bezier-easing.min.js"></script>
<script src="./js/MarkerContainer.js"></script>

<script src="./js/many-markers.js"></script>
<script src="./js/index.js"></script>

</html>
```

## JS部分

```js
function manyMarker() {
    var map = L.map('map').setView([48.838565, 2.449264526367], 13);
    L.tileLayer('//stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', {
        subdomains: 'abcd',
        attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>, under <a href="http://www.openstreetmap.org/copyright">ODbL</a>.',
        minZoom: 4,
        maxZoom: 18
    }).addTo(map);

    //マーカー最大個数
    var markersLength = 1000000;


    function getRandom(min, max) {
        return min + Math.random() * (max - min);
    }

    //Draw a marker
    var loader = new PIXI.loaders.Loader();
    loader.add('marker', '../img/marker-icon.png'); //リソースにmarkerという名で'img/marker-icon.png'を登録
    loader.load(function (loader, resources) {  //リソース(marker)をロードする
        var texture = resources.marker.texture;

        var pixiContainer = new PIXI.Container();
        var innerContainer = new PIXI.particles.ParticleContainer(markersLength, { vertices: true });
        innerContainer.texture = texture;
        innerContainer.baseTexture = texture.baseTexture;
        innerContainer.anchor = { x: 0.5, y: 1 };
        pixiContainer.addChild(innerContainer);

        var firstDraw = true;
        var prevZoom;
        var initialScale;

        var pixiOverlay = L.pixiOverlay(function (utils) { //Leafletでズームやパンを行う度にコールされます。ドラッグ中はコールされない
            var zoom = utils.getMap().getZoom(); //Leafletのズーム率  ex. 0～18
            var container = utils.getContainer();
            var renderer = utils.getRenderer();
            var project = utils.latLngToLayerPoint; //Leaflet座標系LatLngからオーバーレイの座標系に投影されたL.Pointを返す。
            var scale = utils.getScale();
            var invScale = 1 / scale;

            if (firstDraw) {
                var origin = project([(48.7 + 49) / 2, (2.2 + 2.8) / 2]);
                innerContainer.x = origin.x;
                innerContainer.y = origin.y;
                initialScale = invScale / 8;
                innerContainer.localScale = initialScale;
                for (var i = 0; i < markersLength; i++) {
                    var coords = project([getRandom(48.7, 49), getRandom(2.2, 2.8)]);
                    // our patched particleContainer accepts simple {x: ..., y: ...} objects as children:
                    innerContainer.addChild({
                        x: coords.x - origin.x,
                        y: coords.y - origin.y
                    });
                }
            }

            if (firstDraw || prevZoom !== zoom) {
                innerContainer.localScale = zoom < 8 ? 0.1 : initialScale;// 1 / scale;
            }

            firstDraw = false;
            prevZoom = zoom;
            renderer.render(container); //オーバーレイ上にあるオブジェクトの再描画する。
        }, pixiContainer);

        pixiOverlay.addTo(map);
    });
}
```

## コード解説

### Loader
Loader部分はその１とあまり変わりません。
唯一、documentのDOMContentLoadedイベント発火後にloader.loadが実行されるようになっています。（この意図はいまいちわからず）


>            map.attributionControl.setPosition('bottomleft');
>            map.zoomControl.setPosition('bottomright');

zoomControlとLeaflet表記の位置が変更されています。



### ParticleContainer

たくさんのマーカーを扱う為にParticleContainerを使っています。

[公式ヘルプ#ParticleContainer](http://pixijs.download/dev/docs/PIXI.particles.ParticleContainer.html)には以下の記述があります。

>The ParticleContainer class is a really fast version of the Container built solely for speed, so use when you need a lot of sprites or particles. The tradeoff of the ParticleContainer is that most advanced functionality will not work. ParticleContainer implements the basic object transform (position, scale, rotation) and some advanced functionality like tint (as of v4.5.6). Other more advanced functionality like masking, children, filters, etc will not work on sprites in this batch.
>ParticleContainerクラスは、スピード専用に作成されたコンテナの高速バージョンです。スプライトやパーティクルがたくさん必要な場合に使用します。 ParticleContainerのトレードオフは、最も高度な機能が動作しないことです。 ParticleContainerは、基本オブジェクトの変換（位置、スケール、回転）と色調のような高度な機能を実装しています（v4.5.6以降）。 マスキング、子供、フィルタなどのその他の高度な機能は、このバッチのスプライトでは機能しません。


###  L.pixiOverlay
> var pixiLayer = (function () {
>     中略
>     return L.pixiOverlay(function (utils, event) {
> })();

その１でpixiOverlayで作成したレイヤーをpixiOverlayに格納していましたが、
これと書き方が違いますが、これと同様の事を行っています。pixiOverlayをpixiLayerに代入。

> var pixiOverlay = L.pixiOverlay(function (utils) { 
>     中略
> }, pixiContainer);

違いはズーム時の微妙な挙動を付けているところです。



### PIXI.ticker.Ticker