Leafletプラグイン > L.PixiOverlayをTypeScript環境で使う


初めに

TS+ReactでLeafletを使うプロジェクトにおいて、L.PixiOverlayプラグインを使ってみました。

TypeScriptを使う?使わないで気持ちが揺れている中でトライしてみましたが、試行錯誤がしんどかったです。

この作業をプラグインを追加する度にやるのが正しいのかどうかが確証を得れていません。

とはいえ工数を考えると、LeafetとLeafletプラグインをガンガン試しながら使ってフィードバックを出していくプロジェクトでは、TypeScriptを使わない方がいいのかなというのが正直なところです。

このへんは以下の「TypeScriptを使わないほうが良いケース」にあてはまるのかなと思っています。
TypeScriptを使った方がいいケースとは? | POSTD

ASP.NET Core 2 React.js Template with ES6 (not TypeScript)

Why isn't typescript used more in the react community? : reactjs

処理の流れ

パッケージ追加

package.jsonに以下を追加

package.json
    "leaflet-pixi-overlay": "^1.5.1",
    "pixi.js": "^4.8.0",
    "@types/pixi.js": "4.7.4",

typeScript用のd.tsファイルを作成

ソースフォルダにleaflet-pixi-overlay.d.tsを作成します。
中身を以下のようにしました。

leaflet-pixi-overlay.d.ts
import * as L from 'leaflet'
import * as PIXI from 'pixi.js';

declare module 'leaflet' {
    interface pixiOverlay {
        new(callback: (utils: L.pixiOverlayUtils) => void,
            pixiContainer: PIXI.Container,
            options?: PixiOverlayOptions): any;
        addTo(map: L.Map): pixiOverlay;
    }

    interface pixiOverlayUtils {
        latLngToLayerPoint(_wgsOrigin: L.LatLng): any;
        layerPointToLatLng(): any;
        getScale(): any;
        getRenderer(): PIXI.WebGLRenderer;
        getContainer(): PIXI.Container;
        getMap(): L.Map;
    }

    interface PixiOverlayOptions {
    }

    function pixiOverlay(callback: (utils: L.pixiOverlayUtils) => void,
        pixiContainer: PIXI.Container,
        options?: PixiOverlayOptions): pixiOverlay;

}

manubb/Leaflet.PixiOverlayのExamples>Draw a markerを動く最低限の記述にしています。

srcフォルダでもいいですし、参考のように、node_modules/leaflet-pixi-overlayの配下にL.PixiOverlay.d.tsを作成してもいいでしょうし、
node_modules/@types/の下に、leaflet-pixi-overlayフォルダを作成し、index.d.tsファイルにしてもいいでしょうが、管理が煩雑になると思います。

私は作った事を忘れると思ってやめました。

使い方

importでPIXIとeaflet-pixi-overlayを読み込みます。

import * as PIXI from 'pixi.js';
import 'leaflet-pixi-overlay'

あとはサンプルとほぼ同じです。
以下になりました。

import * as React from 'react';

//leaflet
import 'leaflet/dist/leaflet.css';
import * as L from 'leaflet';

//pixi-overlay
import * as PIXI from 'pixi.js';
import 'leaflet-pixi-overlay'



interface ResourceWithMarker extends PIXI.loaders.Resource {
    marker: PIXI.loaders.Resource;
}



export class MapWorldPixi extends React.Component<{}, {}> {

    componentDidMount() {
        // create the Leaflet map object
        this.init();
    }


    private init() {

        //// this function creates the Leaflet map object and is called after the Map component mounts
        let map = L.map('mapUI').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);



        let loader = new PIXI.loaders.Loader();
        loader.add('marker', 'marker-icon.png'); //リソースにmarkerという名で'img/marker-icon.png'を登録

        loader.load(function (loader: PIXI.loaders.Loader, resource: ResourceWithMarker) {  //リソース(marker)をロードする
            var markerTexture = resource.marker.texture;


            var markerLatLng: L.LatLng = new L.LatLng(51.5, -0.09);  //[51.5, -0.09];
            var marker = new PIXI.Sprite(markerTexture);
            marker.anchor.set(0.5, 1); //原点をX:中央 Y:下 に設定

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

            var firstDraw = true;
            var prevZoom: number;


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


                if (firstDraw) {
                    var markerCoords = project(markerLatLng); //Leaflet座標系LatLngからオーバーレイの座標系に投影されたL.Pointを返す。
                    marker.x = markerCoords.x; //ex. markerLatLng[0] 51.5 → 65503.232
                    marker.y = markerCoords.y; //ex. markerLatLng[1] -0.09 → 43589.11367487424
                }

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

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

            }, pixiContainer);
           pixiOverlay.addTo(map);
        });

    }


    public render() {
        return <div id="mapUI">
            <div id='map'></div>
        </div>;
    }

}

以下のような画面がでれば成功

参考

How do I add Typescript definitions for Leaflet plugins - Stack Overflow