Raspberry Pi Zero2 W でGPSロガーを作ってみる – 第03回 CSV to GPX 変換編

最近RaspberryPi沼にハマりかけているgoldear@goldear820です。

スマートアクアリウムのススメ① Raspberry Pi で 水温測定【DS18B20】
北海道で買ってきたまりもをこぢんまりと育てるつもりが、どんどんアクアリウムにハマって後戻りできない状態のgoldear@goldear820です。 アクアリウムで非常に重要になってくるのが「水温管理」ですが、我が家はNASが24時間稼働して...
Raspberry Pi Zero2 W でGPSロガーを作ってみる - 第02回 実装・動作確認編
重い腰を上げて自作GPSロガー作成にやっとこ着手したgoldear@goldear820です。 自宅環境で上手くGPSが受信できない、Raspberry Pi Zero WH が非力過ぎて無線でSSH接続すると反応が悪いなど、言い訳でしかあ...

前回、ようやくGPSロガーとして形になりましたが、記録したCSVファイルではGoogleMapにインポートすることができません。

GPS情報をわざわざ記録するのは「地図に重ね合わせて楽しみたい」というものなので、このままでは非常に残念なCSVファイルだけが残ります。

今回はCSVファイルを、GoogleMapにインポートできる形式GPXファイルに変換していきます。またRaspberryPiは特に必要としないため、python実行環境であればWindows環境でもOKです。

スポンサーリンク

予備知識

CSV->GPX変換するにあたり、GPXフォーマットを知っておく必要があります。大まかなフォーマットは手持ちのGPXファイルをテキストエディタで開けば大体分かります。詳細仕様については以下を確認してください。

GPX 1.1 Schema Documentation

GPSで取得した位置情報はDMM(Degree Minute)でCSVに記録されますが、GPX形式ではDEG(Degree)で記述するため変換が必要です。以下のページが参考になり、分かりやすいです。

緯度経度DMS/DEG形式変換ツール『latlon』 arison.jp
緯度(Latitude)・経度(Longitude)を入力し、DMS (Degree Minute Second)形式(60進数)とDEG (Degree)形式(10進数)を変換するツールを作りました。

GPXファイルはXML記述されており、pythonに組み込まれている標準ライブラリを「ElementTree」を用いてGPXファイルを生成します。以下がpython公式のドキュメントになります。

xml.etree.ElementTree --- ElementTree XML API
Source code: Lib/xml/etree/ElementTree.py xml.etree.ElementTree モジュールは、XML データを解析および作成するシンプルかつ効率的な API を実装しています。 チュートリアル...

ElementTreeでGPXファイルを生成すると改行が入らないため、書込みだけはpythonに組み込まれている標準ライブラリ「minidom」を用います。以下がpython公式のドキュメントになります。

xml.dom.minidom --- 最小限の DOM の実装
ソースコード: Lib/xml/dom/minidom.py xml.dom.minidom は、 Document Object Model インターフェースの最小の実装です。他言語の実装と似た API を持ちます。このモジュールは、完全...

実装

pythonプログラムの大まかな内容としては、CSVファイルを指定して、ファイル先頭から一行読み取り、経度緯度をtrkptの属性として付与し、高度・速度をサブ要素の値として記述します。これをCSVファイル末尾まで繰り返すことでGPXファイルに変換します。

CSVに保存したGPS情報群は第02回のソースコードに記載してあるので参考にどうぞ。

Raspberry Pi Zero2 W でGPSロガーを作ってみる - 第02回 実装・動作確認編
重い腰を上げて自作GPSロガー作成にやっとこ着手したgoldear@goldear820です。 自宅環境で上手くGPSが受信できない、Raspberry Pi Zero WH が非力過ぎて無線でSSH接続すると反応が悪いなど、言い訳でしかあ...
#!/usr/bin/env python3
# encoding: utf-8

## common
import os
from datetime import datetime as dt
import xml.etree.ElementTree as ET
import xml.dom.minidom as md
import re

INPUT_CSV  = "2024-01-01.csv"
OUTPUT_GPX = "2024-01-01.gpx"

GPX_VER = "1.1"
CREATOR = "goldear - https://goldear.net"
XMLNS_XSD = "https://www.w3.org/2001/XMLSchema"
XMLNS_XSI = "https://www.w3.org/2001/XMLSchema-instance"
XSI_SL    = "http://www.topografix.com/GPX/1/1/gpx.xsd"

####################################
# SUB FUNCTION
def get_date(file):
    timestamp = os.path.getmtime(file)
    date = dt.fromtimestamp(timestamp)
    return date

def dmm2deg(dmm_lat, dmm_lon):

    # Convert DMM(Degree Minute) -> DEG(Degree)
    ## Latitude: decimal -90.0 <= value <=90.0
    d_lat       = dmm_lat[0:2]
    int_m_lat   = str(dmm_lat[3:5])
    sub_m_lat   = "." + str(dmm_lat[5:])
    float_m_lat = float(int_m_lat + sub_m_lat)
    m2d_lat     = float_m_lat/60
    lat         = float(d_lat) + m2d_lat

    deg_lat = str(lat)

    ## Longitude: decimal -180.0 <= value <=180.0
    d_lon       = dmm_lon[0:3]
    int_m_lon   = str(dmm_lon[4:6])
    sub_m_lon   = "." + str(dmm_lon[6:])
    float_m_lon = float(int_m_lon + sub_m_lon)
    m2d_lon     = float_m_lon/60
    lon         = float(d_lon) + m2d_lon

    deg_lon = str(lon)

    return deg_lat, deg_lon


####################################
# MAIN FUNCTION
def csv2gpx():

    ## Get time 
    date = get_date(INPUT_CSV)
    s_date = date.strftime("%y-%m-%d")
    s_time = date.strftime("%H:%M:%S")

    ## Create ROOT
    root = ET.Element("gpx")
    root.set("version", GPX_VER)
    root.set("creator", CREATOR)
    root.set("xmlns:xsd", XMLNS_XSD)
    root.set("xmlns:xsi", XMLNS_XSI)
    root.set("xsi:schemaLocation", XSI_SL)

    ## Set Time
    l_time = ET.SubElement(root, "time")
    l_time.text = str("20" + s_date + "T" + s_time + "+09:00")

    ## Create truck
    l_trk = ET.SubElement(root, "trk")

    ## Create truck segment data
    l_trkseg = ET.SubElement(l_trk, "trkseg")

    ## Read Log 1-line
    with open(INPUT_CSV, mode="r", encoding="utf-8") as f_csv:

        while True:
            l_gpslog = f_csv.readline()

            if not l_gpslog:
                break

            else:
                ### split
                ls_gpslog = re.split("[,*]",l_gpslog)

                if ls_gpslog[2] == "." or ls_gpslog[4] == ".":
                    continue

                ## Create truck point
                l_trkpt= ET.SubElement(l_trkseg, "trkpt")

                ## Convert DMM -> DEG
                lat, lon = dmm2deg(ls_gpslog[2], ls_gpslog[4])

                l_trkpt.set("lat", lat[0:10] )
                l_trkpt.set("lon", lon[0:11] )

                ## Create Elevation
                l_ele = ET.SubElement(l_trkpt, "ele")
                l_ele.text = ls_gpslog[5]

                ## Create time
                l_tr_time = ET.SubElement(l_trkpt, "time")
                l_tr_time.text = ls_gpslog[7][0:20]

                ## Create speed
                l_speed = ET.SubElement(l_trkpt, "speed")
                l_speed.text = ls_gpslog[6]


    ## Organize XML
    XML = md.parseString(ET.tostring(root, "utf-8"))

    ## Write
    with open(OUTPUT_GPX, "w") as f:
        XML.writexml(f, encoding="utf-8", newl="\n", indent="", addindent="    ")


if __name__ == "__main__":
    csv2gpx()

Google Map にインポートしてみる

テスト用に擬似位置情報を用いて、pythonコードを実行してCSVファイルをGPXファイルに変換してみます。

場所はぱらのま第02巻Line.07の描写を参考に抽出しました。
亀戸天神: 35.421017 139.491443
高円寺 : 35.421267 139.391011
有明  : 35.375419 139.473294
王子神社: 35.451306 139.441022
厳島神社: 35.373338 139.405905

2024-01-01 09:00:00,N,35.421017,E,139.491443,100,100,2024-01-01T00:00:00.000Z
2024-01-01 09:00:01,N,35.421267,E,139.391011,101,101,2024-01-01T00:00:01.000Z
2024-01-01 09:00:02,N,35.375419,E,139.473294,102,102,2024-01-01T00:00:02.000Z
2024-01-01 09:00:03,N,35.451306,E,139.441022,103,103,2024-01-01T00:00:03.000Z
2024-01-01 09:00:04,N,35.373338,E,139.405905,103,103,2024-01-01T00:00:03.000Z
2024-01-01 09:00:05,N,35.421017,E,139.491443,104,104,2024-01-01T00:00:04.000Z

変換したGPXファイルをGoogle Map にインポートしてみました。バッチリ変換できています。

GoogleMapへのインポート方法は以下にまとめてありますので参考にどうぞ。

GPSロガー Holux M-241をサイクリングに使ってみた
前回の千葉 手賀沼サイクリングでGPSロガーを使って位置情報を記録してみました。 意外と簡単にGoogleマップに表示させることができますし、走った道も振り返ることができるので面白いです。今回は私が使用してるGPSロガーやツール、Googl...

まとめ

細かい部分(入出力ファイルを指定するなど)は改善の余地ありですが、CSV -> GPXファイル変換の基礎は完成しました。

次はGPSロガーの本体のケーシングやさらなる小型化を図っていきます。もしかすると Zero2 W ではなく Pico W で作り直すかもしれないです。

コメント