自作地図システムでは、GPS(Global Positioning System)から得た情報は下に示すようなCSV(Comma Separated Value)ファイル形式としている(数値は架空の値)。 データは順に、時刻、経度、緯度、海抜である。日付別ファイルのため、日付はなく時刻のみ記録している。
GPSデータをOSMデータ編集ソフトJOSMなどに読み込んで、編集に使うには、 GPSデータファイルを GPX(GPS eXchange format: GPSデータ交換用ファイル形式、拡張子 .gpx)ファイルに変換する 必要がある。
10:07:56,138.504389,36.545304,65.4 10:07:57,138.504468,36.545318,47.4 ……
GPXは XMLファイルであり、レコード 10:07:56,138.504389,36.545304,65.4 は 次のように表す。
<trkpt lat="36.545304" lon="138.504389"> <ele>65.4</ele> <time>2016-12-06T10:07:56Z</time> </trkpt>
取り敢えずは、CSVファイルをJOSMに取り込める必要最小限の GPXファイルに変換してみよう。 おそらく次の形でいいだろう。 metadataはなくてもいいのかも知れないし、さらにいくつかの項目が必要かもしれない。
<?xml version="1.0" encoding="UTF-8"?> <gpx version="1.1"> <metadata> <bounds minlat="..." minlon="..." maxlat="..." maxlon="..."/> </metadata> <trk> <trkseg> <trkpt lat="36.545304" lon="138.504389"> <ele>65.4</ele> <time>2016-12-06T10:07:56Z</time> </trkpt> [中略] </trkseg> </trk> </gpx>
OSMデータ編集では metadata はなくてもよいことが分かった。
まずは、引数で日付を指定するコマンドラインプログラムを作る。
GPS軌跡を道路等の描画に使う場合、必要なのは、緯度・経度のみであり、eleタグ、timeタグは要らないので、 なくても動作すると思うが、デフォルトでは、緯度・経度のみとして、-ele, -time オプション指定で、 海抜(標高)、日時を含められるようにしておこう。
XML用のクラスライブラリを使う案もあるが、シンプルな形式のため、何も使わず、直に書いてみよう。
試行錯誤の結果、次のプログラムに落ち着いた。
// csv2gpx.cs using System; using System.IO; using System.Text; public class CSV2GPX { static void Main(string[] args) { bool optEle = false; bool optTime = false; string date = null; int hourFrom = 10; int hourTo = 15; for (int n = 0; n < args.Length; n++) { if (args[n] == "-ele") optEle = true; else if (args[n] == "-time") optTime = true; else { date = args[n]; if (n + 1 < args.Length) { // ex. 10-13 string[] hours = args[++n].Split('-'); hourFrom = int.Parse(hours[0]); hourTo = int.Parse(hours[1]); } } } string pathCSV = "c:/gis/tablet/log/p" + date + ".csv"; string pathGPX = "c:/gis/tablet/gpx/" + date + ".gpx"; if (!File.Exists(pathCSV)) { Console.WriteLine(pathCSV + " は存在しません"); return; } StreamWriter writer = new StreamWriter(pathGPX, false, Encoding.GetEncoding("utf-8")); writer.WriteLine("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); writer.WriteLine("<gpx version=\"1.1\">"); writer.WriteLine(" <trk>"); writer.WriteLine(" <trkseg>"); string[] positions = File.ReadAllText(pathCSV).Trim().Split('\n'); for (int n = 0; n < positions.Length; n++) { string[] items = positions[n].Trim().Split(','); DateTime time = DateTime.ParseExact(date + " " + items[0], // 20161207 12:34:56 "yyyyMMdd H:m:s", System.Globalization.CultureInfo.InvariantCulture); if (time.Hour < hourFrom || time.Hour >= hourTo) continue; writer.WriteLine(" <trkpt lat=\"" + items[2] + "\" lon=\"" + items[1] + "\">"); if (optEle) writer.WriteLine(" <ele>" + items[3] + "</ele>"); if (optTime) writer.WriteLine(" <time>" + time.ToString("u") + "</time>"); writer.WriteLine(" </trkpt>"); } writer.WriteLine(" </trkseg>"); writer.WriteLine(" </trk>"); writer.WriteLine("</gpx>"); writer.Close(); } }
次のようにすれば、2016年12月3日の 12時から14時までのGPS軌跡を GPXファイルに出力できる。 これを OSM編集画面にドラッグ&ドロップすると、めでたく画面上にGPS軌跡が描画され、 これをなぞって、歩いた道を登録できた。
c:\gis>csv2gpx -time 20161203 12-14
結果的には、GPSデータの利用は想像以上に簡単であった。
最初にメタデータとして、緯度・経度の最小値・最大値を出力する必要がある場合、直接、ファイルに書きこまず、 まずは、StringBuilder に trk セクションデータを蓄えた方がいいだろう。