GPXファイルをQGISとMapLibreで読み込む


日本はイルミネーション輝く冬が到来。海外ではFOSS4Gのイベントが開催されていますが、私は日本で今日も路線バスに乗って出かけているのであります。

ときたま、路線バスに乗ってGPSロギングをするときがあるのですが、GPSのロギングでよく使われるファイルフォーマットとしてGPXファイルがあります。

この記事では、簡単にGPXファイルの説明と、GPXファイルがQGISやMapLibreで読み込めるのか述べていきます。

## GPXファイルとは

GPXとは  GPS Exchange Format の略で、GPSを使って位置情報を記録するデバイスでよく用いられているXMLのオープンフォーマットです。

GPXでは、主に３種類のタイプが定義できます。下記に各種類のサンプルのGPXファイルを示していますが、1つのファイルにまとめることもできます。GPXの詳しい書き方は [公式ドキュメント](https://www.topografix.com/gpx/1/1/#ns_xsd) をご覧ください。

| 種類 | 概要 | 説明 |
| --- | --- | --- |
| wptType | ウェイポイント | GPSで取得したWGS84座標のポイントを表現しています。 |
| rteType | ルート | 複数のポイントに順序を付与して１つのルートを表現しています。 |
| trkType | トラック | 過去にどのような点をを通ったのかを表現します。各ポイントにタイムスタンプを付与することが多い。 |

### wptType（ウェイポイント） 

`wpt` タグで１つずつポイントの情報を定義しています。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="santen-ichiyon"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1">
    <metadata>
        <name>ExampleGPX</name>
        <author>santen-ichiyon</author>
    </metadata>
    <wpt lat="35.681111" lon="139.766667">
        <ele>3.5</ele>
        <name>Tokyo Station</name>
    </wpt>
    <wpt lat="35.628222" lon="139.738694">
        <ele>3.2</ele>
        <name>Shinagawa Station</name>
    </wpt>
    <wpt lat="35.690833" lon="139.700278">
        <ele>37.6</ele>
        <name>Shinjuku Station</name>
    </wpt>
    <wpt lat="35.729722" lon="139.710861">
        <ele>32.7</ele>
        <name>Ikebukuro Station</name>
    </wpt>
    <wpt lat="35.713889" lon="139.777222">
        <ele>11.2</ele>
        <name>Ueno Station</name>
    </wpt>
</gpx>
```

### rteType（ルート） 

`rte` の中に複数個の `rtept` を定義して、ルートを表現しています。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="santen-ichiyon"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1">
    <metadata>
        <name>ExampleGPX</name>
        <author>santen-ichiyon</author>
    </metadata>
    <rte>
        <name>Example Route</name>
        <rtept lat="35.713586" lon="139.777218">
            <ele>5.1</ele>
        </rtept>
        <rtept lat="35.713451" lon="139.777545">
            <ele>3.3</ele>
        </rtept>
        <rtept lat="35.713334" lon="139.777803">
            <ele>2.8</ele>
        </rtept>
        <rtept lat="35.713107" lon="139.777658">
            <ele>2.9</ele>
        </rtept>
        <rtept lat="35.712846" lon="139.777449">
            <ele>2.8</ele>
        </rtept>
    </rte>
</gpx>
```

### trkType（トラック） 

トラックは、１つの `trk` タグの中に複数のセグメントタグ ( `trkseg` ) が宣言できるというのが特徴です。

```xml
<?xml version="1.0" encoding="UTF-8"?>
<gpx version="1.1" creator="santen-ichiyon"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.topografix.com/GPX/1/1">
    <metadata>
        <name>ExampleGPX</name>
        <author>santen-ichiyon</author>
    </metadata>
    <trk>
        <name>Example Track</name>
        <trkseg>
            <trkpt lat="35.713586" lon="139.777218">
                <ele>5.1</ele>
                <time>2024-12-07T12:00:00Z</time>
            </trkpt>
            <trkpt lat="35.713451" lon="139.777545">
                <ele>3.3</ele>
                <time>2024-12-07T12:00:02Z</time>
            </trkpt>
            <trkpt lat="35.713334" lon="139.777803">
                <ele>2.8</ele>
                <time>2024-12-07T12:00:05Z</time>
            </trkpt>
            <trkpt lat="35.713107" lon="139.777658">
                <ele>2.9</ele>
                <time>2024-12-07T12:00:07Z</time>
            </trkpt>
            <trkpt lat="35.712846" lon="139.777449">
                <ele>2.8</ele>
                <time>2024-12-07T12:00:10Z</time>
            </trkpt>
        </trkseg>
    </trk>
</gpx>
```

## QGISで読み込む

GPXファイルはQGISファイルで読み込むことができます。実際にウェイポイントのファイルをQGISに読み込むんでみると、どのアイテムを読み込むのか選択するダイアログがでてきます。

ウェイポイントしか記載されていないGPXでも、一応ルートとトラック関連のジオメトリ個数を出してくれて、選択もできます。

![追加するレイヤーを選択する画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/cb89eb9b-d676-b73e-7d02-4f12fe19e0a5.png)

「レイヤを追加」をクリックすると、以下の画像のようにQGISでウェイポイントが描画できます（ポイントの大きさを変えて表示しています）。

![ウェイポイントの表示 ©️OpenStreetMap Contributors](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/362aead3-aaff-1b50-0db4-29ad43a51920.png)

同じように、ルートやトラックのデータもそれぞれ読み込みます。ルートとトラックは、ポイントをつなげたラインデータと、それを構成するポイントが表示できます。

ルート単体のサンプルファイル

![ルートの表示 ©️OpenStreetMap Contributors](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/3fd1198f-b225-16d8-51c6-462192730e56.png)

トラック単体のサンプルファイル

![トラックの表示 ©️OpenStreetMap Contributors](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/09a8cd24-8d6d-4414-abc6-25c32ec31141.png)

## MapLibreで読み込む

MapLibreでGPXを読み込むときは、自分でXMLをパースしてGeoJSONなどMapLibreで読み込めるファイル形式に変換する手法もありますが、 [maplibre-gl-vector-text-protocol](https://github.com/jimmyrocks/maplibre-gl-vector-text-protocol) というライブラリを使ってGPXファイルを読み込むという手法もあります。

今回は、後者の手法でGPXファイルを読み込み、MapLibreで地図上にGPXファイルのジオメトリを描画してみます。

フォルダ構成はこんな感じです。

![フォルダ構成](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/387dcced-bcab-3943-172c-bdf6d0ea9441.png)

`index.html` に、 [maplibre-gl-vector-text-protocol](https://github.com/jimmyrocks/maplibre-gl-vector-text-protocol) のスクリプトを読み込みます。

```index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>ExampleGPXMapLibre</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://unpkg.com/maplibre-gl@4.1.3/dist/maplibre-gl.js"></script>
    <link rel="stylesheet" href="https://unpkg.com/maplibre-gl@4.1.3/dist/maplibre-gl.css">
    <link href="./index.css" rel="stylesheet">
</head>

<body>
    <div id="map"></div>
</body>
<!-- maplibre-gl-vector-text-protocolの外部スクリプトを追加 -->
<script src="https://loc8.us/maplibre-gl-vector-text-protocol/dist/maplibre-gl-vector-text-protocol.js"></script>
<script src="./index.js"></script>
</html>
```

`index.css` は省略しますが、HTMLの `map` ドキュメントを画面いっぱいに広げているスタイルを書いています。

`index.js` ではMapLibreの初期化をして、その後に GPXファイルのプロトコルを追加しています。

マップ読み込み後の処理として、いつものようにSourceとLayerを追加します。ここでGPXファイルを読み込む際には、addSource の data で `gpx://` をつけて GPX ファイルを定義します。

また、GPXをパースしたあとにGeoJSONに変換される仕組みとなっているため、 addSource の type には `"geojson"` を定義します。

addLayerでライン用のレイヤーとポイント用のレイヤーを定義することで、QGISと同じようにポイントとラインの描画ができます。

```index.js
var map = new maplibregl.Map({
  container: "map",
  style: "https://tile.openstreetmap.jp/styles/osm-bright-ja/style.json",
  center: [139.777218, 35.713586],
  zoom: 19,
});

// GPXファイルを読み込むためのプロトコルを追加
maplibregl.addProtocol("gpx", VectorTextProtocol.VectorTextProtocol);

map.on("load", () => {
  map.addSource("sample-gpx-source", {
    // GeoJSONに変換されるため、typeは "geojson" を定義
    type: "geojson",
    // gpx:// をつける必要あり
    data: "gpx://./data/trkTypeSample.gpx",
  });
  
  // ラインを描画 
  map.addLayer({
    id: "sample-gpx-line-layer",
    type: "line",
    source: "sample-gpx-source",
    paint: {
      "line-color": "#000",
      "line-width": 2,
    },
  });

  // ポイントを描画
  map.addLayer({
    id: "sample-gpx-point-layer",
    type: "circle",
    source: "sample-gpx-source",
    paint: {
      "circle-color": "#000",
      "circle-radius": 5,
    },
  });
});

```


このように記述して実際に動かしてみると、GPXファイルに記録されている点と、それをつなぎ合わせたラインが表示されます。

![MapLibreでポイントとラインを表示した画像 ©️OpenStreetMap Contributors](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93706/d723ea7d-843f-df43-5bd2-197365c19d7c.png)