LoginSignup
9
3

More than 1 year has passed since last update.

【deck.gl】react-map-glを使って背景地図を表示する

Posted at

はじめに

react-map-glを使って背景地図を表示し、地図コントロール用のボタンを配置しました。
deck.glとの組み合わせでは、ハマりポイントもあって苦戦したので共有します。

環境構築

Reactプロジェクト作成

deck.glはReactとの親和性がよいので、Reactを使います。

TypeScriptを使う場合は こちら を参考にしてください(型チェックでハマりやすいのでJavaScriptがおすすめです。ちなみに公式ドキュメントに型が書いてないのでソースをたどる必要があります・・)

npx create-react-app sample

ライブラリインストール

deck.glをインストールします。

npm install deck.gl --save

react-map-glをインストールします。
MapLibre GL JSを使う場合は、mapbox-glmaplibre-gl に変えてください。

npm install --save react-map-gl mapbox-gl

実装

背景地図表示

deck.glの公式ドキュメントの、MapboxOverlayを参考に実装します。
deck.glを react-map-gl v7のコントロールで使用するには、MapboxOverlayを使用する必要があります。

App.js
import React from 'react';
import { LineLayer } from '@deck.gl/layers';
import { MapboxOverlay } from '@deck.gl/mapbox';
import Map, { useControl } from 'react-map-gl';
// Mapbox GL JS
import 'mapbox-gl/dist/mapbox-gl.css';
import './style.css';

const App = () => {
    const DeckGLOverlay = (props) => {
        const overlay = useControl(() => new MapboxOverlay(props));
        overlay.setProps(props);
        return null;
    };
    // Mapboxアクセストークン
    const MAPBOX_ACCESS_TOKEN = 'your_access_token';
    // 初期ビューポートの設定
    const INITIAL_VIEW_STATE = {
        latitude: 38.875584,
        longitude: 139.7454316,
        bearing: 0,
        pitch: 0,
        zoom: 5,
    };
    // LineLayerで使うデータ
    const data = [{ sourcePosition: [139.7454316, 38.875584], targetPosition: [145.7454316, 40.875584] }];
    // LineLayer
    const layers = [new LineLayer({ id: 'line-layer', data })];
    return (
        <div className="App">
            <Map
                initialViewState={INITIAL_VIEW_STATE}
                mapStyle="mapbox://styles/mapbox/dark-v9"
                style={{ width: '100vw', height: '100vh' }}
                mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
            >
                <DeckGLOverlay layers={layers} />
            </Map>
        </div>
    );
};

export default App;

公式ドキュメントはTypeScriptで書かれていたのでJavaScriptで書き直しました。
公式ドキュメントにはMapのstyleを設定していませんが、設定しないと地図が表示されません。

image.png

背景地図と、deck.glのLineLayerが表示できました!

コントロール用のボタン配置

react-map-glには地図をコントロールするためのパーツがいくつか用意されています。
詳細はAPIリファレンスをご覧ください。

今回は、現在地を表示できるGeolocateControl、全画面モードに切り替えることができるFullscreenControl、ズームイン・ズームアウトができるNavigationControl、スケールを表示できるScaleControlを追加してみます。

実装は簡単です。インポートして、コンポーネントを追加するだけです。

App.js
import React from 'react';
import { LineLayer } from '@deck.gl/layers';
import { MapboxOverlay } from '@deck.gl/mapbox';
import Map, { useControl, NavigationControl, FullscreenControl, ScaleControl, GeolocateControl } from 'react-map-gl';
// Mapbox GL JS
import 'mapbox-gl/dist/mapbox-gl.css';
import './style.css';

const App = () => {
    const DeckGLOverlay = (props) => {
        const overlay = useControl(() => new MapboxOverlay(props));
        overlay.setProps(props);
        return null;
    };
    // Mapboxアクセストークン
    const MAPBOX_ACCESS_TOKEN = 'your_access_token';
    // 初期ビューポートの設定
    const INITIAL_VIEW_STATE = {
        latitude: 38.875584,
        longitude: 139.7454316,
        bearing: 0,
        pitch: 0,
        zoom: 5,
    };
    // LineLayerで使うデータ
    const data = [{ sourcePosition: [139.7454316, 38.875584], targetPosition: [145.7454316, 40.875584] }];
    // LineLayer
    const layers = [new LineLayer({ id: 'line-layer', data })];
    return (
        <div className="App">
            <Map
                initialViewState={INITIAL_VIEW_STATE}
                mapStyle="mapbox://styles/mapbox/dark-v9"
                style={{ width: '100vw', height: '100vh' }}
                mapboxAccessToken={MAPBOX_ACCESS_TOKEN}
            >
                <DeckGLOverlay layers={layers} />
                <GeolocateControl />
                <FullscreenControl />
                <NavigationControl />
                <ScaleControl />
            </Map>
        </div>
    );
};

export default App;

image.png

コントロール用のボタンなどを配置できました!
これらのパーツを簡単に導入できるのは便利ですね。

余談:ハマりポイント

deck.glの公式ドキュメントの、Adding a Base Mapを参考に実装したらハマりました。
実はこれ、おすすめの方法ではありません・・

App.js
import React from 'react';
import DeckGL from '@deck.gl/react';
import { LineLayer } from '@deck.gl/layers';
import { Map } from 'react-map-gl';
// Mapbox GL JS
import 'mapbox-gl/dist/mapbox-gl.css';
import './style.css';

const App = () => {
    // Mapboxアクセストークン
    const MAPBOX_ACCESS_TOKEN = 'your_access_token';
    // 初期ビューポートの設定
    const INITIAL_VIEW_STATE = {
        latitude: 38.875584,
        longitude: 139.7454316,
        bearing: 0,
        pitch: 0,
        zoom: 5,
    };
    // LineLayerで使うデータ
    const data = [{ sourcePosition: [139.7454316, 38.875584], targetPosition: [145.7454316, 40.875584] }];
    // LineLayer
    const layers = [new LineLayer({ id: 'line-layer', data })];
    return (
        <div className="App">
            <DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} layers={layers}>
                <Map mapboxAccessToken={MAPBOX_ACCESS_TOKEN} mapStyle="mapbox://styles/mapbox/dark-v9" />
            </DeckGL>
        </div>
    );
};

export default App;

公式ドキュメントにはimport 'mapbox-gl/dist/mapbox-gl.css'; がありませんが、これがないとattributionなどのスタイルが崩れるのであったほうがいいです。

image.png

こんな感じでdeck.glのレイヤとreact-map-glの背景地図を表示することができました!
しかし、これには問題点があります。
react-map-glで用意されている地図コントロール用のボタンを配置すると、ボタンを押すことができません。

App.js
import React from 'react';
import DeckGL from '@deck.gl/react';
import { LineLayer } from '@deck.gl/layers';
import { Map, useControl, NavigationControl, FullscreenControl, ScaleControl, GeolocateControl } from 'react-map-gl';
// Mapbox GL JS
import 'mapbox-gl/dist/mapbox-gl.css';
import './style.css';

const App = () => {
    // Mapboxアクセストークン
    const MAPBOX_ACCESS_TOKEN = 'your_access_token';
    // 初期ビューポートの設定
    const INITIAL_VIEW_STATE = {
        latitude: 38.875584,
        longitude: 139.7454316,
        bearing: 0,
        pitch: 0,
        zoom: 5,
    };
    // LineLayerで使うデータ
    const data = [{ sourcePosition: [139.7454316, 38.875584], targetPosition: [145.7454316, 40.875584] }];
    // LineLayer
    const layers = [new LineLayer({ id: 'line-layer', data })];
    return (
        <div className="App">
            <DeckGL initialViewState={INITIAL_VIEW_STATE} controller={true} layers={layers}>
                <Map mapboxAccessToken={MAPBOX_ACCESS_TOKEN} mapStyle="mapbox://styles/mapbox/dark-v9">
                    <GeolocateControl />
                    <FullscreenControl />
                    <NavigationControl />
                    <ScaleControl />
                </Map>
            </DeckGL>
        </div>
    );
};

export default App;

image.png

ボタンを押しても無反応です。

開発者ツールを見てみると、
deck-wrapperのz-indexは0になっていますが、mapboxgl-mapのz-indexが-1になっています。
react-map-glのボタンたちは、重なり順が他の要素よりも下になっていたため、押せなかったということです。
image.png

ためしにmapboxgl-mapのz-indexを0にしてみると、ボタンが押せるようになりました。
ですが、ズームイン・ズームアウトができません。
このようにdeck.gl>react-map-gl の並びは不都合があるようです。
image.png

deck.glの公式ドキュメントの、Using with Mapboxにreact-map-glの言及がありました。

To use deck.gl with react-map-gl v7's controls, you must use MapboxOverlay.

日本語訳:
deck.gl を react-map-gl v7 のコントロールで使用するには、MapboxOverlay を使用する必要があります。

react-map-glの最新バージョンはv7なので、当てはまります。
これ、かなりハマりました。Get-Startedにも書いてほしかった

おわりに

ボタンが押せない問題でかなり苦戦してしまいましたが、公式ドキュメントを漁ると解決策が書いてあったので無事に解決しました。
この手のトラップは多いです。。困ったら公式ドキュメントを見るクセをつけましょう。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3