GPS軌跡を描いた地図を自動作成するツール

ガーミンのGPSで取得したトラックログをホームページのオンライン地図に表示する方法についてご紹介します。

最初は電子国土サイトに詳しいAPIリファレンスが掲載されていたので、それを使って電子国土オンライン地図に表示していました。その後バージョンアップを繰り返して、現在はGoogle Map と同じオープンレイヤAPI になったので、Google Map と地理院地図が共用できるようになりました。 今回は Google Map API を使って表示する方法をご紹介します。

Google Map API には64進データからポリラインを表示する API があります。GPSデータを64進データに変換すれば、作業の90%以上は完成したも同然です。そこで先ずはGPSデータから64進データを作成します。

■ GPSデータからポリライン64進数データを作成する

  1. 下記のコードはGPSデータから64進のポリラインを作成するツールです。
  2. コードは今や誰も使わなくなったVBScriptで書いています。
    ただしポリラインを作成する部分は Google Map API と jQuery を使う関係で JavaScript です。
  3. このツールは、地図に重ねて表示するポリラインの作成と撮影ポイントを表示するマーカーを同時に作成します。
  4. このツールを使うためには次の項目の事前準備が必要です。

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false&v=3&language=ja&libraries=geometry"></script>
<script type="text/javascript" src="jquery.min.js"></script>

<title>gps2map作成ツール Ver.15.05.17</title>
<HTA:APPLICATION BORDER="dialog">
<script language="VBScript">

'変数宣言
Dim fomData()                              'fomデータ
Dim jpgData(20,8)                          '画像データ読み込む配列
Dim fomlen
Dim gpxPath1, gpxPath2, camOffset, offsetT
Dim walkTime, walkSecond, gpolylinepoints
Dim maxN : maxN = 0                        '最大緯度初期値
Dim minN : minN = 90                       '最少緯度初期値
Dim maxE : maxE = 0                        '最大経度初期値
Dim minE : minE = 180                      '最少経度初期値

'配列のメモ
'jpgData(i,j)    i=0~19 マーカー画像
'                j:0=jpgFullPath    1=ファイル名    2=撮影Time    3=lat lon    4=ele    5=title    6=coment    7=Time(JST)    8=DiffTime

Set objFso  = CreateObject("Scripting.FileSystemObject")    '準備 ファイルシステムオブジェクトのセット
Set objFile = objFso.GetFile("gps2map.hta")                 'C:\Users\Owner\Documents\tool\gps2map.hta

toolPath = objFile.ParentFolder                             'C:\Users\Owner\Documents\map\tool
mapPath  = Replace(toolPath, "\tool", "")                   'C:\Users\Owner\Documents\map

Sub Window_OnLoad
    Call Window.ResizeTo(1100,800)
End Sub

'-------------------------------------------------
'メインルーチン        実行 ボタン
'-------------------------------------------------
Sub okClick()

    fomlen = Document.forms("fom").Length - 1
    ReDim fomData(fomlen)                                   'fomData配列の大きさ宣言
    For i=0 To fomlen
        Select Case i
            Case 2,6
                fomData(i) = Document.forms("fom").item(i).checked
            Case Else
                fomData(i) = Document.forms("fom").item(i).Value
        End Select
    Next

    gpxPath1 = fomData(0)                                   'GPXファイルPath(C:\Users\Owner\Desktop\new.gpx)
    gpxPath2 = "file:///" & Replace(gpxPath1, "\", "/")     'GPXファイルPath(file:///C:/Users/Owner/Desktop/new.gpx)
    offsethh = fomData(3)                                   'オフセット時
    offsetmm = fomData(4)                                   'オフセット分
    offsetss = fomData(5)                                   'オフセット秒

    For Each i In document.getElementsByName("offset")
        If i.checked Then
            camOffset = i.value                             'カメラオフセット
            Exit For
        End If
    Next

    offsetT = (offsethh)*3600 + (offsetmm)*60 + (offsetss)
    If camOffset = "o1" Then
        offsetT = (-1) * offsetT
    End If

    For i=0 To 19
        jpgData(i,0) = fomData(i*3+7)                                           '画像ファイルネーム
        If  jpgData(i,0) <> "" Then
            jpgData(i,1) = objFso.GetFileName(jpgData(i,0))
            jpgData(i,1) = LCase(Replace(jpgData(i,1), " ", "-"))               '画像ファイルネーム
            jpgData(i,5) = fomData(i*3+8)                                       '画像タイトル
            jpgData(i,6) = fomData(i*3+9)                                       '画像コメント
            If (jpgData(i,5)<>"" And jpgData(i,6)<>"") Or (jpgData(i,5)="" And jpgData(i,6)="") Then
                jpgData(i,5) = jpgData(i,5) & " "
            End If
            jpgData(i,2) = funcExifTimeGet(jpgData(i,0), OffsetT)               '撮影時刻
            jpgData(i,8) = 100000000                                            'GPS時刻と撮影時刻との時間差の初期値(充分大きな値)
        End If
    Next
    Call subJpgResize()                                                         '画像リサイズ
    Call createEncodedPolyline()                                                'エンコードポリライン作成
    Call subMarkerCreate()                                                      'marker.xml 作成
    Call subHtmlCreate()                                                        'gmap.html 作成
    MsgBox("完了!")
    Window.Close()
End Sub


'-------------------------------------------------
'サムネイル表示
'-------------------------------------------------
Sub fnameCopy(num)

    Document.getElementById("thumb" & num).innerHTML = "<img id=""pic"" src=""" & Document.forms("fom").item(num*3+7).Value & """ width=""108"">"        'サムネイルにコピー
    Document.getElementById("imgName" & num).innerHTML = num+1                  'ツールに表示するサムネイル用画像番号
End Sub


'----------------------------------------------------------------
'htmlファイル生成    gmap.html
'----------------------------------------------------------------
Sub subHtmlCreate()

    motoFile = toolPath & "\gmap.html"                      'テンプレート
    shinFile = mapPath & "\gmap.html"                       '出力ファイル

    With CreateObject("ADODB.Stream")
        .Charset = "UTF-8"                                  '文字コードはUTF-8
        .Open
        .LoadFromFile(motoFile)
        txOut = .ReadText                                   'テンプレートを全行読み込む
        .Close
    End With

    txOut = Replace(txOut, "hhhmmm", walkTime)              '所要時間
    txOut = Replace(txOut, "minlat", minN)                  'minlatをポリラインの範囲の南西の角の緯度で書き換える
    txOut = Replace(txOut, "minlng", minE)                  'minlngをポリラインの範囲の南西の角の経度で書き換える
    txOut = Replace(txOut, "maxlat", maxN)                  'maxlatをポリラインの範囲の北東の角の緯度で書き換える
    txOut = Replace(txOut, "maxlng", maxE)                  'maxlngをポリラインの範囲の北東の角の経度で書き換える
    txOut = Replace(txOut, "pppppppppp", gpolylinepoints)   'pppppppppをエンコードポリラインで書き換える

    Call WriteUTF8(txOut, shinFile)                         '文字コードUTF-8でファイルを出力する
End Sub


'-------------------------------------------------
'マーカー・ファイル生成            marker.xml
'-------------------------------------------------
Sub subMarkerCreate()

    shinFile = mapPath  & "\marker.xml"                     'xmlファイル

    Str0 =  "<?xml version=""1.0"" encoding=""UTF-8"" ?>" & vbCrLf & vbCrLf & _
        "<points>" & vbCrLf
    txOut = ""
    txOut = txOut & Str0
    For j=0 To 19
        If jpgData(j,1) <> "" Then
            myDate = DateAdd("s", 30,  jpgData(j,2))
            myTime = DatePart("h", myDate) & ":" & Right("0" & DatePart("n", myDate), 2)
            txOut = txOut & String(1, vbTab) & "<point>" & vbCrLf
            txOut = txOut & String(2, vbTab) & "<lat>" & Replace(jpgData(j,3), " ", "</lat><lng>") & "</lng>" & vbCrLf
            txOut = txOut & String(2, vbTab) & "<title>" & myTime & " " & jpgData(j,5) & "</title>" & vbCrLf
            txOut = txOut & String(2, vbTab) & "<description>標高=" & Round(jpgData(j,4), 0) & "m  " & jpgData(j,6) & "</description>" & vbCrLf
            txOut = txOut & String(2, vbTab) & "<image>" & jpgData(j,1) & "</image>" & vbCrLf
            txOut = txOut & String(1, vbTab) & "</point>" & vbCrLf
        End If
    Next
    txOut = txOut & "</points>" & vbCrLf

    Call WriteUTF8(txOut, shinFile)                         '文字コードUTF-8でファイルを出力する
End Sub


'--------------------------------------------
'function funcExifTimeGet Exifタイム取得
'--------------------------------------------
Function funcExifTimeGet(Filname, Offset)

    Dim jpgFil, i, j, binDate(9), binTime(7), binData(19)
    Dim byt, jpgTimeStr

    set jpgFil = CreateObject("ADODB.Stream")
    jpgFil.Open
    jpgFil.Type = 1
    jpgFil.LoadFromFile(Filname)

    Flag = 0
    i = 0
    Do
        i = i + 1
        byt = jpgFil.Read(1)
        Select Case i
            Case 1,2,3,4,6,7,9,10,12,13,15,16,18,19
                If AscB(midb(byt,1))=>48 And AscB(midb(byt,1))=<57 Then
                    binData(i) = Chr(AscB(midb(byt,1)))
                Else
                    i = 0
                End If
            Case 5,8,14,17
                If Chr(AscB(midb(byt,1)))=":" Then
                    binData(i) = Chr(AscB(midb(byt,1)))
                Else
                    i = 0
                End If
            Case 11
                If Chr(AscB(midb(byt,1)))=" " Then
                    binData(i) = Chr(AscB(midb(byt,1)))
                Else
                    i = 0
                End If
        End Select
        If Flag=0 And i=19 Then
            i = 0
            Flag = 1
        ElseIf Flag=1 And i=19 Then
            Exit Do
        End If
    Loop
    jpgFil.Close

    jpgDateStr = ""
    For i=1 To 19
        If i<10 And binData(i) = ":" Then
            binData(i) = "/"
        End If
        jpgDateStr = jpgDateStr & binData(i)
    Next

    jpgDate = DateAdd("s", Offset, CDate(jpgDateStr))
    funcExifTimeGet = CStr(jpgDate)
End Function


'--------------------------------------------
'JPEG画像リサイズ  xxxxxx.jpg
'--------------------------------------------
Sub subJpgResize()

    Set WshShell = CreateObject("WScript.Shell")
    exeFile = toolPath & "\photoshifter.exe"
    setFile = toolPath & "\mysetting.xml"
    outPath = mapPath & "\"

    For i=0 To 19
        If jpgData(i,0) <> "" Then
            copyFileStr  = exeFile & " /overwrite /par " & setFile & " /format JPEG /file " & """" & jpgData(i,0) & """ " & outPath & jpgData(i,1)
            Call WshShell.Run (copyFileStr,,True)
        End If
    Next
    Set WshShell = Nothing
End Sub


'--------------------------------------------
'UTF8で書込む
'--------------------------------------------
Sub WriteUTF8(text, fileName)

    tmpFile = fileName & ".tmp"

    ' UTF-8で書きこむと自動的にBOM(Byte Order Mark)が、先頭に3バイト付加されてしまう。
    ' それを回避するため、一旦一時ファイルにUTF-8形式で書き込む
    With CreateObject("ADODB.Stream")
        .Type = 2
        .charset = "UTF-8"
        .Open
        .WriteText text
        .SaveToFile tmpFile, 2
        .Close
    End With

    With CreateObject("ADODB.Stream")                       '一時ファイルをバイナリで読み取る
        .Type = 1
        .Open
        .LoadFromFile(tmpFile)                              '一時ファイルをバイナリで読み取る
        .Position = 3                                       'BOMの3バイトをスキップして、次行以降でバイナリで書き込む

        Dim ws : Set ws = CreateObject("ADODB.Stream")
        ws.Type = 1
        ws.Open
        ws.Write(.Read(-1))
        ws.SaveToFile fileName, 2
        ws.Close
        .Close
    End With

    Call CreateObject("Scripting.FileSystemObject").DeleteFile(tmpFile)         '一時ファイルの削除
End Sub
</script>

<script language="Javascript">
//--------------------------------------------
//EncodedPolyline作成
//--------------------------------------------

function createEncodedPolyline(){

    var buff = [];
    var encodedPath;
    var dateStr, timeStr, jst, jpgTime, gpsTime, sTime, eTime;

    for (var j = 0; j < 19; j++) {
        if (jpgData(j,0) != "") {
            dateStr = gpsDate2dateStr(jpgData(j,2));
            timeStr = gpsTime2timeStr(jpgData(j,2));
            jst     = dateTimeStr2dateJST(dateStr,timeStr);
            jpgData(j,7) = jst;
        }
    }

    //GPXのデータを取得する
    $.get(gpxPath2, function(xml){
        var gpx = $("trkpt", xml);

        //取得したデータから緯度経度を取得、配列に格納
        var trkpt, lat, lng, i, j, latLng, ele, utc, diffTime;
        for (i = 0; i < gpx.length; i++) {

            lat = $(gpx[i]).attr("lat");                    //<trkpt lat="yyy" lng="xxx">からyyy,xxxを切り出す
            lng = $(gpx[i]).attr("lon");
            ele = $(gpx[i]).find("ele").text();
            utc = $(gpx[i]).find("time").text();
            utc = utc.replace(/-/g, "/");

            dateStr = gpsDate2dateStr(utc);
            timeStr = gpsTime2timeStr(utc);
            jst     = dateTimeStr2dateJST(dateStr,timeStr);
            gpsTime = jst.getTime() + 32400000;             //日本標準時にするため9時間プラス(ミリ秒)
            for (j = 0; j < 20; j++) {
                if (jpgData(j,0) != "") {
                    diffTime = Math.abs(gpsTime - jpgData(j,7));
                    if (jpgData(j,8) > diffTime) {
                        jpgData(j,8) = diffTime;
                        jpgData(j,3) = lat + " " + lng;
                        jpgData(j,4) = ele;
                    }
                }
            }

            //GPS軌跡の範囲を取得(南西の角から北東の角の緯度経度取得)
            lat = +lat;                                     //文字列を数値に変換
            lng = +lng;                                     //文字列を数値に変換
            maxE = (maxE < lng) ? lng : maxE;
            minE = (minE > lng) ? lng : minE; 
            maxN = (maxN < lat) ? lat : maxN;
            minN = (minN > lat) ? lat : minN;

            if (i == 10) {
                sTime = gpsTime / 1000;                     //Start時間(最初はデータが荒れている場合が多いため10ポイントは準備中として捨てる)
            }
            if (i == gpx.length - 2){
                eTime = gpsTime / 1000;                     //End時間(最後の1ポイントは捨てる)
            }

            //latLngを作成
            buff.push( new google.maps.LatLng(lat, lng) );
        }
        encodedPath = google.maps.geometry.encoding.encodePath(buff);
    });
    gpolylinepoints = encodedPath.replace(/\\/g, "\\\\");
    walkSecond = eTime - sTime;                             //行動時間(End時間 - Start時間)
}

// 日付形式を書き換える DDMMYY形式 (例)2014年11月20日 or 2014-11-20 →  [dateStr] "2014/11/20"
function gpsDate2dateStr(s){
    var yy = s.substr(0,4);
    var mm = s.substr(5,2);
    var dd = s.substr(8,2);
    return (yy + '/' + mm + '/' + dd);
}

// 時刻形式を書き換える hhmmss形式 (例)8:55:30 → [timeStr] " 8:55:30"
function gpsTime2timeStr(s){
    var pos = s.indexOf(":");
    var hh = s.substr(pos-2, 2);
    var mm = s.substr(pos+1, 2);
    var ss = s.substr(pos+4, 2);
    return (hh + ':' + mm + ':' + ss);
}

function dateTimeStr2dateJST(dateStr,timeStr){
    var d = new Date(dateStr + ' ' + timeStr);
    return d;
}
</script>
<style type="text/css">
body        { font-size: 16px;}
#offsetTable{ margin-top:0; margin-left:100px;}
#imgTable   { margin-top:0; margin-left:15px;}
.small      { font-size:12px;}
.largest    { font-size:30px; font-weight:bold;}
</style>

</head>
<body style="background-color:#eff8ef;">
<h2>ホームページ用のGPSポリラインを作成します</h2>
<h3>■ 使い方</h3>
<ol>
    <li>撮影ポイントに表示するデジカメ画像を準備してください。最大20枚です。(リサイズは自動で行います)</li>
    <li>GPSデータはカシミールなどで[GPX]形式(new.gpxなど)で書き出しておいてください。</li>
</ol>
<p> </p>
<form id="fom">
<h3>■ GPXファイル     <span class="small">GPS軌跡を作成する GPXファイル を指定してください。</span></h3>
<p>   <input type="file" size="70" name="imp_file" />   <span class="small">カシミールなどでGPX形式(new.gpx)で書き出したファイルを指定します。</span></p>
<p>                                        <input id="Ok" name="Ok" type="button" value="[作 成]" onclick="okClick()" class="largest" /></p>
<h3>■ 撮影ポイントに表示する画像</h3>
<div id="offsetTable">
<table border="0">
    <tbody>
        <tr>
            <td rowspan="2" width="180">カメラ内蔵時計補正値</td>
            <td><input type="radio" name="offset" value="o1" checked /><b>+</b></td>
            <td rowspan="2"><input size="3" type="text" name="hh" value="00" /></td>
            <td rowspan="2"><font size="-1">時</font></td>
            <td rowspan="2"><input size="3" type="text" name="mm" value="00" /></td>
            <td rowspan="2"><font size="-1">分</font></td>
            <td rowspan="2"><input size="3" type="text" name="ss" value="00" /></td>
            <td rowspan="2"><font size="-1">秒</font></td>
            <td rowspan="2" width="300" align="center"><span class="small">  GPSに対してカメラが 進み=(+) 遅れ=(-)</span></td>
        </tr>
        <tr>
            <td><input type="radio" name="offset" value="o2" /><b>-</b></td>
        </tr>
    </tbody>
</table>
</div>
<br>
<div id="imgTable">
<table border="0">
    <tbody>
        <tr><td width="108"><div id="thumb0"></div></td><td width="108"><div id="thumb1"></div></td><td width="108"><div id="thumb2"></div></td><td width="108"><div id="thumb3"></div></td><td width="108"><div id="thumb4"></div></td><td width="108"><div id="thumb5"></div></td><td width="108"><div id="thumb6"></div></td><td width="108"><div id="thumb7"></div></td><td width="108"><div id="thumb8"></div></td><td width="108"><div id="thumb9"></div></td></tr>
        <tr><td align="center"><div id="imgName0"></div></td><td align="center"><div id="imgName1"></div></td><td align="center"><div id="imgName2"></div></td><td align="center"><div id="imgName3"></div></td><td align="center"><div id="imgName4"></div></td><td align="center"><div id="imgName5"></div></td><td align="center"><div id="imgName6"></div></td><td align="center"><div id="imgName7"></div></td><td align="center"><div id="imgName8"></div></td><td align="center"><div id="imgName9"></div></td></tr>
        <tr><td width="108"><div id="thumb10"></div></td><td width="108"><div id="thumb11"></div></td><td width="108"><div id="thumb12"></div></td> <td width="108"><div id="thumb13"></div></td><td width="108"><div id="thumb14"></div></td><td width="108"><div id="thumb15"></div></td><td width="108"><div id="thumb16"></div></td><td width="108"><div id="thumb17"></div></td><td width="108"><div id="thumb18"></div></td><td width="108"><div id="thumb19"></div></td></tr>
        <tr><td align="center"><div id="imgName10"></div></td><td align="center"><div id="imgName11"></div></td><td align="center"><div id="imgName12"></div></td><td align="center"><div id="imgName13"></div></td><td align="center"><div id="imgName14"></div></td><td align="center"><div id="imgName15"></div></td><td align="center"><div id="imgName16"></div></td><td align="center"><div id="imgName17"></div></td><td align="center"><div id="imgName18"></div></td><td align="center"><div id="imgName19"></div></td></tr>
    </tbody>
</table>
</div>
<div nowrap>  <span class="small">[ファイル名]                                             [画像タイトル]9文字まで  [画像コメント]1列:19文字まで</span><br />
 1<input id="jpg1" name="jpeg1" type="file" onchange="fnamecopy(0)" size="50" />  <input size="16" type="text" name="ptitle1" /> <input size="70" type="text" name="coment1" /><br />
 2<input id="jpg2" name="jpeg2" type="file" onchange="fnameCopy(1)" size="50" />  <input size="16" type="text" name="ptitle2" /> <input size="70" type="text" name="coment2" /><br />
 3<input id="jpg3" name="jpeg3" type="file" onchange="fnameCopy(2)" size="50" />  <input size="16" type="text" name="ptitle3" /> <input size="70" type="text" name="coment3" /><br />
 4<input id="jpg4" name="jpeg4" type="file" onchange="fnameCopy(3)" size="50" />  <input size="16" type="text" name="ptitle4" /> <input size="70" type="text" name="coment4" /><br />
 5<input id="jpg5" name="jpeg5" type="file" onchange="fnameCopy(4)" size="50" />  <input size="16" type="text" name="ptitle5" /> <input size="70" type="text" name="coment5" /><br />
 6<input id="jpg6" name="jpeg6" type="file" onchange="fnameCopy(5)" size="50" />  <input size="16" type="text" name="ptitle6" /> <input size="70" type="text" name="coment6" /><br />
 7<input id="jpg7" name="jpeg7" type="file" onchange="fnameCopy(6)" size="50" />  <input size="16" type="text" name="ptitle7" /> <input size="70" type="text" name="coment7" /><br />
 8<input id="jpg8" name="jpeg8" type="file" onchange="fnameCopy(7)" size="50" />  <input size="16" type="text" name="ptitle8" /> <input size="70" type="text" name="coment8" /><br />
 9<input id="jpg9" name="jpeg9" type="file" onchange="fnameCopy(8)" size="50" />  <input size="16" type="text" name="ptitle9" /> <input size="70" type="text" name="coment9" /><br />
10<input id="jpg10" name="jpeg10" type="file" onchange="fnameCopy(9)" size="50" />  <input size="16" type="text" name="ptitle10" /> <input size="70" type="text" name="coment10" /><br />
11<input id="jpg11" name="jpeg11" type="file" onchange="fnameCopy(10)" size="50" />  <input size="16" type="text" name="ptitle11" /> <input size="70" type="text" name="coment11" /><br />
12<input id="jpg12" name="jpeg12" type="file" onchange="fnameCopy(11)" size="50" />  <input size="16" type="text" name="ptitle12" /> <input size="70" type="text" name="coment12" /><br />
13<input id="jpg13" name="jpeg13" type="file" onchange="fnameCopy(12)" size="50" />  <input size="16" type="text" name="ptitle13" /> <input size="70" type="text" name="coment13" /><br />
14<input id="jpg14" name="jpeg14" type="file" onchange="fnameCopy(13)" size="50" />  <input size="16" type="text" name="ptitle14" /> <input size="70" type="text" name="coment14" /><br />
15<input id="jpg15" name="jpeg15" type="file" onchange="fnameCopy(14)" size="50" />  <input size="16" type="text" name="ptitle15" /> <input size="70" type="text" name="coment15" /><br />
16<input id="jpg16" name="jpeg16" type="file" onchange="fnameCopy(15)" size="50" />  <input size="16" type="text" name="ptitle16" /> <input size="70" type="text" name="coment16" /><br />
17<input id="jpg17" name="jpeg17" type="file" onchange="fnameCopy(16)" size="50" />  <input size="16" type="text" name="ptitle17" /> <input size="70" type="text" name="coment17" /><br />
18<input id="jpg18" name="jpeg18" type="file" onchange="fnameCopy(17)" size="50" />  <input size="16" type="text" name="ptitle18" /> <input size="70" type="text" name="coment18" /><br />
19<input id="jpg19" name="jpeg19" type="file" onchange="fnameCopy(18)" size="50" />  <input size="16" type="text" name="ptitle19" /> <input size="70" type="text" name="coment19" /><br />
20<input id="jpg20" name="jpeg20" type="file" onchange="fnameCopy(19)" size="50" />  <input size="16" type="text" name="ptitle20" /> <input size="70" type="text" name="coment20" /><br />
</div>
</form>
</body>
</html>	

■ 事前準備

  1. PCの適当なフォルダに[map]フォルダを作成、その下に[tool]フォルダを作成します。(下図)
  2. その[tool]中に上記の枠内のコードをコピーして[gps2map.hta]のファイル名で保存します。
  3. この[map]フォルダに新しい地図が作成されます。
  4. [tool]フォルダには下記のファイルが必要です。
     a.上記のツール本体[gps2map.hta
     b.画像リサイズ用[photoshifter]一式(フリーソフト)
     c.リサイズ用設定ファイル[mysetting.xml](自動生成)
     d.jQuery の[jquery.min.js](フリーソフト)
     e,地図表示用テンプレート[gmap.html](自作ファイル)
  5. [photoshifter]はここからダウンロードできます。(http://www.vector.co.jp/soft/dl/winnt/art/se378743.html
  6. [jquery.min.js]はここからダウンロードできます。(http://osdn.jp/projects/sfnet_worthens/downloads/js/jquery/jquery.min.js/
  7. 自作ファイル[gmap.html]は右の兄のサイトにあります。(http://gps-walk.com/gps/gmap.html
    青一色の画面です。これをコピーします。Firefox の場合のやり方は
     a.[ツール]→[Web開発]→[ページのソース]と進みます
     b.ソースが表示されている画面の[編集]→[すべて選択]で、全行を選択(青くして)コピー
     c.それをメモ帳などで新規ファイルに張り付けて
     d.ファイル名を[gmap.html]として[tool]フォルダに保存します。
  8. リサイズ設定ファイル「mysetting.xml]は[photoshifter.exe]をダブルクリックで起動して、画像サイズなどを設定します。その設定を保存すると設定ファイルが本体と同じフォルダに生成されます。
  9. [gps2map.hta]のショートカットをデスクトップに出した方が便利です。

■ 作成作業

  1. GPS受信機のログデータをカシミールにダウンロード。
  2. カシミール3D(フリーソフト)は右からダウンロードできます。(http://www.kashmir3d.com/
  3. GPS受信機の生ログデータはNMEA形式で扱いにくいので、カシミールを使用して、XMLベースのGPX形式で書き出す。(ファイル名は何でもいいです。デフォルトはnew.gpx。フォルダはどこでもOKですが、デスクトップが便利)


    <カシミールでGPSログを右クリック><ファイルの種類でGPX形式を選択>
  4. そのGPSデータ(new.gpx)から上記のツール(gps2map.hta)を使って地図を作成する方法は
  5. ツール[gps2map.hta]または そのショートカットをダブルクリックするとツールが起動します。
  6. ツールの指示に従って GPX ファイル、画像ファイル、コメントなどを入力し、「作成」ボタンをクリックします。
    注:画像は縮小したものは Exif の撮影時刻情報が失われている場合がありエラーになります。その場合はオリジナル画像をお使いください。画像が多いと動作が重くなります。画像を事前にある程度縮小しておくと軽くなりますが、その場合 Exif情報を失わない方法で縮小してください。
  7. しばらくすると「完了!」のメッセージが表示されます。(画像1枚につき1秒ぐらいかかります)
  8. 以上で[map]フォルダに[gmap.html]と[marker.xml]が生成されています。
  9. [gmap.html]をダブルクリックすれば、GPS軌跡が見えます。マーカーをクリックすればそのポイントで撮影した画像が見えます。
  10. ホームページに掲載するには、掲載する場所に下記のようなインナーフレームを設置して[gmap.html]を読み込めばOKです。(サイズは自由に設定してください)
  11. <iframe name="map" src="gmap.html" width="640" height="600" scrolling="no"></iframe>

撮影ポイントのマーカー表示は、GPS時計(人工衛星に搭載されている原子時計で校正された非常に正確な時刻)と画像に埋め込まれた Exif 情報の撮影時刻とを照合して、撮影ポイント割り出しています。
従って歩き始める前にカメラ内蔵時計をGPS時計で校正しておく必要があります。
もし、校正を忘れた場合は、GPS時計をデジカメで撮影しておけば、写っているGPS時計の値(右画像)と、そのデジカメ画像の撮影時刻(Exif情報で確認)との差を求めればカメラ内蔵時計の誤差を後でも知ることが可能です。
(画像をテキストエディタで開くと文字の羅列の中に日付と時刻の数字を見つけることができ、撮影時刻を秒単位まで知ることができます)

歩いている途中でカメラの時刻を修正すると、撮影時刻が前半と後半で違ってくるので面倒なことになりますから、途中では修正しない方がベターです。
GPS時刻をデジカメで撮影しておけば、後でこのツールを使ってポリラインを作成する時に補正ができます。

■ コードの概略

もう、ほとんど誰も使っていない VBScript を使っています。ソースを見ると分かる通り HTML と全く同じです。拡張子が「.html」の代わりに「.hta」になっているだけです。ヘッダー部分(<head>~</head>)が極端に大きい頭でっかちのHTMLファイルです。<body>部分は下の方に少しあります。データを入力する INPUT タグの羅列です。

14行
マーカー用画像を読み込む配列 20枚分。25行目に配列の内容のメモがあります。
2番=画像から読み込んだ撮影時刻(文字列)
3番=8番が最少になったポイントの緯度経度が残る
4番=8番が最少になったポイントの標高が残る
7番=2番の撮影時刻の日時データ(JST)、この時刻とGPS時刻を比較する
8番=GPSポイントと撮影時刻の時刻差(この値が最少になったポイントが撮影ポイント)
34行
このツールの画面の大きさを設定している
46~49行
INPUTタグのタイプ別に値を取得
66~69行
カメラの内蔵時計の誤差補正値
71~81行
フォームに入力された画像データを読み込む
82行
GPS時刻と撮影時刻との時間差の初期値(充分大きな値を初期設定)
142~153行
撮影ポイント用XMLファイル作成(最大20枚)配列jpgDataから読み込む
163~217行
撮影時刻を読むルーチン。幼稚な方法ですがこれしか思い浮かばなかったので・・・
179~184行
画像をテキスト形式で開いて、表示の番号の値が半角数字の文字列を探す
185~190行
同じく表示の番号の文字がコロン(:)である文字列であること
191~196行
同じく11番目の文字が半角スペースであること
208~213行
上記3条件を満足したら19文字のデータをつないで 2015/05/17 12:10:20 形式の撮影時刻を得る
215行
撮影時刻にカメラ時計の誤差分を加算/減算する
222~237行
画像リサイズ(photoshifter使用)
230~235行
コマンドラインで photoshifter.exe を実行。画像サイズは mysetting.xml で設定
243~274行
UTF-8でファイルを書き込む。先頭の3バイトのBOMが付くので、それを回避するため処理が複雑
282~370行
ここが本命。jQuery を使うと簡単なため、ここだけ JavaScript
288~295行
20枚の画像の撮影時刻(文字列)を日本標準時の日時データ(JST)に変換
298行
GPXファイルを取得(XML形式)
299行
<trkpt>から</trkpt>までが1ポイントのデータ
305~308行
順に緯度、経度、標高、を取得
309行
日付の「-」を「/」に変換。これはなくてもいい。311行で同じことをやっている
311~313行
日時データの成型
314行
GPSの世界標準時を日本標準時に変換するため 9時間プラス。ミリ秒単位
315~324行
画像の撮影時刻とGPS時刻の差の絶対値が最小になる点を探す。そこが撮影ポイント
318~322行
前のポイントの時刻差(絶対値)より小さかったら diffTime、緯度経度、標高を書換える
327~332行
ポリラインの表示範囲の南西の角(緯経最小値)と北東の角(緯経最大値)の緯度・経度を取得
334~339行
最初の10ポイントと最後の1ポイントはデータを捨てる。GPS電源を入れた直後は誤差が多いため。
342行
取得した緯度経度データをバッファにプッシュ(末尾に追加)する
344行
この1行で64進のエンコードポリラインが得られる夢のようなAPI (Google Maps API Ver3 は素晴らしい! 過去の苦労は何だったのだろう? http://gps-walk.com/niki/110906/index.html
346行
64進文字列の中に含まれる特殊文字 '\' の機能をエスケープ(無効化)するために '\' を '\\' に変換
351~356行
日付形式を成型する
359~365行
時刻形式を成型する。分・秒は2桁だが時は1桁と2桁の場合があるので要注意
367~370行
日時データ作成。本命部分の JavaScript ここまで。
372~378行
このツールHTAのスタイルシート。 以上でヘッダー部終り
381~448行
このツールHTAのbody部分。 データ入力用のフォームになっています
参考図書
ほかの参考サイト
現在位置: ホーム > GPS地図作成ツール