noah.plus

Reactとdeck.glでGoogle MapタイムラインのData Visualization

2018-08-05

モバイル版Google Mapにはタイムラインという機能があり、自分がいつどこに居たのかという情報が常に蓄積されている。今回はUberが公開しているData Visualizationのためのフレームワーク、deck.glを使ってGoogle Mapのデータを可視化したことについてまとめる。

deck.gl

GitHub: deck.gl

deck.glは地理情報を可視化することに特化したWebGLベースのフレームワーク。mapboxが提供するMapbox GLと統合されており、視点の操作・2Dや3Dの情報可視化・カラーテーマなど、mapboxの機能をそのまま使えて便利。

deck.glには色々な種類の「レイヤー」が用意されているので、それらを使うことで多種多様なビジュアライゼーションを簡単に行うことができる。

deckgl

レイヤーは、LineLayer・HexagonLayer・ScatterplotLayerなどあるが、独自のカスタムレイヤーを作成することも可能。

レイヤーの一覧は公式のEXAMPLEに載っている。

また、deck.glはReactで使うことを想定されて作られているので、Reactのステートやコンポーネントといった考え方にもよく馴染む。

deck.glでReactコンポーネントを作る場合、最小構成は以下の通り。

import React from 'react';
import DeckGL, {LineLayer} from 'deck.gl';

// カメラ(視点)の設定
const viewState = {
  longitude: -122.41669,
  latitude: 37.7853,
  zoom: 13,
  pitch: 0,
  bearing: 0
};

// データ(始点と終点)
const data = [{
    sourcePosition: [-122.41669, 37.7853],
    targetPosition: [-122.41669, 37.781]
}];

class App extends React.Component {
  render() {
    const layers = [
      // レイヤーの種類を指定し、データを渡す
      new LineLayer({id: 'line-layer', data})
    ];

    return (
      // deck.glにpropsとしてカメラとレイヤーを渡す
      <DeckGL viewState={viewState} layers={layers} />
    );
  }
}

これをレンダリングすると真っ白な画面に縦線が一本だけ表示され、なかなか侘しい感じになる。

deckgl_sample

ハローワールド。

ここから地図を表示したり色々やる場合は、Uberが公開しているチュートリアルに沿って進めるとわかりやすかった。

Building Geospacial App -Vis Academy

Google Mapからタイムラインのデータを取得

可視化するにはそもそもデータが必要なので、Google Mapから持ってくる。

ただしGoogle MapのタイムラインにはAPIがないので手動でダウンロードしてくる必要がある。

  1. Google Mapのメニューからタイムラインを選択

    map_top

  1. タイムライン画面右下の設定メニューから「すべてのデータのコピーをダウンロード」を選択

    map_timeline

  1. ロケーション履歴をJSON形式でダウンロード

    map_download

これでタイムラインのすべてのデータをダウンロードできる。

ダウンロードされるJSONは100MB以上のサイズがあるので、vimかメモ帳じゃないと閲覧すら厳しかった。

自分のタイムラインのデータを確認してみると、最も古いデータは2012年12月12日のものだった🙊

バンコク旅行を可視化

タイムラインのすべてのデータをdeck.glに渡してしまうとパフォーマンス的にかなり厳しくなってしまうので、データを厳選する。

ありふれた日常を可視化しても面白みがないので、せっかくなのでバンコクへ旅行に行った時のデータのみ抽出した。

trip_visualization

実際に動作するものは以下のリンクから確認できる。

Trip to Bangkok

コード全体は以下。

GitHub: trip-visualization

このサンプルでは2つのデータセットを使っている。1つがGoogle Mapのタイムラインのデータ(赤い線)、もう1つが観光スポットを示す位置データ(青い点)で、観光スポットのデータはタイムラインのデータとは別に新しく用意した。

そしてタイムラインのデータにはPathLayer、観光スポットのデータにはScatterplotLayerを使ってレンダリングしている。

// src/DeckGLOverlay.js

import DeckGL, {ScatterplotLayer, PathLayer} from 'deck.gl';
import React, {Component} from 'react';

export default class DeckGLOverlay extends Component {
    render() {
        if (!this.props.timelineData && !this.props.pointData) {
            return null;
        }

        const layers = [
            new PathLayer({
                id: 'timeline-layer',
                // propsとして渡されるタイムラインのデータをセット
                data: this.props.timelineData,
                opacity: 0.5,
                pickable: false,
                widthScale: 2,
                widthMinPixels: 2,
                getPath: d => d.path,
                getColor: d => d.color,
            }),
            new ScatterplotLayer({
                id: 'point-layer',
                // propsとして渡される観光スポットのデータをセット
                data: this.props.pointData,
                pickable: true,
                opacity: 1,
                radiusScale: 50,
                radiusMaxPixels: 25,
                radiusMinPixels: 1,
                getPosition: d => [d.longitude, d.latitude, 30],
                getColor: d => [0, 128, 255],
                onClick: d => this.props.onPointClick(d.object)
            })
        ];

        return (
            // PathLayerとScatterplotLayerのオブジェクトを渡す
            <DeckGL {...this.props.viewport} layers={layers}/>
        );
    }
}

まとめ

deck.glを使えばインタラクティブな地図上で簡単にビジュアライゼーションを行えるのでとても良い。

何らかの位置を示すデータさえあればいいので応用の幅がかなり広いと感じた。

たとえばスマホで撮った写真のExifデータから位置情報を取り出して、撮影場所を地図上に示すなんてことも簡単にできそう。いろいろと活用方法を考えていきたい。


noah.plus