Uber社製、ジオビジュアライゼーションフレームワーク「deck.gl」を触ってみた。
deck.glとは?
Uber社がオープンソースとして公開しているWebGLベースの地理情報視覚化フレームワークです。webGLを利用しているため、比較的データ量の多い地理情報もフロントエンドに表示することができます。
Reactのコンポーネントとして作成されているため、いままで手を出して来なかったのですが、最近Reactを勉強し始めたついでに弄ってみました。
今回は、とりあえず開発環境の構築と、サンプルにあるgeojsonデータを読み込んで表示するコンポーネントを作ってみます。
この記事は内容が古くなっています。
最新のDeck.glについては、以下の記事を参照してください。
React Hooks とdeck.gl v.7を使ってweb地図を作ってみた。
deck.glのインストール
deck.glリポジトリをcloneして開発を始めることもできますが、個人的にcreate-react-appコマンドを使いたかったので、生成したテンプレートにdeck.glを読み込んでカスタムコンポーネントを作成します。
まず、テンプレートを生成します。
1 2 3 |
$ create-react-app deckgl-example $ cd deckgl_example $ yarn install |
続いて、deck.glをインストールします。
deck.glは単体で利用できるコンポーネントですが、公式のサンプルではUber社が公開している他のコンポーネントも利用しているので、それら必要なもの全てをインストールします。
1 2 3 |
$ yarn add deck.gl $ yarn add luma.gl $ yarn add react-map-gl |
それぞれ以下の機能が実装されています。
- deck.gl
地理情報を視覚化するコンポーネント - luma.gl
webGL APIをreactから使いやすくしたwrapper - react-map-gl
MapboxGLをreactで利用するためのコンポーネント
他、geoJSONサンプルではD3.jsのrequestモジュールが利用されているので、こちらもインストールします。
1 |
$ yarn add d3-request |
今回のサンプルでは、deck.gl & luma.gl で読み込んだGeoJSONデータを視覚化し、react-map-glでベースマップを表示します。
d3-requestはデータの読み込みに使います。
react-map-glを利用するには、Mapboxで開発者トークンを取得しておく必要があります。
カスタムコンポーネントを作る
今回は、deck.glを利用してgeoJSONデータを元に3Dポリゴンを表示するMapsコンポーネントを作成します。
カスタムコンポーネント構成
作成するカスタムコンポーネントは、src フォルダ配下にcomponentsフォルダを作成し、そこに収納します。
最終的なファイル構成は以下。
1 2 3 4 5 6 |
src └── components └── Maps ├── Maps.js ├── deckgl-overlay.js └── index.js |
地理データを用意する
次にpubulicフォルダの中に、dataフォルダを作成し今回視覚化するgeoJSONを保存します。
今回はOSMから大阪のビルディングデータ(ポリゴン)を一部切り出して、テストデータを作成しました。
1 2 3 |
public ├── data └── osaka.geojson |
コーディング開始
ここから、コーティング(コピペ)に入っていきます。
components/Maps/index.js
まず、はじめにindex.jsを作成します。
1 2 |
import Maps from './Maps.js'; export default Maps; |
これ自体は、たんなるエントリー用のスクリプトです。
components/Maps/Maps.js
次に、コンポーネントの本体であるMaps.jsを作成します。
基本は、上記example内のApp.jsをコピペしただけのものです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
import React, {Component} from 'react'; import {render} from 'react-dom'; import MapGL from 'react-map-gl'; import DeckGLOverlay from './deckgl-overlay.js'; import {json as requestJson} from 'd3-request'; // Set your mapbox token here const MAPBOX_TOKEN = 'ここにマップボックスの開発者用トークンを書く'; // Source data GeoJSON const DATA_URL = './data/osaka3.geojson'; class Maps extends Component { constructor(props) { super(props); this.state = { viewport: { ...DeckGLOverlay.defaultViewport, width: 500, height: 500 }, data: null }; requestJson(DATA_URL, (error, response) => { if (!error) { this.setState({data: response}); } }); } componentDidMount() { window.addEventListener('resize', this._resize.bind(this)); this._resize(); } _resize() { console.dir(this.div._eventCanvas.parentElement.clientHeight) this._onViewportChange({ width: this.div._eventCanvas.parentElement.clientWidth, height: this.div._eventCanvas.parentElement.clientHeight }); } _onViewportChange(viewport) { this.setState({ viewport: {...this.state.viewport, ...viewport} }); } render() { const {viewport, data} = this.state; return ( <MapGL ref={div => this.div = div} {...viewport} mapStyle="mapbox://styles/mapbox/dark-v9" onViewportChange={this._onViewportChange.bind(this)} mapboxApiAccessToken={MAPBOX_TOKEN}> <DeckGLOverlay viewport={viewport} data={data} colorScale={colorScale} /> </MapGL> ); } } export default Maps; |
Maps.jsでは主にベースマップ (Mapbox)のコンフィグレーションをおこなっています。
GeoJSONのデータを実際に出力しているのは DeckGLOverlay コンポーネントになります。
DeckGLOverlay コンポーネントの設定はdeckgl-overlay.jsの中に記載されています。
components/Maps/deckgl-overlay.js
最後に、deckgl-overlay.jsをコピペします。
サンプルでは、元のgeojsonデータの中に高さの値がありましたが、今回用意した大阪のデータには存在しなかったので、ランダムに高さを割り当てるようにサンプルコートを修正しています。また、3Dモデルの配色もブルーを基準にランダムに塗るように変更しました。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
import React, {Component} from 'react'; import DeckGL, {GeoJsonLayer} from 'deck.gl'; const LIGHT_SETTINGS = { lightsPosition: [-125, 50.5, 5000, -122.8, 48.5, 8000], ambientRatio: 0.2, diffuseRatio: 0.5, specularRatio: 0.3, lightsStrength: [2.0, 0.0, 2.0, 0.0], numberOfLights: 2 }; export default class DeckGLOverlay extends Component { static get defaultViewport() { return { latitude: 34.65875, longitude: 135.46257650000007, zoom: 15, maxZoom: 16, pitch: 59, bearing: 0 }; } render() { const {viewport, data} = this.props; if (!data) { return null; } const randomColor = () =>{ if(Math.random() > 0.95){ return [0, 0, 120]; }else if(Math.random() > 0.25){ return [60,60, 255]; }else { return [120, 120, 255]; } } const randomHight = () => { if(Math.random() > 0.999){ return 400 }else if(Math.random() > 0.75){ return 150 }else if(Math.random() > 0.5){ return 50; }else if(Math.random() > 0.25){ return 25 }else { return 10; } } const layer = new GeoJsonLayer({ id: 'geojson', data, opacity: 0.5, stroked: true, filled: true, extruded: true, wireframe: false, fp64: true, getElevation:randomHight, getFillColor: randomColor, getLineColor: [0, 0, 255], lightSettings: LIGHT_SETTINGS, pickable: Boolean(this.props.onHover), onHover: this.props.onHover }); return ( <DeckGL {...viewport} layers={ [layer] } initWebGLParameters /> ); } } |
主にデフォルトの表示領域や3Dモデルのライティングなどの設定が記載されています。
カスタムコンポーネントをApp.jsに追加する
最後に、作成したカスタムコンポーネントをApp.jsに追加して終了です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React, { Component } from 'react'; import logo from './logo.svg'; import './App.css'; import Maps from './components/Maps'; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">Hello. DECK.GL</h1> </header> <div className="Map-wrapper"> <Maps /> </div> </div> ); } } export default App; |