StravaなどのGPSデータをOracle Data Visualization DesktopでGIS的に扱う


ベトナムオフショアネタなどをGYAO時代に投稿していた玉利です。会社アカウントはやめて、個人の立場で前と同じ調子で行きます。IoTやデータ分析がらみのお仕事募集中です。

2017年12月から2018年の1月に、自分のエンジニアとしてのキャリア棚卸しにベトナムや中国深センなどを回ってきました。日本人のステレオタイプを取り除くために、一枚の写真をお見せしたいと思います。

10年ちょっと前に私が深センにいたころは、リアル北斗の拳とまではいかずとも、リアルFinalFightみたいな町で、ぼーっとして歩いてるとぶん殴られて強盗に会うような町でした。ソースは同僚のTくんで、駐在10日目で強盗にあって頭を殴られて、あえなく帰国してました。わたしも中国の国境を越えて香港に入った瞬間に、体の緊張感が一気に開放されたことをよく覚えてます。

深センでは公共交通機関などではスリが多いので、乗客は皆ビクビクしてたのですが、いまはこんな感じです。地下鉄の中で爆睡してます。

何が置きたかというと、まちなか至る所に監視カメラがついていて、犯罪者は顔認証で身分がバレて即座に逮捕される仕組みができたので犯罪が激減したようです。中国では政府が戸籍ある人全員の顔認証DBを持ってるので、信号無視しただけで晒し者にされます。

ITのちからって、本当にすごいですね。自分も、社会をよくするために技術力を使おうと思いました。
閑話休題

Oracle Data Visualization DesktopでGPSデータを可視化する

IoTのデータをモデリング向けに楽に可視化してあげたいなーと思ってました。ガチな人はMortionBoardとか使っておりますが、小遣いで契約するにはちょっとお高い、だけどもExcelはな~、という御方むけに、いい製品がありました。

エンジニアだとVagrantを使うために無料のOracle VirtualBoxを入れていた人も多いかと思います。実は買うと結構高いBIツールも、Oracleは無料で使わせてくれます。20年前に植物生態学を学んでいた時に教授からGIS地図表示を綺麗にできる方法をなんとかしてみろと言われてたのですが、当時これがあったらすごくいい研究が出来たと思います。当時は、ライセンスだけで500万とかかかる世界でした。

ダウンロード

無料で使えるData Visualization Desktopを触ってみよう

リンクから辿っていき、Desktop版をダウンロードしてください。Oracleの無料アカウント登録が必要になると思います。

ガッツリと仕事で使うならクラウド版もあるのですが、簡単に試してみるならDesktop版が良いと思います。データ・ソースはExcelやCSVから、Oracle DB Spark SQL Server,MySQL,DB2,Teradata,Redshift,Hive,Impala,MongoDB,SybaseIQ等々使えます。今回の記事では地図表示に的を絞り、Desktop版で紹介します。

セットアップ

Oracle Data Visualization DesktopはJavaアプリなので、Windows/Mac両方で使えます。大きな画面と大きなメモリのある、パワフルなPCで使った方が楽です。私はMacで使っています。

ヒートマップ表示のプラグインを導入する

地図表示には、緯度経度が入ったデータが必要です。そういえば、以前のPJで映画館の満空情報をElasticsearchで緯度経度扱った覚えがありました。さて、緯度経度の入ったデータなんてどうやって手に入れたらいいのかな、と思ったら、自分の契約しているStravaという自転車やマラソンの分析サービスからデータを取得できることがわかりました。これをテストデータとします。

実際に仕事や研究で使うのであれば、店舗分析であれば店舗住所からGoogleの地図API経由で緯度経度を取得するとか、社用車の走行分析だったらGPSデータをそのまま使うとかできるでしょう。

DVDのデフォルトで入っているMapは、都市名から場所をプロットしてくれるので、ざっくりとしたマーケティング分析には便利ですが、粒度の細かいリアルデータを扱うためには

こちらのライブラリからHeatmap Pluginをダウンロードします。
https://www.oracle.com/solutions/business-analytics/data-visualization/library.html

ダウンロードしたら、DVDのコンソールからプラグインのインストールをします。

このプラグインで、バックグラウンドにGoogle mapsを使おうと思うので、PC内のファイルを修正します
このファイルは編集権限がread onlyになっているので、先にrwに変更してあげてください。

Windows

C:\Program Files\Oracle Data Visualization Desktop\war\va\WEB-INF\quickstart-web.xml

Mac

/Applications/dvdesktop.app/Contents/Resources/app.nw/war/va/WEB-INF

変更内容

oracle.bi.tech.contentSecurityPolicy要素で、maps.googleapis.com:* をscript src以下に追加します。

. script-src 'self' 'unsafe-inline' 'unsafe-eval' .@requestDomain@: @sawServerHost@:* maps.us.oracle.com:* elocation.oracle.com:* maps.googleapis.com:* ;

    <init-param>
      <param-name>oracle.bi.tech.contentSecurityPolicy</param-name>
      <param-value>default-src 'self' *.@requestDomain@:* www.googleapis.com *.dropboxapi.com login.live.com apis.live.net login.microsoftonline.com;script-src 'self' 'unsafe-inline' 'unsafe-eval' *.@requestDomain@:* @sawServerHost@:* elocation.oracle.com:*  apis.daum.net:* *.daumcdn.net:* maps.googleapis.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net;child-src 'self' 'unsafe-inline' 'unsafe-eval' *.@requestDomain@:* @sawServerHost@:* data: blob:;style-src 'self' 'unsafe-inline' *.@requestDomain@:* @sawServerHost@:* elocation.oracle.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net data:;img-src 'self' *.@requestDomain@:* @sawServerHost@:* elocation.oracle.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net *.baidu.com blob: *.googleusercontent.com data:;font-src 'self' *.@requestDomain@:* @sawServerHost@:* elocation.oracle.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net data:;frame-src 'self' @sawServerHost@:* *.@requestDomain@:*;frame-ancestors 'self' @sawServerHost@:* *.@requestDomain@:*;media-src 'self' *.@requestDomain@:* @sawServerHost@:* elocation.oracle.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net data: blob: mediastream:;connect-src 'self' @sawServerHost@:* *.@requestDomain@:* www.googleapis.com *.dropboxapi.com login.live.com apis.live.net login.microsoftonline.com elocation.oracle.com:* maps.google.com *.googleapis.com *.gstatic.com *.virtualearth.net *.here.com *.map.baidu.com *.map.bdimg.com *.locationbox.com.tr *.openstreetmap.org *.basemaps.cartocdn.com *.a.ssl.fastly.net;</param-value>
    </init-param>

書き換えがおわったら、いちどDVDを完全に終了して、再度アプリを立ち上げてリソースファイルを読ませてあげてください。

Stravaから、データをダウンロードする

自転車乗りにおなじみのStrava会員であれば、自分のアクティビティのURLの後ろに /export_tcx を付けてあげるとtcxファイルダウンロードが出来ます。/export_originalだと元データが取れるみたいです。

URL例
https://www.strava.com/activities/1427998696/export_tcx

残念ながら、ログインしていないとデータは取れません。Stravaのアカウントが無い方用として、githubに私の走行データをあげておきます。

ダウンロードしたデータはXMLでした。ちゃんと緯度経度のLatitudeDegrees LongitudeDegrees要素が入ってます。これをこのままDVDに食わせることはできないので、変換してあげることにします。正攻法だとPythonでcsv変換ですが、世の中同じ悩みを持ってる人はいっぱいいるので、ここはどなたかが作ってくれた出来合いのコンバーターを利用することにします。

<?xml version="1.0" encoding="UTF-8"?>
<TrainingCenterDatabase xsi:schemaLocation="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd" xmlns:ns5="http://www.garmin.com/xmlschemas/ActivityGoals/v1" xmlns:ns3="http://www.garmin.com/xmlschemas/ActivityExtension/v2" xmlns:ns2="http://www.garmin.com/xmlschemas/UserProfile/v2" xmlns="http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <Activities>
  <Activity Sport="Biking">
   <Id>2018-02-27T01:28:23Z</Id>
   <Lap StartTime="2018-02-27T01:28:23Z">
    <TotalTimeSeconds>13056</TotalTimeSeconds>
    <DistanceMeters>47118.6</DistanceMeters>
    <MaximumSpeed>48600.0</MaximumSpeed>
    <Calories>0</Calories>
    <Intensity>Active</Intensity>
    <Cadence>56</Cadence>
    <TriggerMethod>Manual</TriggerMethod>
    <Track>
     <Trackpoint>
      <Time>2018-02-27T01:28:23Z</Time>
      <Position>
       <LatitudeDegrees>35.2965030</LatitudeDegrees>
       <LongitudeDegrees>139.5800100</LongitudeDegrees>
      </Position>
      <AltitudeMeters>87.4</AltitudeMeters>
      <DistanceMeters>0.5</DistanceMeters>
      <Cadence>33</Cadence>
      <Extensions>
       <TPX xmlns="http://www.garmin.com/xmlschemas/ActivityExtension/v2">
        <Speed>0.0</Speed>
       </TPX>
      </Extensions>
     </Trackpoint>

GPSiesでデータ変換

今回のテストデータだけではなく、おそらく、お手元の他のタイプのデータもここで変換できるはずです。

変換してあげると、ダウンロードフォルダにCSVファイルがダウンロードされます。

ラベルを付ける

CSV変換したファイルを開くと、速度データなどが欠落してしまっているのですが、まあ試しなので良いとしましょう。速度データが取れれば、配送車の滞留してしまう渋滞ポイントなどを割り出すことができるはずです。

このままだと、ラベルが無くて具合が良くないので、D列にpointというデータを1から順番に付けてあげました。

csvデータをDVDに投入

新しいプロジェクトを作ります。

いよいよデータを投入してあげましょう。

データセットの作成からデータを読んであげると、以下のように表になります。ここでpoint列は、ラベルとして扱いたいのでちょっといじります。

Pointのタイトル行のところにマウスオーバーすると、歯車マークが表れます。クリックすると、設定項目が現れるので、データ型を属性にしてあげます。緯度経度も属性にしないとうまくいかないようです。

ここまで直してあげてください。

データを描画

データが取り込めたら、ビジュアル化タブを押します。データを選んでドラッグアンドドロップしてあげます。

データを自動でビジュアル化してくれるのですが、今回はちょっと思ったのと違いますね。可視化の種別を変えてあげます。

Heatmapを選んでみました。データ読むのにちょっと時間かかります。

走行経路がヒートマップ表示されました。地図は英語版OpenStreetMapなのがちょっと日本で使うには厳しいですね。これをGoogle mapsに変更してみます。

左側の設定でOpenStreetMapから修正します。

Google mapsで表示できました。

結構ここまでたどり着くのが大変だったのですが、いちどやってしまえばあとは簡単なので、ぜひとも試してみてください。

追記 地名から緯度経度を取得する

今回はGPSデータなので緯度経度が最初から含まれていましたが、ヒートマップ表示したいデータは地名だったり住所だったりすることがよくあります。このような場合は、Google map apiを利用してGoogle Spreadsheetで緯度経度を入れてあげるのがかんたんです。

会社でやってみたら、テストデータ作成のgoogle maps apiがIP単位の上限に達していたようで、うまくデータ変換ができませんでした。たぶん地図側も同様の理由でもたつくと思うので、google maps api keyの個人用を取得して設定してあげることをお勧めします。個人で使う常識的ボリュームであれば無料です。

2019/08/21追記 プロジェクトでGeocoding APIを有効にする必要があります。以前は要らなかったのですが、追加設定が必要になりました。

getLatLong.gs
function getLocation(address) {
//   var address = '渋谷駅'
  var api_key = '上のリンクからキーを取得してここに入れてください'
  var requestUrl = "https://maps.googleapis.com/maps/api/geocode/json?address=" + address + "&key=" + api_key; // Google Geocoding API
  var result = JSON.parse( UrlFetchApp.fetch(requestUrl) );
  if ( result['status'] === 'ZERO_RESULTS' ) { return 'No Result.' }
  var location = result['results'][0]['geometry']['location'];
  return [ [ location['lat'] , location['lng'] ] ];
}

このコードをGoogle Spreadsheetにセットし、
  =getLatLong(C4)
みたいに、地名セル(住所)に対して使うと、式をセットしたセルとその右側のセルに緯度経度が表示されます。