サイクルコンピュータのGPSログデータをMapbox GL JSで表示してみました

# はじめに
- サイクルコンピュータ（Garmin）のGPSログデータ（gpxファイルをgeojsonファイルに変換したデータ）をMapbox GL JSで表示してみました。
- Mapbox GL JSでの表示部分は、[こちらのMapboxさんのドキュメント](https://docs.mapbox.com/jp/mapbox-gl-js/example/live-update-feature/)を参考にさせていただきました。
- Mapbox GL JSのバージョンはv2になります。
- Mapboxのアクセストークンが必要になります。

# アウトプットイメージ
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">MapboxさんのExamplesを参考にさせていただきながら、サイコン（<a href="https://twitter.com/GarminJP?ref_src=twsrc%5Etfw">@GarminJP</a>）のGPSログデータをMapboxGLJS(v2)のlinelayerで表示してみた😀Relive（<a href="https://twitter.com/relivecc?ref_src=twsrc%5Etfw">@relivecc</a>）みたいに表示が滑らかなのが素晴らしい📝<a href="https://twitter.com/hashtag/Mapbox?src=hash&amp;ref_src=twsrc%5Etfw">#Mapbox</a><a href="https://t.co/96W5mgbZAH">https://t.co/96W5mgbZAH</a> <a href="https://t.co/X2pnqAmVQ3">pic.twitter.com/X2pnqAmVQ3</a></p>&mdash; shi_works🌕 (@syanseto) <a href="https://twitter.com/syanseto/status/1419972944487337984?ref_src=twsrc%5Etfw">July 27, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 前提条件
- GPSログデータ（gpxファイル）は、予めQGIS等でgeojsonファイルに変換したデータを用いています。
- gpxファイルの中からtracksというLineString形式（ジオメトリオブジェクトのtype）のデータをgeojsonファイルに変換しています。
- QGISでのLineString形式のデータ（geojsonファイル）の抽出イメージは以下のとおりです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/15bf7ecf-bf52-bc0c-a444-5579a1a6ea82.png)
- 抽出したgeojsonファイルの形式は以下のとおりです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/205045/c63ec4dd-bcf9-c57d-4e12-a2dd867979a2.png)
- GPSログデータ（geojsonファイル）を読み込んでいます。
- また、[3D地形データ](https://docs.mapbox.com/mapbox-gl-js/example/add-terrain/)と[3D建物データ](https://docs.mapbox.com/jp/mapbox-gl-js/example/3d-buildings/)を読み込んでいます。

# html
```html:GPSLog_Animation.html
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>GPSログアニメーション</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
    <link href="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.css" rel="stylesheet">
    <script src="https://api.mapbox.com/mapbox-gl-js/v2.3.1/mapbox-gl.js"></script>
    <link rel="stylesheet" href="style.css" />
</head>

<body>
    <div id="map"></div>
    <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="script.js"></script>
</body>

</html>
```
# CSS
```CSS:style.css
body {
  margin: 0;
  padding: 0;
}

#map {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
}
```
# JavaScript
```javascript:script.js
mapboxgl.accessToken = 'Mapboxのアクセストークンを入力してください';
var map = new mapboxgl.Map({
    container: 'map',
    style: 'mapbox://styles/mapbox-map-design/ckhqrf2tz0dt119ny6azh975y',
    zoom: 0
});

map.on('load', function () {
    map.addSource('mapbox-dem', {
        'type': 'raster-dem',
        'url': 'mapbox://mapbox.mapbox-terrain-dem-v1',
        'tileSize': 512,
        'maxzoom': 14
    });
    // add the DEM source as a terrain layer with exaggerated height
    map.setTerrain({ 'source': 'mapbox-dem', 'exaggeration': 1.5 });

    // add a sky layer that will show when the map is highly pitched
    map.addLayer({
        'id': 'sky',
        'type': 'sky',
        'paint': {
            'sky-type': 'atmosphere',
            'sky-atmosphere-sun': [0.0, 0.0],
            'sky-atmosphere-sun-intensity': 15
        }
    });

    // 既存の3D建物レイヤを追加
    map.addLayer({
        "id": "3d-buildings",
        "source": "composite",
        "source-layer": "building",
        "filter": ["==", "extrude", "true"],
        "type": "fill-extrusion",
        "minzoom": 10,
        "paint": {
            "fill-extrusion-color": "#ffffff",
            "fill-extrusion-height": [
                "interpolate", ["linear"], ["zoom"],
                10, 0,
                14.05, ["get", "height"]
            ],
            "fill-extrusion-base": [
                "interpolate", ["linear"], ["zoom"],
                10, 0,
                14.05, ["get", "min_height"]
            ],
            "fill-extrusion-opacity": 1.0
        }
    });
    // We use D3 to fetch the JSON here so that we can parse and use it separately
    // from GL JS's use in the added source. You can use any request method (library
    // or otherwise) that you want.
    d3.json(
        'data/activity_6257932835 tracks.geojson',
        function (err, data) {
            if (err) throw err;

            // save full coordinate list for later
            var coordinates = data.features[0].geometry.coordinates;

            // start by showing just the first coordinate
            data.features[0].geometry.coordinates = [coordinates[0]];

            // add it to the map
            map.addSource('trace', { type: 'geojson', data: data });
            map.addLayer({
                'id': 'trace',
                'type': 'line',
                'source': 'trace',
                'paint': {
                    'line-color': 'yellow',
                    'line-opacity': 0.75,
                    'line-width': 7
                }
            });

            // setup the viewport
            map.jumpTo({ 'center': coordinates[0], 'zoom': 14 });
            map.setPitch(30);

            // on a regular basis, add more coordinates from the saved list and update the map
            var i = 0;
            var timer = window.setInterval(function () {
                if (i < coordinates.length) {
                    data.features[0].geometry.coordinates.push(
                        coordinates[i]
                    );
                    map.getSource('trace').setData(data);
                    map.panTo(coordinates[i]);
                    i++;
                } else {
                    window.clearInterval(timer);
                }
            }, 10);
        }
    );
});

// コントロール関係表示
var nav = new mapboxgl.NavigationControl();
map.addControl(nav, 'top-left');

// スケール表示
map.addControl(new mapboxgl.ScaleControl({
    maxWidth: 200,
    unit: 'metric'
}));
```
# 参考文献
https://docs.mapbox.com/jp/mapbox-gl-js/example/live-update-feature/

https://docs.mapbox.com/mapbox-gl-js/example/add-terrain/

https://docs.mapbox.com/jp/mapbox-gl-js/example/3d-buildings/