Newer
Older
gpx_web_viewer / js / gpx.js
/* gpx.js - display GPX tracks on a leaflet maps
 * see leaflet-gpx https://github.com/mpetazzoni/leaflet-gpx for the underlying leaflet plugin
 */

// keep track of how many tracks we've added
var trackCount = 0
var trackArray = []

var map = L.map('map')

L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
    attribution: 'Map data &copy <a href="http://www.osm.org">OpenStreetMap</a>'
}).addTo(map)

function download(url) {
    const a = document.createElement('a')
    a.href = url
    a.download = url.split('/').pop()
    document.body.appendChild(a)
    a.click()
    document.body.removeChild(a)
}

function sortedAppend(t) {
    // splice might fail "not a function" otherwise
    if(trackArray.length == 0) {
        trackArray.push(t)
        return
    }
    // find idx to splice
    var idx = 0
    for(const track of trackArray) {
        if(t.date > track.date) {
            break
        }
        idx++
    }
    trackArray.splice(idx, 0, t)
    trackCount++
}

function createTrackList(arr) {
    var tracklist = document.getElementById('tracklist')
    arr.forEach((track, idx, array) => {
        const date = track.date
        const text = track.name
        const bounds = track.bounds
        var t = document.createElement('li')
        t.className = 'track'
        t.innerHTML = `<a><date>${date.toISOString().split('T')[0]}</date><span>${text}</span></a>`
        t.onclick = function() { map.fitBounds(bounds) }
        tracklist.appendChild(t)
    })
}

function addGPX(map, gpx, lineColor) {
    new L.GPX(gpx, {
        async: true,
        marker_options: {
            startIconUrl: 'images/pin-icon-start.png',
            endIconUrl: 'images/pin-icon-end.png',
            shadowUrl: 'images/pin-shadow.png'
        },
        polyline_options: {
            smoothFactor: 3,
            color: lineColor,
            opacity: 1,
            weight: 3,
            lineCap: 'round'
        }
    }).on('loaded', function(e) {
        // add track to list
        const track = {
            date: e.target.get_start_time(),
            name: e.target.get_name(),
            bounds: e.target.getBounds()
        }
        sortedAppend(track)
        // fit map to track bounds on loading
        map.fitBounds(e.target.getBounds())
    }).on('mouseover', function(e) {
        var paths = gpx.split("/")
        var filename = paths[paths.length - 1]
        var kms = e.target.get_distance() / 1000
        var movingTime = e.target.get_duration_string_iso(e.target.get_moving_time(), true)
        var movingSpeed = e.target.get_moving_speed()
        var totalAvgTime = e.target.get_duration_string_iso(e.target.get_total_time(), true)
        var totalAvgSpeed = e.target.get_total_speed()
        var tooltip = `
<div class="maptooltip">
    <h3>${e.target.get_name()}</h3>
    <div class="maptooltip-left">
        <b>Distance: ${kms.toFixed(2)} km</b><br>
        Total time: ${totalAvgTime} h<br>
        Moving time: ${movingTime} h<br>
    </div>
    <div class="maptooltip-right">
        <b>Elevation gain: ${e.target.get_elevation_gain().toFixed()} m d+</b><br>
        Avg speed: ${totalAvgSpeed.toFixed(2)} km/h<br>
        Avg moving speed: ${movingSpeed.toFixed(2)} km/h <br>
    </div>
    <div style="clear:both"></div>
    <h4>Click to download: ${filename}</h4>
</div>`
        this.bindTooltip(tooltip, {sticky: true})
        this.openPopup(e.latlng)
    }).on('click', function(e) {
        download(gpx)
    }).addTo(map)
}

const ta = [
    this.addGPX(map, '../tracks/turin_09122022.gpx', 'red'),
    this.addGPX(map, '../tracks/biella_27122022.gpx', 'blue'),
    this.addGPX(map, '../tracks/la_fausto_coppi_2026_lungo.gpx', 'purple')
]

// this.createTrackList(trackArray)

// // wait for all tracks to be loaded, then create tracklist
function sleep(ms) {
    return new Promise(resolve => { setTimeout(resolve, ms); });
}
async function waitForLoading() {
    while (trackArray.length < ta.length) {
        await(sleep(100));
    }
    this.createTrackList(trackArray)
}
waitForLoading()

// todo this should run after addGPX complete