マップマッチングの説明
ここでは，取得したプローブデータからマップマッチングを行うための Java のコードの内容と行っている計算について説明します．マップマッチングとは，図-1 のように GPS などで観測した位置データの誤差を補正して，道路ネットワーク上で移動経路を特定することを指します．

図 1 マップマッチングのイメージ図
今回紹介するマップマッチングのプログラムの概要は以下のようになっています．
入力：ネットワークデータ(ノード・リンク)・PP データ(トリップデータ・ロケーションデータ)①トリップ単位でロケーションデータを分割する②ネットワーク内の対象リンクのリンク尤度を計算する③ネットワーク内の対象リンクの中から，起点リンクと終点リンクを決定する④リンク尤度をリンクコストとして最短経路探索をすることで，経路を決定する
出力：経路データ・リンクデータ・kml 形式のデータ

1

プロジェクトとデータの準備

ま ず ， プ ロ ジ ェ ク ト と デ ー タ の 準 備 を し ま す ． 「 Mapmatching 」 の フ ァ イ ル を 任 意 の 場 所 に 保 存 しま す（workspace の下など）．次に，ダウンロードした「input_mapmatching」の「input」のファイルの中に入っているデータを「Mapmatching」の「input」のファイルに保存します(/〜/Mapmatching/input/〜)．
ここまでの準備が出来たら，eclipse を起動し，プロジェクトのインポートをして(「Java の導入」に記載してあるのでそれを見ていただけるとわかりやすいと思います)，Main.java ファイルを開きます．Main.java ファイルと実行することでマップマッチングのプログラムを動かすことが出来ます．
以下では，Main.java ファイルのコードに従って解説を進めていきます．

1
18
19
20
21
22

データファイルの読み込み//読み込みファイルの指定
String file1 = "./input/YOKOHAMAnode.csv";
String file2 = "./input/YOKOHAMAlink.csv";
String file3 = "./input/UnlinkedTrip.csv";
String file4 = "./input/LocData.csv";//nodeデータの場所を指定//linkデータの場所を指定//tripデータの場所を指定//location データの場所を指定

まず，入力データファイルの設定を行います．入力データファイルを格納した場所とファイル名を上図のように設定します．データの場所は，ファイル上で右クリック→プロパティから確認することができます．ディレクトリ(フォルダ)を表す記号が”¥”ではなく，”/”であることに注意してください．データを読み込む際には，「,」で区切ってあるデータを一列ずつ項目ごとに読み込みます．このデータの順序が変更されるとデータの読み込みに失敗してしまいます．以下に入力データについて説明があるので，自分でサンプルデータを作成する際には，以下を参考にして作成してください．

2

入力データについて

入力データは，ノードデータ，リンクデータ，トリップデータ，ロケーションデータの４つのデータを使用します．ここで，ノードデータ，リンクデータに関して，道路網はノードとリンクによって表現され，ノードとリンクには固有の ID番号が振られています．リンクは方向毎に起終点が分けられており，一本の道路に対して，双方向に通行可能な場合は，上下線の２本のリンクに分かれています．（ノード１・ノード２間の道路の場合，1→２と２→１のふたつのリンクが存在します）ここでは，計算時にリンクの O と D を区別しているため，リンクが一方通行かどうかを考慮する必要はありません．

◇ ノードデータ（YOKOHAMAnode.csv）
1 列目：ノード ID(nodeID)…各ノードを識別する ID
２列目：各ノードの緯度(lat)…北緯，日本測地系
３列目：各ノードの経度(lng)…東経，日本測地系◇ リンクデータ（YOKOHAMAlink.csv）
1 列目:リンク ID(LinkID)
2 列目:出発ノード(O)
3 列目:到着ノード(D)
4 列目:車線数(lanes)◇トリップデータ（UnlinkedTrip.csv）
1 列目：アンリンクドトリップ ID…最初のアンリンクドトリップはトリップ ID + 「011」
交通手段が一緒なら 1 の位を増やす．
(例:「011」→「012」)

・前のアンリンクドトリップと・前のアンリンクドトリップと交通手段が異なるなら 10 の位を増やして，1 の位を 1 にする．
(例:「012」→「021」)
２列目：モニターID…トリップを行ったモニターの ID
３列目：TripID…各トリップに割り振られている ID
４列目：移動開始時刻
５列目：移動終了時刻
６列目：交通手段…自動車での移動のコード番号は以下の通りです．

000

--

600

食事

100

出勤

700

娯楽

200

帰宅

800

散歩・回遊

300

帰社

900

サイクリング

400

業務

910

モビリティカフェ

500

買い物

999

その他

◇ロケーションデータ(locData.csv)
１列目：測位データ ID…各測位データの ID
２列目：ユーザーコード…ユーザーに割り振られているコード番号
３列目：測位日時
４列目：緯度（日本測地系）
５列目：経度（日本測地系）
６列目：測位モード…データの取得方法を表しています．（0：GPS，1：Hybrid，2：複数基地局，3：測位失敗）

3出力ファイルの設定
4041424344454647484950515253//マップマッチング計算
try {PrintWriter line1 = new PrintWriter(new FileWriter("./output/link.csv"));//全体の経路データ
line1.println("モニターID,ダイアリーID,交通手段,リンクID,リンク出発時刻,所要時間(s),速度(km/h),リンクコスト,リンク長(m)");PrintWriter line2 = new PrintWriter(new FileWriter("./output/path.csv"));//全体の経路データ
line2.println("モニターID,ダイアリーID,交通手段,経路出発nodeID,経路到着nodeID,経路長（m）,所要時間(s),速度(km/h)");List <Trip> tripList = ReadFile.getTripList();for(Trip trip : tripList){//モニターごとにフォルダーを作成しその中に管理File file00 = new File("./output/linkFile/" + trip.getPersonalID() + "/");//もしそのモニター名のフォルダがないなら作成する
if(file00.exists() == false && trip.getLocationList().size() > 1){
file00.mkdirs();}

ここでは，出力ファイルの指定をします．あらかじめ「Mapmatching」の直下のディレクトリに「output」ファイルを作成しておきます．コンパイルすると，ここに「link.csv」「path.csv」「linkFile」という名前の出力データが保存されます．算出されたデータは，43，45 行目のカンマで区切られた各項目に格納されます．

4

出力データについて

出力データは「link.csv」「path.csv」「linkFile」の３種類です．もともとのインプットデータには，自動車以外での移動によるトリップも含まれていますが，使用しているネットワークは車道ネットワークなので，プログラムでは自動車トリップのみを読み込んでいます．
◇リンクデータ(link.csv)
・ モニターID…トリップを行ったモニターの ID
・ ダイアリーID…各トリップの ID
・ 交通手段…各交通手段のコード番号
・ リンク ID…各リンクに割り振られている番号
・ リンク出発時刻
・ 所要時間(s)・ 速度(km/h)
・ リンクコスト…算出されたリンクコスト
・ リンク長(m)◇パスデータ(path.csv)
・ モニターID…トリップを行ったモニターの ID
・ ダイアリーID…各トリップの ID
・ 交通手段…各トリップの ID
・ 経路出発 nodeID…トリップを開始したノードの ID
・ 経路到着 nodeID…トリップを終了したノードの ID
・ 経路長（m）
・ 所要時間(s)・ 速度(km/h)◇linkFile
モニター毎にフォルダが作成されてあり，kml 形式でデータが格納されています．KML (Keyhole MarkupLanguage) は、GoogleEarth や Google マップなどのアプリケーションに表示するポイント、線、画像、ポリゴン、モデルなどの地理的特徴をモデリングして保存するためのファイル形式です。KML ファイルはGoogleEarth にドラッグ＆ドロップすることで，ユーザーの移動経路を表示させることが出来ます．

5

リンク尤度の算出

読み込んだノード・リンクデータと抽出したトリップデータから，経路の精度を評価するために，リンク尤度を算出します．図-2 にリンク尤度の算出のイメージ図を示します． リンク尤度の計算は Matching.java ファイルの中で行われており，式(１)によって求めます．
𝐿𝐿 = (𝑙𝑢𝑝𝑁 + 𝐿𝑑𝑛𝑁 + 𝛼 × 𝑙𝑚𝑖𝑑 ) ×

𝐿𝑙𝑒𝑛𝑔𝑡ℎ𝛽×𝐿𝑙𝑎𝑛𝑒

図 2 リンク尤度算出のイメージ図
ここで，𝐿𝐿 ：リンク尤度
𝑙𝑢𝑝𝑁 ：起点ノードから位置間直線距離までの最短経路
𝑙𝑑𝑛𝑁 ：終点ノードから位置間直線距離までの最短経路
𝑙𝑚𝑖𝑑 ：リンク中央から位置間直線距離までの最短経路
𝐿𝑙𝑒𝑛𝑔𝑡ℎ ：リンク長
𝐿𝑙𝑎𝑛𝑒 ：リンク車線数…(1)

55565758596061626364656667686970717273747576

if(trip.getLocationList().size()> 1){//リンクの中点から取得点を繋げたリンクまでの最短距離を算出し，起終点および中点と取得点をつなげた経路の面積をリンクコストとして算出//初期値
int minCost = Integer.MAX_VALUE;
int limit = DISTANCE_TO_NODE; //ノードとの距離
while(true){//ネットワーク内の対象リンクを抽出しコストを算出する
Matching.exeLinkCost(ReadFile.getNodeMap(), ReadFile.getLinkMap(),trip.getLocationList(), limit);//ネットワーク内の対象リンクの中から起点リンク候補を決定する
List<Integer> startLinkList = null;for(int j = 0; j < trip.getLocationList().size();j++){if(startLinkList==null || startLinkList.isEmpty()==true){
startLinkList = Matching.getNearLinkList(ReadFile.getLinkMap(),
trip.getLocationList(), j);}}77
78//ネットワーク内の対象リンクの中から終点リンク候補を決定する
List<Integer> endLinkList=null;for(int j = trip.getLocationList().size() ;j > 0; j--){if(endLinkList==null || endLinkList.isEmpty()==true){endLinkList=Matching.getNearLinkList(ReadFile.getLinkMap(),
trip.getLocationList(), j-1);}}

6

最短経路探索による経路の特定

どの経路を通ったのかを特定する計算方法について説明します．まず，位置間直線経路(ロケーションデータを直線で結んだもの)の起点および終点から最も近いノードをそれぞれ起点ノード(O)，終点ノード(D)とします．そして，求めたリンク尤度をリンクコストとして最短経路探索を行います．最短経路探索はダイクストラ法と呼ばれるアルゴリズムを利用し，計算は，Dijkstra.java ファイルの中で行われています．図-3 に最短経路探索による経路特定のイメージ図を示します．

図 3 最短経路探索法による経路の特定

80818283848586878889909192939495969798997//起点，終点のリンク候補同士から経路列挙
for(int j = 0; j < startLinkList.size();j++){int sLinkID = startLinkList.get(j);int sNodeID = ReadFile.getLinkMap().get(sLinkID).getUpNodeID();for(int k = 0; k < endLinkList.size(); k++){int eLinkID = endLinkList.get(k);int eNodeID = ReadFile.getLinkMap().get(eLinkID).getDnNodeID();
if(sNodeID != eNodeID){
System.out.println(sLinkID+" "+eLinkID+" "+sNodeID+" "+eNodeID);
Dijkstra.exeDijkstra(ReadFile.getLinkMap(),
ReadFile.getNodeMap(), sNodeID,eNodeID);
int cost = Dijkstra.getCost(ReadFile.getNodeMap(), eNodeID);if(minCost > cost){
minCost = cost;trip.setStartNodeID(sNodeID);
trip.setEndNodeID(eNodeID);}}}}

データクリーニング

最後に，マッチング後にデータの分析を進めて行く上で，不必要なトリップデータが生成されることがあるため，データクリーニングを行います．データクリーニングの必要があるデータとして，出発ノードと到着ノードが同じもの，出発ノードと到着ノードが同一のもの，出発地と到着地がつながっていないもの，自動車トリップとしての取得点がないものとしました．最終的にはマッチングできたものだけ結果を出力します．