Skip to main content

Walking Tracker

Animated Walking Tracker with Stats Requires Trimble Maps v4.0.0 or later.

<!doctype html>
<html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.5.css" />
        <script src="https://maps-sdk.trimblemaps.com/v4/trimblemaps-4.2.5.js"></script>
        <script src="https://cdn.jsdelivr.net/npm/@turf/turf@6/turf.min.js"></script>
        <style>
            body { margin: 0; padding: 0; }

            #map {
                position: absolute;
                top: 0;
                bottom: 0;
                width: 100%;
            }

            #stats {
                position: absolute;
                top: 10px; left: 10px;
                background: rgba(0,0,0,0.6);
                color: #fff;
                padding: 10px 15px;
                border-radius: 8px;
                font-family: system-ui, sans-serif;
           }
        </style>
    </head>
    <body>
  <div id="map"></div>
  <div id="stats">
    <div><b>Walking Tracker</b></div>
    <div id="distance">Distance: 0.00 km</div>
    <div id="time">Time: 0s</div>
  </div>

  <script>
    TrimbleMaps.setAPIKey('0D8BA43647605743A5FB4B225664EF0F');

    const map = new TrimbleMaps.Map({
      container: 'map',
      center: [-73.968285, 40.785091], // Central Park
      zoom: 13,
      pitch: 50,
      attributionControl: false,
      hash: true,
    });

    // Walking route
    const routeCoords = [
      [-73.9731, 40.7644],
      [-73.9712, 40.7681],
      [-73.9690, 40.7725],
      [-73.9660, 40.7770],
      [-73.9655, 40.7822],
      [-73.9685, 40.7851],
      [-73.9735, 40.7870],
      [-73.9770, 40.7840],
      [-73.9790, 40.7785],
      [-73.9780, 40.7715],
      [-73.9750, 40.7670],
      [-73.9731, 40.7644] // loop back
    ];

    const route = turf.lineString(routeCoords);
    const routeLength = turf.length(route, {units: 'kilometers'});

    let progress = 0;
    let distanceTravelled = 0;
    const speed = 0.0001; // fraction of route per frame

    const startTime = Date.now();

    map.on('load', async () => {

      // Load custom walking icon
      const img = await map.loadImage('https://cdn-icons-png.flaticon.com/512/1077/1077012.png');
      map.addImage('walker-icon', img.data, {sdf: false});

      // Route line
      map.addSource('walk-route', {
        type: 'geojson',
        data: route
      });

      map.addLayer({
        id: 'walk-route-line',
        type: 'line',
        source: 'walk-route',
        paint: {
          'line-color': '#ff7f0e',
          'line-width': 4,
          'line-dasharray': [2, 2]
        }
      });

      // Walker icon
      map.addSource('walker', {
        type: 'geojson',
        data: turf.point(routeCoords[0])
      });

      map.addLayer({
        id: 'walker',
        source: 'walker',
        type: 'symbol',
        layout: {
          'icon-image': 'walker-icon', // ✅ custom loaded image
          'icon-size': 0.08, // adjust size
          'icon-rotate': ['get', 'bearing'],
          'icon-rotation-alignment': 'map'
        }
      });

      function animate() {
  progress += speed;
  if (progress > 1) progress = 0; // loop again

  const dist = progress * routeLength;
  const along = turf.along(route, dist, {units: 'kilometers'});
  const next = turf.along(route, Math.min(dist + 0.001, routeLength), {units: 'kilometers'});

  if (!along || !along.geometry) return;

  const bearing = turf.bearing(along, next);

  // Update walker position smoothly
  map.getSource('walker').setData({
    type: 'Feature',
    geometry: along.geometry,
    properties: {bearing}
  });

  // Update stats
  distanceTravelled = dist;
  const elapsedSec = Math.floor((Date.now() - startTime) / 1000);

  document.getElementById('distance').innerText =
    `Distance: ${distanceTravelled.toFixed(2)} km`;
  document.getElementById('time').innerText = `Time: ${elapsedSec}s`;

  requestAnimationFrame(animate);
}
      animate();
    });
  </script>
    </body>
</html>
Last updated September 5, 2025.