路面管理のビッグデータを利用して路面状況を取得してみる

  • 30
    いいね
  • 0
    コメント
この記事は最終更新日から1年以上が経過しています。

現在、「路面管理の高度化における実証実験」として、バス,タクシーなどの公共交通車両に取り付けられた安価なセンサーによって取得されたデータを収集し,それらを集約したビッグデータが公開されています。

路面管理ビッグデータのオープンデータコンテスト
http://micrms.force.com/apis

今回はこのデータを利用して地図上で路面の状況を確認できるようにしました。

路面状況Viewer
http://needtec.sakura.ne.jp/road_mgt/road_mgt.html

GitHub
https://github.com/mima3/road_mgt

路面3.png

路面管理の高度化における実証実験 APIについて

APIの仕様書は下記からダウンロードできます。
http://micrms.force.com/developer

データとしては大きく以下の3つに分けることができます。
・路面計測データ i-DRIMSという機械で計測したデータ。加速度、角速度、位置のデータ
・路面状況データ 路面計測データを分析した路面の状況のデータ
・路面緒元データ 自治体毎に短い区間で設定した道路管理情報

通常は路面状況データと路面緒元データを使用することになると思います。

路面状況データの取得方法

路面状況を取得するには以下のURLに対してGETを行います。

http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-surface

この結果は次のようなXMLとなります。

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:rm="http://roadmgt.herokuapp.com/vocab/rm#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
  <rdf:Description rdf:about="http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_282">
    <rm:iri>2.65</rm:iri>
    <rm:step>0.0</rm:step>
    <rm:patching_num>0</rm:patching_num>
    <rm:municipality_id>1</rm:municipality_id>
    <rm:too_slow_fast_flag>0</rm:too_slow_fast_flag>
    <rm:subsidence_and_puddle>0.0</rm:subsidence_and_puddle>
    <rm:section_id>-1</rm:section_id>
    <rm:speed>37.55711977</rm:speed>
    <rdf:type>http://roadmgt.herokuapp.com/vocab/rm#road-surface</rdf:type>
    <geo:alt>0.0</geo:alt>
    <rm:rms>21.14314346</rm:rms>
    <geo:long>140.1434769</geo:long>
    <rm:rutting_amount>3.75</rm:rutting_amount>
    <rm:pothole_num>0</rm:pothole_num>
    <rm:speed_fluctuation_flag>0</rm:speed_fluctuation_flag>
    <rdfs:label>road-surface_282</rdfs:label>
    <rm:cracking_rate>5.3</rm:cracking_rate>
    <geo:lat>35.61753776</geo:lat>
    <rm:analysis_timestamp>2014-12-30 17:00:02.0</rm:analysis_timestamp>
    <rm:distance>220</rm:distance>
  </rdf:Description>
  <rdf:Description rdf:about="http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_294">
    <rm:speed_fluctuation_flag>1</rm:speed_fluctuation_flag>
    <rm:patching_num>0</rm:patching_num>
    <rm:iri>2.5</rm:iri>
    <rm:step>0.0</rm:step>
    <rm:too_slow_fast_flag>0</rm:too_slow_fast_flag>
    <rm:subsidence_and_puddle>0.0</rm:subsidence_and_puddle>
    <rm:section_id>-1</rm:section_id>
    <rm:municipality_id>1</rm:municipality_id>
    <rm:distance>340</rm:distance>
    <geo:alt>0.0</geo:alt>
    <rm:cracking_rate>7.5</rm:cracking_rate>
    <geo:long>140.1464737</geo:long>
    <rdf:type>http://roadmgt.herokuapp.com/vocab/rm#road-surface</rdf:type>
    <rm:rms>26.23188158</rm:rms>
    <geo:lat>35.61759593</geo:lat>
    <rm:pothole_num>0</rm:pothole_num>
    <rm:speed>42.15859739</rm:speed>
    <rm:analysis_timestamp>2014-12-30 17:00:02.0</rm:analysis_timestamp>
    <rm:rutting_amount>5.3</rm:rutting_amount>
    <rdfs:label>road-surface_294</rdfs:label>
  </rdf:Description></rdf:RDF>

路面状況を全データ取得する方法

APIを実行した時の最大取得件数はデフォルトで300件です。
以降のデータを取得するには、以下のようにoffsetを利用する必要があります。

(1)まず先頭を取得
http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-surface&limit=300&offset=0

(2)正常にデータが取得できたら、offsetを変更して実行
http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-surface&limit=300&offset=300

(3)これを以下のレスポンスになるまで繰り返す。

404 notFound

路面状況のパラメータ

路面状況はパラメータを指定してデータをフィルタリングすることが可能です。
指定可能なパラメータは次の通りです。

名称 説明
municipality xsd:int 自治体ID
自治体のIDらしいが当然の権利のように説明が一切ないので意味がわかりません。
1と11が入っていてます。おそらく、1が千葉市で11が豊中市だとは思います。
section xsd:int 区間ID。おそらく、道路の区間を内部で一意のIDをふっているのでしょうがよくわかりません。-1とか入っています。
date_start xsd:date 分析日。終了の指定とセットでない場合はエラー(YYYY-MM-DD)
date_end xsd:date 分析日。開始の指定とセットでない場合はエラー(YYYY-MM-DD)
lat_start xsd:double 緯度開始。終了の指定とセットでない場合はエラー
lat_end xsd:double 緯度終了。開始の指定とセットでない場合はエラー
lon_start xsd:double 経度開始。終了の指定とセットでない場合はエラー
lon_end xsd:double 経度終了。開始の指定とセットでない場合はエラー

経度緯度を指定した実行例:

http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-surface&lat_start=34.66802946&lon_start=135.362425&lat_end=35.67436512&lon_end=140.2823427&offset=300&limit=300

注意 
ここで紹介したパラメータ並びに、offset,limit以外のパラメータを指定するとエラーとなります。
このことは、以下のようなコードだとエラーになることを意味します。

$.ajax({
  type : 'GET',
  url : 'http://roadmgt.herokuapp.com/api/v1/datapoints',
  cache: false // キャッシュを回避するとエラーになる。
  data : {
    data_type : 'road-surface',
    lat_start  : lat_start,
    lon_start : lon_start,
    lat_end  : lat_end,
    lon_end  : lon_end,
    offset : offset,
    limit : limit
  },
  success : function (data) {
  },
  'error' :  function(xhr, textStatus, error) {
  }
});

このことはIE系でデータが更新されていても、内容が変わらないというリスクを含むことになります。

cahce=falseの場合、パラメータにユニークな一時的な値を指定することで、別のリクエストとして認識させ、キャッシュの使用を回避しているのですが、これが利用できなくなります。

http://stackoverflow.com/questions/4303829/how-to-prevent-a-jquery-ajax-request-from-caching-in-internet-explorer

ターゲットの直接指定

rdf:aboutに記述してある、URIを指定することで直接ターゲットを取得できます。

実行例:
http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_282

取得結果:

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:rm="http://roadmgt.herokuapp.com/vocab/rm#" xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<rdf:Description rdf:about="http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_282">
<rm:iri>2.65</rm:iri>
<rm:step>0.0</rm:step>
<rm:patching_num>0</rm:patching_num>
<rm:municipality_id>1</rm:municipality_id>
<rm:too_slow_fast_flag>0</rm:too_slow_fast_flag>
<rm:subsidence_and_puddle>0.0</rm:subsidence_and_puddle>
<rm:section_id>-1</rm:section_id>
<rm:speed>37.55711977</rm:speed>
<rdf:type>http://roadmgt.herokuapp.com/vocab/rm#road-surface</rdf:type>
<geo:alt>0.0</geo:alt>
<rm:rms>21.14314346</rm:rms>
<geo:long>140.1434769</geo:long>
<rm:rutting_amount>3.75</rm:rutting_amount>
<rm:pothole_num>0</rm:pothole_num>
<rm:speed_fluctuation_flag>0</rm:speed_fluctuation_flag>
<rdfs:label>road-surface_282</rdfs:label>
<rm:cracking_rate>5.3</rm:cracking_rate>
<geo:lat>35.61753776</geo:lat>
<rm:analysis_timestamp>2014-12-30 17:00:02.0</rm:analysis_timestamp>
<rm:distance>220</rm:distance>
</rdf:Description>
</rdf:RDF>

ターゲット名の後にプロパティ名を指定することで、プロパティーのみの取得ができます。
この際、「:」を「_」に変更する必要があります。たとえば、「rm:step」を取得したい場合は「rm_step」と指定します。

実行例:
http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_282/rm_step

取得結果

<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#" xmlns:rm="http://roadmgt.herokuapp.com/vocab/rm#">
<rdf:Description rdf:about="http://roadmgt.herokuapp.com/api/v1/datapoints/road-surface_282">
<rm:step>0.0</rm:step>
</rdf:Description>
</rdf:RDF>

レスポンスの説明

主なプロパティを以下に説明します。

プロパティ 名称 説明
rm:analysis_timestamp 分析日時 xsd:dateTime 「2015-01-14 09:01:57」という形式で入っています。
rm:iri IRI xsd:double International Roughness Index 舗装の平坦性(乗り心地)を客観的. に評価する尺度
http://www.kandoken.jp/huroku/130913_2_kouenshiryo.pdf
rm:pothole_num ポットホール数 xsd:int 道路の舗装表面が陥没してできた穴の数
rm:patching_num パッチング数 xsd:int 舗装の損傷に対する応急処置を実施した数
http://www.mlit.go.jp/road/ir/ir-council/pdf/roadstock05.pdf
rm:cracking_rate ひび割れ率 xsd:double http://www.town.oki.fukuoka.jp/file/gyousei/nyuusatsu/FILE_346_45.pdf
rm:rutting_amount わだち掘れ深さ xsd:double http://www.fuzita.org/cod/rut_.html
rm:step 段差 xsd:double
rm:subsidence_and_puddle 沈下・水たまり xsd:double
geo:lat 緯度 xsd:double
geo:long 経度 xsd:double
geo:alt 高度 xsd:double

路面緒元データの取得方法

基本的に路面状況と同じです。
以下のURLに対してGETを行います。

http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-master&lat_start=34.66802946&lon_start=135.362425&lat_end=35.67436512&lon_end=140.2823427&offset=300&limit=300

現在は以下の赤線のデータしか存在していません。
路面.png

拡大するとわかりますが、一応、車線毎にデータが存在するようです。

路面2.png

プログラム言語で使用する

Python

ここではPythonで路面状況を取得する方法を記述します。

road_mgt.py
# -*- coding: utf-8 -*-
import urllib
import urllib2
from lxml import etree
import csv
from collections import defaultdict


def get_road_surface(offset, limit):
    """
    路面状況データ
    """
    url = ('http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=road-surface&offset=%d&limit=%d' % (offset, limit))

    req = urllib2.Request(url)
    opener = urllib2.build_opener()
    conn = opener.open(req)
    cont = conn.read()
    parser = etree.XMLParser(recover=True)
    root = etree.fromstring(cont, parser)
    namespaces = {
        'geo' : 'http://www.w3.org/2003/01/geo/wgs84_pos#',
        'rdf' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 
        'rdfs' : 'http://www.w3.org/2000/01/rdf-schema#', 
        'rm' : 'http://roadmgt.herokuapp.com/vocab/rm#'
    }

    row = []
    datas = {}
    value_tags = root.xpath('//rdf:Description', namespaces=namespaces)
    value_tags = root.xpath('//rdf:Description', namespaces=namespaces)
    for value_tag in value_tags:
        label = value_tag.find('rdfs:label', namespaces).text
        step = value_tag.find('rm:step', namespaces).text
        alt = value_tag.find('geo:alt', namespaces).text
        analysis_timestamp = value_tag.find('rm:analysis_timestamp', namespaces).text
        rutting_amount = value_tag.find('rm:rutting_amount', namespaces).text
        municipality_id = value_tag.find('rm:municipality_id', namespaces).text
        speed_fluctuation_flag = value_tag.find('rm:speed_fluctuation_flag', namespaces).text
        section_id = value_tag.find('rm:section_id', namespaces).text
        distance = value_tag.find('rm:distance', namespaces).text
        long = value_tag.find('geo:long', namespaces).text
        iri = value_tag.find('rm:iri', namespaces).text
        cracking_rate = value_tag.find('rm:cracking_rate', namespaces).text
        pothole_num = value_tag.find('rm:pothole_num', namespaces).text
        subsidence_and_puddle = value_tag.find('rm:subsidence_and_puddle', namespaces).text
        speed = value_tag.find('rm:speed', namespaces).text
        rms = value_tag.find('rm:rms', namespaces).text
        lat = value_tag.find('geo:lat', namespaces).text
        too_slow_fast_flag = value_tag.find('rm:too_slow_fast_flag', namespaces).text
        patching_num = value_tag.find('rm:patching_num', namespaces).text

        row.append({
            'label' : label,
            'step' : step,
            'alt' : alt,
            'analysis_timestamp' : analysis_timestamp,
            'rutting_amount' : rutting_amount,
            'municipality_id' : municipality_id,
            'speed_fluctuation_flag' : speed_fluctuation_flag,
            'section_id' : section_id,
            'distance' : distance,
            'long' : long,
            'iri' : iri,
            'cracking_rate' : cracking_rate,
            'pothole_num' : pothole_num,
            'subsidence_and_puddle' : subsidence_and_puddle,
            'speed' : speed,
            'rms' : rms,
            'lat' : lat,
            'too_slow_fast_flag' : too_slow_fast_flag,
            'patching_num' :patching_num
        })
    return row

def get_meas_locale(offset, limit):
    """
    路面計測時の位置データ
    """
    url = ('http://roadmgt.herokuapp.com/api/v1/datapoints?data_type=meas-locale&offset=%d&limit=%d' % (offset, limit))

    req = urllib2.Request(url)
    opener = urllib2.build_opener()
    conn = opener.open(req)
    cont = conn.read()
    parser = etree.XMLParser(recover=True)
    root = etree.fromstring(cont, parser)

    namespaces = {
        'geo' : 'http://www.w3.org/2003/01/geo/wgs84_pos#',
        'rdf' : 'http://www.w3.org/1999/02/22-rdf-syntax-ns#', 
        'rdfs' : 'http://www.w3.org/2000/01/rdf-schema#', 
        'rm' : 'http://roadmgt.herokuapp.com/vocab/rm#'
    }

    row = []
    datas = {}
    value_tags = root.xpath('//rdf:Description', namespaces=namespaces)
    for value_tag in value_tags:
        label = value_tag.find('rdfs:label', namespaces).text
        gpstimestamp = value_tag.find('rm:gpstimestamp', namespaces).text
        course = value_tag.find('rm:course', namespaces).text
        measurement_start_timestamp = value_tag.find('rm:measurement_start_timestamp', namespaces).text
        mounting_direction = value_tag.find('rm:mounting_direction', namespaces).text
        car_model = value_tag.find('rm:car_model', namespaces).text
        long = value_tag.find('geo:long', namespaces).text
        lat = value_tag.find('geo:lat', namespaces).text
        alt = value_tag.find('geo:alt', namespaces).text
        model_number = value_tag.find('rm:model_number', namespaces).text
        car_number = value_tag.find('rm:car_number', namespaces).text
        speed = value_tag.find('rm:speed', namespaces).text
        vertical_accuracy = value_tag.find('rm:vertical_accuracy', namespaces).text
        measurement_timestamp = value_tag.find('rm:measurement_timestamp', namespaces).text
        horizontal_accuracy = value_tag.find('rm:horizontal_accuracy', namespaces).text
        row.append({
            'label' : label,
            'gpstimestamp' : gpstimestamp,
            'course' : course,
            'measurement_start_timestamp' : measurement_start_timestamp,
            'mounting_direction' : mounting_direction,
            'car_model' : car_model,
            'long' : long,
            'lat' : lat,
            'alt' : alt,
            'model_number' : model_number,
            'car_number' : car_number,
            'speed' : speed,
            'vertical_accuracy' : vertical_accuracy,
            'measurement_timestamp' : measurement_timestamp,
            'horizontal_accuracy' : horizontal_accuracy
        })
    return row

def get_road_surface_all():
    ret = []
    limit = 300
    offset = 0
    try:
        while True:
            print ('get_road_surface_all %d' % offset)
            r = get_road_surface(offset, limit)
            ret.extend(r)
            offset += limit
    except urllib2.HTTPError, ex:
        if ex.code == 404:
            return ret
        else:
            raise

get_road_surface_all()を使用すれば、全路面状況が取得できます。
プログラムとしてはurllibでAPIを実行して、レスポンスをlxmlで解析しているだけです。

JavaScript

ここではJavaScriptで路面状況を取得する方法を記述します。

  /**
   * 路面情報のAPIを実行
   */
  function getDataByRange(data_type, lat_start, lon_start, lat_end, lon_end, callback) {
    var offset = 0;
    var limit = 300;
    var obj = {};

    function loadRoadOffset(lat_start, lon_start, lat_end, lon_end, offset, limit, obj, cb) {

      console.log(offset, limit);
      $.ajax({
        type : 'GET',
        url : 'http://roadmgt.herokuapp.com/api/v1/datapoints',
        cache: true , //, cacheをfalseにするとエラーになる。おそらく変なパラメータを無視するのでなく、はじいている
        data : {
          data_type : data_type,
          lat_start  : lat_start,
          lon_start : lon_start,
          lat_end  : lat_end,
          lon_end  : lon_end,
          offset : offset,
          limit : limit
        },
        success : function (data) {
          console.log(data);
          var root = data.documentElement;
          var attrs = root.attributes;
          var records = root.childNodes;
          for(var i=0; i<records.length; i++){
            if(records[i].nodeName.match(/rdf:Description/i)){
              var s = records[i].attributes["rdf:about"].value;
              var props = records[i].childNodes;
              for(var j=0; j<props.length; j++){
                if(props[j].nodeType == 1){
                  var p = props[j].nodeName;
                  var o = props[j].textContent;
                  if (!obj[s]) {
                    obj[s] = {};
                  }
                  if (obj[s][p]) {
                    if (!Array.isArray(obj[s][p])) {
                      var tmp = arys[s][p];
                      obj[s][p] = [];
                      obj[s][p].push(tmp);
                    }
                    obj[s][p].push(o);
                  } else {
                    obj[s][p] = o;
                  }
                }
              }
            }
          }
          loadRoadOffset(lat_start, lon_start, lat_end, lon_end, offset + limit, limit, obj, cb);
        },
        'error' :  function(xhr, textStatus, error) {
          console.log(xhr);
          var err = xhr.responseText;
          if (err == '404 notFound') {
            cb(null , obj);
          } else {
            cb(err , null);
          }
        }
      });
    }
    loadRoadOffset(lat_start, lon_start, lat_end, lon_end, offset, limit, obj, function(err, res) {
      callback(err, res);
    });
  }

この関数は次のように実行します。

getDataByRange('road-master', surfaceRange.lat_min, surfaceRange.long_min, surfaceRange.lat_max, surfaceRange.long_max, function(err, data) {
      console.log('loadRoad', err, data);
});

この処理は指定の範囲の道路緒元データを全て取得します。

まとめ

「路面管理の高度化における実証実験」のAPIを実行することで路面の状況を取得することができます。

これにより道路の劣化状況などの可視化が行えます。