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で書いています。(70歳を過ぎた頭では使い慣れているものがベスト)
    ただしポリラインを作成する部分は 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(30,7)                          '画像データ読み込む配列
    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~29 マーカー画像
    '                j:0=jpgFullPath    1=ファイル名    2=撮影Time    3=lat lon    4=ele    5=coment    6=Time(JST)    7=DiffTime
     
    Set objFso  = CreateObject("Scripting.FileSystemObject")    '準備 ファイルシステムオブジェクトのセット
    Set objFile = objFso.GetFile("gps2map.hta")                 'C:\Users\Owner\Documents\map\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(1200,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 29
            jpgData(i,0) = fomData(i*2+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*2+8)                           '画像コメント
                jpgData(i,2) = funcExifTimeGet(jpgData(i,0), OffsetT)   '撮影時刻
                jpgData(i,7) = 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*2+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 29
            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 & "     GPS標高=" & Round(jpgData(j,4), 0) & "m</title>" & vbCrLf
				txOut = txOut & String(2, vbTab) & "<description>" & jpgData(j,5) & "</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 29
            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 < 29; j++) {
            if (jpgData(j,0) != "") {
                dateStr = gpsDate2dateStr(jpgData(j,2));
                timeStr = gpsTime2timeStr(jpgData(j,2));
                jst     = dateTimeStr2dateJST(dateStr,timeStr);
                jpgData(j,6) = 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 < 30; j++) {
                    if (jpgData(j,0) != "") {
                        diffTime = Math.abs(gpsTime - jpgData(j,6));
                        if (jpgData(j,7) > diffTime) {
                            jpgData(j,7) = 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>撮影ポイントに表示するデジカメ画像を準備してください。(縮小した画像は撮影時刻のExif情報が失われている場合があります。オリジナル画像をお使いください。リサイズは自動で行います)</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>
                    <tr><td width="108"><div id="thumb20"></div></td><td width="108"><div id="thumb21"></div></td><td width="108"><div id="thumb22"></div></td> <td width="108"><div id="thumb23"></div></td><td width="108"><div id="thumb24"></div></td><td width="108"><div id="thumb25"></div></td><td width="108"><div id="thumb26"></div></td><td width="108"><div id="thumb27"></div></td><td width="108"><div id="thumb28"></div></td><td width="108"><div id="thumb29"></div></td></tr>
                    <tr><td align="center"><div id="imgName20"></div></td><td align="center"><div id="imgName21"></div></td><td align="center"><div id="imgName22"></div></td><td align="center"><div id="imgName23"></div></td><td align="center"><div id="imgName24"></div></td><td align="center"><div id="imgName25"></div></td><td align="center"><div id="imgName26"></div></td><td align="center"><div id="imgName27"></div></td><td align="center"><div id="imgName28"></div></td><td align="center"><div id="imgName29"></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="70" type="text" name="coment1" /><br />
             2<input id="jpg2" name="jpeg2" type="file" onchange="fnameCopy(1)" size="50" /> <input size="70" type="text" name="coment2" /><br />
             3<input id="jpg3" name="jpeg3" type="file" onchange="fnameCopy(2)" size="50" /> <input size="70" type="text" name="coment3" /><br />
             4<input id="jpg4" name="jpeg4" type="file" onchange="fnameCopy(3)" size="50" /> <input size="70" type="text" name="coment4" /><br />
             5<input id="jpg5" name="jpeg5" type="file" onchange="fnameCopy(4)" size="50" /> <input size="70" type="text" name="coment5" /><br />
             6<input id="jpg6" name="jpeg6" type="file" onchange="fnameCopy(5)" size="50" /> <input size="70" type="text" name="coment6" /><br />
             7<input id="jpg7" name="jpeg7" type="file" onchange="fnameCopy(6)" size="50" /> <input size="70" type="text" name="coment7" /><br />
             8<input id="jpg8" name="jpeg8" type="file" onchange="fnameCopy(7)" size="50" /> <input size="70" type="text" name="coment8" /><br />
             9<input id="jpg9" name="jpeg9" type="file" onchange="fnameCopy(8)" size="50" /> <input size="70" type="text" name="coment9" /><br />
            10<input id="jpg10" name="jpeg10" type="file" onchange="fnameCopy(9)" size="50" /> <input size="70" type="text" name="coment10" /><br />
            11<input id="jpg11" name="jpeg11" type="file" onchange="fnameCopy(10)" size="50" /> <input size="70" type="text" name="coment11" /><br />
            12<input id="jpg12" name="jpeg12" type="file" onchange="fnameCopy(11)" size="50" /> <input size="70" type="text" name="coment12" /><br />
            13<input id="jpg13" name="jpeg13" type="file" onchange="fnameCopy(12)" size="50" /> <input size="70" type="text" name="coment13" /><br />
            14<input id="jpg14" name="jpeg14" type="file" onchange="fnameCopy(13)" size="50" /> <input size="70" type="text" name="coment14" /><br />
            15<input id="jpg15" name="jpeg15" type="file" onchange="fnameCopy(14)" size="50" /> <input size="70" type="text" name="coment15" /><br />
            16<input id="jpg16" name="jpeg16" type="file" onchange="fnameCopy(15)" size="50" /> <input size="70" type="text" name="coment16" /><br />
            17<input id="jpg17" name="jpeg17" type="file" onchange="fnameCopy(16)" size="50" /> <input size="70" type="text" name="coment17" /><br />
            18<input id="jpg18" name="jpeg18" type="file" onchange="fnameCopy(17)" size="50" /> <input size="70" type="text" name="coment18" /><br />
            19<input id="jpg19" name="jpeg19" type="file" onchange="fnameCopy(18)" size="50" /> <input size="70" type="text" name="coment19" /><br />
            20<input id="jpg20" name="jpeg20" type="file" onchange="fnameCopy(19)" size="50" /> <input size="70" type="text" name="coment20" /><br />
            21<input id="jpg21" name="jpeg21" type="file" onchange="fnameCopy(20)" size="50" /> <input size="70" type="text" name="coment21" /><br />
            22<input id="jpg22" name="jpeg22" type="file" onchange="fnameCopy(21)" size="50" /> <input size="70" type="text" name="coment22" /><br />
            23<input id="jpg23" name="jpeg23" type="file" onchange="fnameCopy(22)" size="50" /> <input size="70" type="text" name="coment23" /><br />
            24<input id="jpg24" name="jpeg24" type="file" onchange="fnameCopy(23)" size="50" /> <input size="70" type="text" name="coment24" /><br />
            25<input id="jpg25" name="jpeg25" type="file" onchange="fnameCopy(24)" size="50" /> <input size="70" type="text" name="coment25" /><br />
            26<input id="jpg26" name="jpeg26" type="file" onchange="fnameCopy(25)" size="50" /> <input size="70" type="text" name="coment26" /><br />
            27<input id="jpg27" name="jpeg27" type="file" onchange="fnameCopy(26)" size="50" /> <input size="70" type="text" name="coment27" /><br />
            28<input id="jpg28" name="jpeg28" type="file" onchange="fnameCopy(27)" size="50" /> <input size="70" type="text" name="coment28" /><br />
            29<input id="jpg29" name="jpeg29" type="file" onchange="fnameCopy(28)" size="50" /> <input size="70" type="text" name="coment29" /><br />
            30<input id="jpg30" name="jpeg30" type="file" onchange="fnameCopy(29)" size="50" /> <input size="70" type="text" name="coment30" /><br />
        </div>
    </form>
</body>
</html>
	

■ 事前準備

  1. PCの適当なフォルダに[map]フォルダを作成、その下に[tool]フォルダを作成します。(下図)
  2. この[map]フォルダに新しい地図が作成されます。
  3. [tool]フォルダに上記の枠内のコードをコピーして[gps2map.hta]のファイル名で保存します。(文字コードはUTF-8で保存)
    ※ツールを起動したとき、文字化けしている場合は保存時の文字コードが間違っていることが予想されます。
  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
    青一色の画面です。これをコピーします。各ブラウザでのやり方は
     a.[表示]→[ソース]と進みます。(Internet Explorer の場合)
       [ツール]→[Web開発]→[ページのソース]と進みます。(Firefox の場合)
     b.ソースが表示されている画面の[編集]→[すべて選択]で、全行を選択(青くして)コピー
     c.それをメモ帳などで新規ファイルに張り付けて
     d.ファイル名を[gmap.html]として[tool]フォルダに保存します。(文字コードはUTF-8で保存)
  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です。(サイズは width, height で、自由に設定してください)
  11. <iframe name="map" src="gmap.html" width="640" height="600" scrolling="no"></iframe>

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

歩いている途中でカメラの時刻を修正すると、撮影時刻が前半と後半で違ってくるので面倒なことになりますから、途中では時刻修正はしない方がベターです。GPS時刻をデジカメで撮影しておくだけにしてください。

■ コードの概略

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

14行
マーカー用画像を読み込む配列 30枚分。25行目に配列の内容のメモがあります。
2番=画像から読み込んだ撮影時刻(文字列)
3番=8番が最少になったポイントの緯度経度が残る
4番=8番が最少になったポイントの標高が残る
7番=2番の撮影時刻の日時データ(JST)、この時刻とGPS時刻を比較する
7番=GPSポイントと撮影時刻の時刻差(この値が最少になったポイントが撮影ポイント)
34行
このツールの画面の大きさを設定している
45~50行
INPUTタグのタイプ別に値を取得
66~69行
カメラの内蔵時計の誤差補正値
71~80行
フォームに入力された画像データを読み込む
78行
GPS時刻と撮影時刻との時間差の初期値(充分大きな値を初期設定)
139~150行
撮影ポイント用XMLファイル作成(最大30枚)配列jpgDataから読み込む
160~214行
撮影時刻を読むルーチン。幼稚な方法ですがこれしか思い浮かばなかったので・・・
176~181行
画像をテキスト形式で開いて、表示の番号の値が半角数字の文字列を探す
182~187行
同じく表示の番号の文字がコロン(:)である文字列であること
188~193行
同じく11番目の文字が半角スペースであること
205~210行
上記3条件を満足したら19文字のデータをつないで 2015/05/17 12:10:20 形式の撮影時刻を得る
212行
撮影時刻にカメラ時計の誤差分を加算/減算する
220~234行
画像リサイズ(photoshifter使用)
227~232行
コマンドラインで photoshifter.exe を実行。画像サイズは mysetting.xml で設定
240~271行
UTF-8でファイルを書き込む。先頭の3バイトのBOMが付くので、それを回避するため処理が複雑
279~367行
ここが本命。jQuery を使うと簡単なため、ここだけ JavaScript
285~292行
30枚の画像の撮影時刻(文字列)を日本標準時の日時データ(JST)に変換
295行
GPXファイルを取得(XML形式)
296行
<trkpt>から</trkpt>までが1ポイントのデータ
302~305行
順に緯度、経度、標高、時刻、を取得
306行
日付の「-」を「/」に変換。これはなくてもいい。308行で同じことをやっている
308~310行
日時データの成型
311行
GPSの世界標準時を日本標準時に変換するため 9時間プラス。ミリ秒単位
312~321行
画像の撮影時刻とGPS時刻の差の絶対値が最小になる点を探す。そこが撮影ポイント
315~319行
前のポイントの時刻差(絶対値)より小さかったら diffTime、緯度経度、標高を書換える
324~329行
ポリラインの表示範囲の南西の角(緯度経度最小値)と北東の角(緯度経度最大値)を取得
331~336行
最初の10ポイントと最後の1ポイントはデータを捨てる。GPS電源を入れた直後は誤差が多いため。
339行
取得した緯度経度データをバッファにプッシュ(末尾に追加)する
341行
この1行で64進のエンコードポリラインが得られる夢のようなAPI (Google Maps API Ver3 は素晴らしい! 過去の苦労は何だったのだろう? http://gps-walk.com/niki/110906/index.html
343行
64進文字列の中に含まれる特殊文字 '\' の機能をエスケープ(無効化)するために '\' を '\\' に変換
348~353行
日付形式を成型する
356~362行
時刻形式を成型する。分・秒はいつも2桁だが、時は1桁と2桁の場合があるので要注意
364~367行
日時データ作成。本命部分の JavaScript ここまで。
369~375行
このツールHTAのスタイルシート。 以上でヘッダー部終り
378~457行
このツールHTAのbody部分。 データ入力用のフォームになっています

現在位置: ホーム > GPS軌跡を自動作成