GPSトラッカーやInsta360 GPSアクションリモコンから取得したgpxデータ(緯度・経度)は微妙であるがズレが生じる。このままでは使い勝手が悪いので、QGISを使用してgpxデータを修正する方法について説明します。最終的にやりたいことは、以下の様に自分で撮影したStreetViewのブルーライン(青線)を綺麗にしたいということです。
【注意】
・この方法では、GPXのGPSデータに問題があると表示されてStreetViewに投稿できませんでした。投稿できる方法が見つかりましたら、内容を修正したいと思います。
・原因は日時データが無くなるためです。日時データ追加の方法が判明しましたら追記したいと思います。
・修正方法について追記しました、この方法でStreetViewに投稿出来ましたがStreetView内で処理中ですので完了しましたらコメントを追記したいと思います。
QGISにgpxデータをドラッグ&ドロップする
QGISの詳細設定は別のサイトをご覧ください。gpxデータ(ここでのデータはInsta360 GPSアクションリモコンがら取得したgpxデータ)をドラッグ&ドロップすると以下の様になります。レイヤーとしては
・waypoints
・track_points
・rute_points
・tracks
・routes
の5つのレイヤーになります
ちなみに、GoogleMapsと重ねると以下の様になります
赤矢印が移動したルート(うろおぼえですが)で点がGPSで取得したポイントデータになります。ひょっとしたらGPSは正しくて、自分は真っ直ぐ歩いているつもりでも曲がっているのでこんな軌跡になっているかもしれませんが、自分のイメージ通りに修正したいと思います。
gpxデータをESRI Shapefileへ変換を行う
QGISで緯度経度を修正するためにはベクターファイル(ベクトルファイル)であるESRI Shapefile(シェープファイル)にしなければならないので、変換を行います。
・waypoints
・track_points
・rute_points
・tracks
・routes
の5つのレイヤーをドラックして右クリック→エクスポート(x)→新規ファイルに建物を保存(A)を選択する。
【名前を付けてベクタファイルを保存】から、形式を【ESRI Shapefile】にして自由なファイル名を付けてデフォルト設定「初期値」のまま保存します。この際に、上書き保存(旧ファイルが残っている場合に)すると、Shapefileがおかしくなる場合(ラインデータが保存されない等)があるので、上書き保存する場合には旧ファイルは完全に削除しましょう。
コツがありました。(理由はよく分かりませんが以下のとおりに行えばShapefileがLineで生成できます。
(1)gpxデータをQGISへドラッグ&ドロップすると以下の5レイヤがQGISに反映されます
(2)Shitfを押しながらレイヤをマウスクリックして5つのレイヤを選択します
(3)tracksレイヤ上にマウスカーソルを乗せて「右クリック」を押下して、メニューを表示させます。そうして、gpxデータをESRI Shapefileへ出力させます。
保存が完了したら新規QGISのプロジェクトを立ち上げて、先ほど保存したShapefile形式のファイルを読み込みましょう。以下の様な線ができあがれば成功です(線の色や太さはQGISの設定に依存しますので気にしないで良いです)。
Shapefileを編集する
Shapefileで読込が出来ると、編集モードが使えることになります。編集モードとはポリゴンを編集出来るモードで緯度経度も自動的に編集出来ます。メニューの鉛筆マークかShapefileレイヤを右クリックして編集モードを切り替えます(編集可へ)
編集モードを切り替えて(編集可)にしたら、頂点ツールで【現在のレイヤ】を選択します
ラインを拡大して、ライン上にマウスカーソルを置くと編集出来る頂点が選択できます。下記画像は拡大して線を分かり易く細くしています。
右クリックで頂点を選択して、マウスで移動させれば頂点が移動します。
左クリックで頂点を選択して、好きなポイントへ移動させ、左クリックで頂点を確定させます。
Shapefileを保存する
編集モードを終了することで、編集したShapefileを保存することが出来ます。
ESRI Shapefileをへgpxデータに変換を行う
基本的にはgpx→Shapefileと同じです。ただし、Shapefileレイヤは1つだけですので編集が完了したレイヤのみ選択してgpxデータで保存を行います
形式を【GPS eXchange Format[GPX]】に選択して、自由なファイル名を付けてデフォルト設定「初期値」のまま保存します。
「gpxファイルが無効です」と表示される
どうやら、Shapefileには日付フィールドに時刻を格納することはできないため、gpxファイルに戻したときに時刻データが全て無くなってしまうのが問題らしい。ということで、Pythonで時刻を挿入するツールを作成してみた。
左が元のgpxファイル、右がQGISで修正して出力したgpxファイル。時刻データが消えているのが分かる。
タイムスタンプ挿入pythonプログラム
いろいろなサイトを検索したところ、以下の参考サイトから少々手を加えることでタイムスタンプの挿入が出来た。
参考:https://qiita.com/oinu-poppo/items/d2977c24b04c60fd82c7
#!/usr/bin/env python3 # coding: UTF-8 # # モジュールのインポート import os #ファイル操作用ライブラリ import argparse #argparseをインポート import gpxpy #gpxファイルを解析•作成するためのpython用ライブラリ import gpxpy.gpx from datetime import datetime, timedelta #----------------------------------------------------------------------------------------------------------------------------- #Python argparse:ヘルプテキストに改行を挿入するためのカスタムフォーマッタを宣言する #「|n」で改行が設定出来る。 import textwrap as _textwrap class MultilineFormatter(argparse.HelpFormatter): def _fill_text(self, text, width, indent): text = self._whitespace_matcher.sub(' ', text).strip() paragraphs = text.split('|n ') multiline_text = '' for paragraph in paragraphs: formatted_paragraph = _textwrap.fill(paragraph, width, initial_indent=indent, subsequent_indent=indent) + '\n' multiline_text = multiline_text + formatted_paragraph return multiline_text #----------------------------------------------------------------------------------------------------------------------------- #----------------------------------------------------------------------------------------------------------------------------- #引数による変換ファイル名と変換方法を指定する parser = argparse.ArgumentParser(description= """ ---------------------------------------------------------------------------------------------------------|n 【ツール説明】|n Insta360で取得したgpxファイルをQGISで修正した際にShapefileには日付フィールドに時刻を格納することはできないため、|n オリジナルのgpxファイルから日付フィールドを抽出して、修正したgpxファイルにインポートするためのツール|n --------------------------------------------------------------------------------------------------------- """,formatter_class=MultilineFormatter) #parser.add_argumentで受け取る引数を追加していく # 必須の引数 parser.add_argument('arg1', help='QGIS修正 : gpxファイル') # 必須の引数を追加 # 引数を解析 args = parser.parse_args() #----------------------------------------------------------------------------------------------------------------------------- #gpxファイルの読み込み gpx_file_r = open( args.arg1, 'r', encoding='utf-8') #出力用gpxファイルの設定 print( os.path.dirname( args.arg1 ) ) gpx_file_w = open( os.path.dirname( args.arg1 ) + '\Time_Fix.gpx', 'w') #gpxファイルの解析 gpx = gpxpy.parse(gpx_file_r) #----------------------------------------------------------------------------------------------------------------------------- #基準となる時間の設定 #------------------------------------------------------------------------------------------------------ # #観測点県コートを設定 # def menu(): #画面クリア #os.system('cls') print ( "" ) print ( "" ) print ( "QGISで修正を行ったgpxファイルに対して時刻を追加します。原本の時刻を秒まで入力してください" ) print ( "設定時間を秒まで入れてください(2023年8月14日10時01分22秒であれば20230814100122)で入力してください" ) print ( "(月日時分秒は2桁で入力)" ) print ( "" ) yyyymmdd = input('Enter Date&Time: ') return yyyymmdd #------------------------------------------------------------------------------------------------------ #メニュー表示と日付の入力 while True: yyyymmdd = menu() year = yyyymmdd[0:4] #文字の0~4を取得して代入 month = yyyymmdd[4:6] #文字の5~6を取得して代入 day = yyyymmdd[6:8] #文字の7~8を取得して代入 hour = yyyymmdd[8:10] #文字の9~10を取得して代入 min = yyyymmdd[10:12]#文字の11~12を取得して代入 sec = yyyymmdd[12:14]#文字の12~13を取得して代入 # print( year ) # print( month ) # print( day ) # print( hour ) # print( min ) # print( sec ) #数字以外が入ったら処理を行わない #8桁に満たなければ処理を行わない #日付が変な値ならば処理を行わない if yyyymmdd.isdecimal() and len(yyyymmdd) == 14 and \ ( int(month) <= 12 and int(month) >= 1) and \ ( int(day) <= 31 and int(day) >= 1) and \ ( int(hour) <= 23 and int(hour) >= 0) and \ ( int(min) <= 59 and int(min) >= 0) and \ ( int(sec) <= 59 and int(sec) >= 0): break #datetime(year, month, day, hour=0, minute=0, second=0, microsecond=0, tzinfo=None) # (1)initial date time_set = datetime( int(year), int(month), int(day), int(hour), int(min),int(sec)-1) # (2)gps period: 1s gps_delta = timedelta(seconds=1) #--------------以下は、トラックポイントまでループさせるルーチン----------------- #メモリ内部で変換されるので、書き出しをするまでは旧ファイルは有効 # #<trk> # <trkseg> # <trkpt # # </trkpt> # </trkseg> #</trk> # for track in gpx.tracks: for segment in track.segments: for point in segment.points: if point.time == None: # (3) invalid time time_set = time_set + gps_delta point.time = time_set else: # (4) valid time time_set = point.time #print('Point at ({0},{1}) -> {2}'.format(point.latitude, point.longitude, point.time)) gpx_file_w.write(gpx.to_xml()) gpx_file_r.close() gpx_file_w.close()
使い方
コマンドラインで引数に時間情報の無くなったgpxファイルを指定し、コメントが出るので元になるgpx時間情報をセットしてやれば良い。セットする時間は、オリジナル(元)gpxファイルの先頭に時刻があるのでそれを打ち込めば良い。
以下の例なら「20230817022746」となる。