はじめに
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-gl
をmaplibre-gl
に変えてください。
npm install --save react-map-gl mapbox-gl
実装
背景地図表示
deck.glの公式ドキュメントの、MapboxOverlayを参考に実装します。
deck.glを react-map-gl v7のコントロールで使用するには、MapboxOverlayを使用する必要があります。
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
を設定していませんが、設定しないと地図が表示されません。
背景地図と、deck.glのLineLayerが表示できました!
コントロール用のボタン配置
react-map-glには地図をコントロールするためのパーツがいくつか用意されています。
詳細はAPIリファレンスをご覧ください。
今回は、現在地を表示できるGeolocateControl
、全画面モードに切り替えることができるFullscreenControl
、ズームイン・ズームアウトができるNavigationControl
、スケールを表示できるScaleControl
を追加してみます。
実装は簡単です。インポートして、コンポーネントを追加するだけです。
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;
コントロール用のボタンなどを配置できました!
これらのパーツを簡単に導入できるのは便利ですね。
余談:ハマりポイント
deck.glの公式ドキュメントの、Adding a Base Mapを参考に実装したらハマりました。
実はこれ、おすすめの方法ではありません・・
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などのスタイルが崩れるのであったほうがいいです。
こんな感じでdeck.glのレイヤとreact-map-glの背景地図を表示することができました!
しかし、これには問題点があります。
react-map-glで用意されている地図コントロール用のボタンを配置すると、ボタンを押すことができません。
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;
ボタンを押しても無反応です。
開発者ツールを見てみると、
deck-wrapperのz-indexは0になっていますが、mapboxgl-mapのz-indexが-1になっています。
react-map-glのボタンたちは、重なり順が他の要素よりも下になっていたため、押せなかったということです。
ためしにmapboxgl-mapのz-indexを0にしてみると、ボタンが押せるようになりました。
ですが、ズームイン・ズームアウトができません。
このようにdeck.gl>react-map-gl の並びは不都合があるようです。
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にも書いてほしかった
おわりに
ボタンが押せない問題でかなり苦戦してしまいましたが、公式ドキュメントを漁ると解決策が書いてあったので無事に解決しました。
この手のトラップは多いです。。困ったら公式ドキュメントを見るクセをつけましょう。