Displaying a round-the-world itinerary with Google Maps – Part 6

Here's a quick recap of the series, so far:

  1. Creating a basic map for our round-the-world itinerary
  2. Adding a photo for each placemark
  3. Adding labels for the approximate timing of the journey
  4. Overlaying the actual path visited from a tracking device
  5. Downloading the actual path in multiple queries

A few weeks ago – while we were in Tahiti, but again in New Zealand – I noticed that the "visited" path from the Garmin site wasn't being integrated properly into the Google Map integrated into our family blog. After a little bit of analysis, I realised that the amount of tracking data being pulled down each time the map was being built was large (upwards of 10MB). I figured it was probably related to the connection speed – the page had trouble getting all that data in time – but of course having a page query 10MB+ plus of data (that will result in the same data for every page loaded in every browser) isn't the best way to build a site.

The itinerary, to date

Attempting to address the problem, I thought about ways to reduce this payload. The obvious one was to take the KML data from previous months of the trip and compress it into a single KMZ. I looked at ways to do this, and found you can load the respective KMLs – which I downloaded via the URLs contained in the source of Part 5, above – into Google Earth using File –> Open:

Paths loaded in Google Earth

From here you can right-click Temporary Places (or whichever place you've moved the imported data to) and select Save Place As… to export the data to a KMZ file.Saving to KMZThe KML files I selected – from July to November – weighed in at a whopping 11.9MB and resulted in a KMZ file of just 380KB: a very respectable compression ratio.

One other thing I decided to fix, while I was at it, was to add some points in for the early part of the trip: I'd only picked up the Garmin inReach Explorer+ in West Hartford, so nothing was tracked prior to that point. I started trying to add these points inside Google Earth, but found that really hard: I ended up handrolling a KML with the points I wanted to add and then included these as a separate overlay. There weren't many, so adding the overhead of an additional 6KB KML file seemed the simplest way to go.

Here's the KML file, placed in the same folder as the HTML and called pre-tracking.kml:

<?xml version="1.0" encoding="UTF-8"?>

<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom">

<Folder>

  <name>Temporary Places</name>

  <open>1</open>

  <Folder>

    <name>Temporary Places</name>

    <open>1</open>

    <Document>

      <name>Pre-Garmin Tracking</name>

      <open>1</open>

      <Style id="linestyle_857985">

        <LineStyle>

          <color>ffff5500</color>

        </LineStyle>

      </Style>

      <Style id="style_8579852">

        <IconStyle>

          <color>ffff5500</color>

          <Icon>

            <href>http://maps.google.com/mapfiles/kml/paddle/wht-blank.png</href>

          </Icon>

        </IconStyle>

        <BalloonStyle>

          <text><![CDATA[<table><tr><td>Id</td><td> $[Id] </td></tr><tr><td>Time</td><td> $[Time] </td></tr><tr><td>Time UTC</td><td> $[Time UTC] </td></tr><tr><td>Name</td><td> $[Name] </td></tr><tr><td>Map Display Name</td><td> $[Map Display Name] </td></tr><tr><td>Device Type</td><td> $[Device Type] </td></tr><tr><td>IMEI</td><td> $[IMEI] </td></tr><tr><td>Incident Id</td><td> $[Incident Id] </td></tr><tr><td>Latitude</td><td> $[Latitude] </td></tr><tr><td>Longitude</td><td> $[Longitude] </td></tr><tr><td>Elevation</td><td> $[Elevation] </td></tr><tr><td>Velocity</td><td> $[Velocity] </td></tr><tr><td>Course</td><td> $[Course] </td></tr><tr><td>Valid GPS Fix</td><td> $[Valid GPS Fix] </td></tr><tr><td>In Emergency</td><td> $[In Emergency] </td></tr><tr><td>Text</td><td> $[Text] </td></tr><tr><td>Event</td><td> $[Event] </td></tr></table>]]></text>

        </BalloonStyle>

      </Style>

      <Folder>

        <name>Kean Walmsley</name>

        <open>1</open>

        <Placemark>

          <name>Geneva Airport</name>

          <TimeStamp><when>2017-07-07T12:42:45Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>6.109156399971248,46.23700969987675,0</coordinates>

          </Point>

        </Placemark>   

        <Placemark>

          <name>Marin-Epagnier</name>

          <TimeStamp><when>2017-07-02T12:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>7.001589598912035,47.00918080225755,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Heathrow Terminal 5</name>

          <TimeStamp><when>2017-07-02T16:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-0.4879806004656406,51.47146600045286,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Washington Dulles</name>

          <TimeStamp><when>2017-07-02T23:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-77.45653879988311,38.95311619998505,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>University Inn, Washington DC</name>

          <TimeStamp><when>2017-07-03T00:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-77.05284809999405,38.90030650017117,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Union Station</name>

          <TimeStamp><when>2017-07-04T12:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-77.00250794999864,38.90428664917936,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Penn Station, NYC</name>

          <TimeStamp><when>2017-07-04T16:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-73.99352333750556,40.75056537490029,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Sunnyside</name>

          <TimeStamp><when>2017-07-04T20:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-73.91963239878011,40.74327590022647,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Times Square</name>

          <TimeStamp><when>2017-07-04T22:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-73.98512727848284,40.75889951208065,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Central Park, NYC</name>

          <TimeStamp><when>2017-07-05T00:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-73.96535509973162,40.78286469982217,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Hartford</name>

          <TimeStamp><when>2017-07-06T12:00:00Z</when></TimeStamp>

          <styleUrl>#style_8579852</styleUrl>

          <Point>

            <altitudeMode>absolute</altitudeMode>

            <coordinates>-72.68740885096805,41.76193520621887,0</coordinates>

          </Point>

        </Placemark>

        <Placemark>

          <name>Kean Walmsley</name>

          <description>Kean Walmsley&apos;s track log</description>

          <styleUrl>#linestyle_857985</styleUrl>

          <LineString>

            <tessellate>1</tessellate>

            <coordinates>7.001589598912035,47.00918080225755,0 -0.4879806004656406,51.47146600045286,0 -77.45653879988311,38.95311619998505,0 -77.05284809999405,38.90030650017117,0 -77.00250794999864,38.90428664917936,0 -73.99352333750556,40.75056537490029,0 -73.91963239878011,40.74327590022647,0 -73.98512727848284,40.75889951208065,0 -73.96535509973162,40.78286469982217,0 -73.98512727848284,40.75889951208065,0 -72.68740885096805,41.76193520621887,0</coordinates>

          </LineString>

        </Placemark>

      </Folder>

    </Document>

  </Folder>

</Folder>

</kml>

Here's the updated HTML file:

<html>

  <head>

    <style>

    html,

    body {

      padding: 0;

      margin: 0;

    }

    #map {

      height: 100%;

      width: 100%;

      overflow: hidden;

      float: left;

      border: thin solid #333;

    }

    h3 {

      margin: 0 0 5px 0;

      white-space: nowrap;

      overflow: hidden;

      text-overflow: ellipsis;

    }

    p {

      margin: 0 0 10px 0;

      white-space: nowrap;

      overflow: hidden;

      text-overflow: ellipsis;

    }

    </style>

  </head>

  <body>

    <div id="map"></div>

    <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDtsYI6-syxALgcCkz3hjsAnzK8_4C_OSc&libraries=places&callback=initMap"></script>

    <script type='text/javascript'>

    var map;

    var infowindow;

    var service;

    var overlayBase = 'https://inreach.garmin.com/feed/Share/mondeEnPoche?';

    var locals = [ 'pre-tracking.kml', '2017-07-to-12.kmz' ];

    var overlays = [

      // 'd1=2017-07-01T00:00Z&d2=2017-08-01T00:00Z',

      // 'd1=2017-08-01T00:00Z&d2=2017-09-01T00:00Z',

      // 'd1=2017-09-01T00:00Z&d2=2017-10-01T00:00Z',

      // 'd1=2017-10-01T00:00Z&d2=2017-11-01T00:00Z',

      // 'd1=2017-11-01T00:00Z&d2=2017-12-01T00:00Z',

      'd1=2017-12-01T00:00Z&d2=2018-01-05T00:00Z'

    ];

 

    // Data for the markers consisting of a name, a LatLng and a zIndex for the

    // order in which these markers should display on top of each other.

    var stops = [

      ['Marin-Epagnier', 'ChIJt4RhN0UJjkcR9dxtPHTvIRY', 47.0091808, 7.0015896, 1, 'Start & End'],

      ['Washington', 'ChIJW-T2Wt7Gt4kRKl2I1CJFUsI', 38.9071923, -77.0368707, 2],

      ['New York', 'ChIJOwg_06VPwokRYv534QaPC8g', 40.7127837, -74.0059413, 3],

      ['West Hartford', 'ChIJ_RQEifWs54kRfDtRDlPX-Wc', 41.7620842, -72.7420151, 4],

      ['Boston', 'ChIJGzE9DS1l44kRoOhiASS_fHg', 42.3600825, -71.0588801, 5, 'July'],

      ['Toronto', 'ChIJpTvG15DL1IkRd8S0KlBVNTI', 43.653226, -79.3831843, 6],

      ['Bozeman', 'ChIJE4i6T0xERVMRqmA792TQ9WM', 45.6769979, -111.0429339, 7],

      ['Yellowstone National Park', 'ChIJVVVVVVXlUVMRu-GPNDD5qKw', 44.427963, -110.588455, 8],

      ['Grand Teton National Park', 'ChIJqRtdyZ5RUlMRN6ORzI64oKU', 43.7904282, -110.6817627, 9],

      ['Salt Lake City', 'ChIJ7THRiJQ9UocRyjFNSKC3U1s', 40.7607793, -111.8910474, 10],

      ['Bryce Canyon', 'ChIJbUw47h9pNYcRYv1Jemw3nHU', 37.6283161, -112.1676947, 11],

      ['Zion National Park', 'ChIJ2fhEiNDqyoAR9VY2qhU6Lnw', 37.2982022, -113.0263005, 12],

      ['Las Vegas', 'ChIJ0X31pIK3voARo3mz1ebVzDo', 36.1699412, -115.1398296, 13],

      ['Death Valley', 'ChIJsf-PHqI5x4ARJd0j14NziRw', 36.5322649, -116.9325408, 14],

      ['Sequoia National Park', 'ChIJeWUZLX37v4ARZPQen_nfCkQ', 36.4863668, -118.5657516, 15],

      ['Big Sur', 'ChIJVVikTfuPjYARYuO38cfXpRY', 36.2704212, -121.807976, 16],

      ['Monterey', 'ChIJkfu1cFLkjYARXj1K2AlJSO4', 36.6002378, -121.8946761, 17],

      ['San Francisco', 'ChIJIQBpAG2ahYAR_6128GcTUEo', 37.7749295, -122.4194155, 18, 'August'],

      ['Lima', 'ChIJ3-EpLOzDBZERRBEzku1Ooak', -12.0463667, -77.0427891, 19],

      ['Machu Picchu', 'ChIJVVVViV-abZERJxqgpA43EDo', -13.1631412, -72.5449629, 20],

      ['Cusco', 'ChIJMYRZJtjVbZERXTEYI8yWqSo', -13.53195, -71.9674626, 21],

      ['S�o Paulo', 'ChIJ0WGkg4FEzpQRrlsz_whLqZs', -23.5505199, -46.6333094, 22],

      ['Rio de Janeiro', 'ChIJW6AIkVXemwARTtIvZ2xC3FA', -22.9068467, -43.1728965, 23, 'September'],

      ['Iguazu Falls', 'ChIJbRuqowzq9pQRfphenBd1e5E', -25.695259, -54.4388549, 24],

      ['C�rdoba', 'ChIJaVuPR1-YMpQRkrBmU5pPorA', -31.4200833, -64.1887761, 25],

      ['Parque Provincial Ischigualasto', 'ChIJwynmBT3sgpYR0J11F_1O5cw', -30.167266,-67.9860327, 26],

      ['Parque Nacional Talampaya', 'ChIJUUxbf6rPgpYRaEkBxpGDANQ', -29.8906226, -67.853468, 27],

      ['Catamarca', 'ChIJzZ8PHb8oJJQRGoYJFkvdHn4', -28.469581, -65.7795441, 28],

      ['San Miguel de Tucum�n', 'ChIJA2nF1pI3IpQRJ2XFtZJbjfg', -26.8082848, -65.2175903, 29],

      ['Salta', 'ChIJ-bdRUaPDG5QRBvKH1SyZzaU', -24.7821269, -65.4231976, 30],

      ['Salar de Uyuni', 'ChIJh9rdHuC6_5MRkFuFng0T5RI', -20.1595348, -67.4054025, 31],

      ['San Pedro de Atacama', 'ChIJP78qqXpMqJYR0Zf5rExh9Ho', -22.9087073, -68.1997156, 32],

      ['Pan de Az�car National Park', 'ChIJM6BM4cewvJYRbC7GcVat_6U', -26.177565, -70.5495396, 33],

      ['Ra�l Marine Balmaceda', 'ChIJ4V-JqObIkZYRiGptmZGVUn8', -29.9695076, -71.3416309, 34],

      ['Santiago', 'ChIJuzrymgbQYpYRl0jtCfRZnYc', -33.4378305, -70.6504492, 35],

      ['Easter Island', 'ChIJK67UqBfwR5kRti0qwO2z5bs', -27.112723, -109.3496865, 36],

      ['Tahiti', 'ChIJTddtfNB1GHQREVfDCXp6wJs', -17.6509195, -149.4260421, 37],

      ['Auckland', 'ChIJ--acWvtHDW0RF5miQ2HvAAU', -36.8484597, 174.7633315, 38],

      ['Rotorua', 'ChIJK7L2gj2Ybm0RMZmjQ2HvAAU', -38.1368478, 176.2497461, 39, 'October'],

      ['Wellington', 'ChIJy3TpSfyxOG0RcLQTomPvAAo', -41.2864603, 174.776236, 40],

      ['Paparoa National Park', 'ChIJbZoxICBxJW0RIPF5hIbvAAU', -42.1632433, 171.366731, 41],

      ['Queenstown', 'ChIJX96o1_Ed1akRAKZ5hIbvAAU', -45.0311622, 168.6626435, 42],

      ['Sydney', 'ChIJP5iLHkCuEmsRwMwyFmh9AQU', -33.8688197, 151.2092955, 43],

      ['Brisbane', 'ChIJM9KTrJpXkWsRQK_e81qjAgQ', -27.4697707, 153.0251235, 44],

      ['Cairns', 'ChIJEySiW1VieGkRYHggf_HuAAQ', -16.9185514, 145.7780548, 45],

      ['Kuala Lumpur', 'ChIJ5-rvAcdJzDERfSgcL1uO2fQ', 3.139003, 101.686855, 46],

      ['Singapore', 'ChIJdZOLiiMR2jERxPWrUs9peIg', 1.352083, 103.819836, 47],

      ['Coimbatore', 'ChIJtRyXL69ZqDsRgtI-GB7IwS8', 11.0168445, 76.9558321, 48],

      ['Kodaikanal', 'ChIJhwMKf2NmBzsRPMFYNzfp-p8', 10.2381136, 77.4891822, 49],

      ['Bangalore', 'ChIJbU60yXAWrjsR4E9-UejD3_g', 12.9715987, 77.5945627, 50, 'November'],

      ['Durban', 'ChIJt2G8AQCq9x4RgW6qxEZVp8w', -29.8586804, 31.0218404, 51],

      ['Lesotho', 'ChIJ64xf1idIjB4Rsx7ReLhXLSM', -29.609988, 28.233608, 52, 'December'],

      ['Addo Elephant National Park', 'ChIJY2nuzYRPex4RCsT--8cm454', -33.4833333, 25.75, 53],

      ['Tsitsikamma', 'ChIJaTwmTQ5ueR4R5_kNGLX6RBs', -32.2178721, 26.5772048, 54],

      ['Knysna', 'ChIJ2QwBlkDqeB4Rzc5QdeG5Kr4', -34.0350856, 23.0464693, 55],

      ['Oudtshoorn', 'ChIJtRO16obB1R0RYesIjnRHQ40', -33.6007225, 22.2026347, 56],

      ['Franschhoek', 'ChIJz7IFaAe9zR0R-bJW01SGtDw', -33.8974833, 19.1523292, 57],

      ['Stellenbosch', 'ChIJpeKIUfeyzR0R4mvj3gCqCXA', -33.9321045, 18.860152, 58],

      ['Cape Town', 'ChIJ1-4miA9QzB0Rh6ooKPzhf2g', -33.9248685, 18.4240553, 59]

    ];

 

    var labels = [

      ['Start & End', 47.0091808, 7.0015896, 1],

      ['July', 42.409143, -102.280372, 2],

      ['August', 5.247246, -73.979869, 3],

      ['September', -36.753594, -65.018287, 4],

      ['October', -33.622306, 160.985311, 5],

      ['November', 16.921484, 91.724302, 6],

      ['December', -16.011953, 23.167125, 7]

    ];

 

    function initMap() {

      map = new google.maps.Map(document.getElementById('map'), {

        center: new google.maps.LatLng(15, -30),

        zoom: 2,

        mapTypeId: 'satellite'

      });

      infowindow = new google.maps.InfoWindow();

      service = new google.maps.places.PlacesService(map);

 

      loadJS('./maplabel-compiled.js', onInit, document.body);

    }

 

    function loadJS(url, implementationCode, location){

      var scriptTag = document.createElement('script');

      scriptTag.src = url;

      scriptTag.onload = implementationCode;

      scriptTag.onreadystatechange = implementationCode;

      location.appendChild(scriptTag);

    };

 

    function onInit(){

      setMarkers(map, function() {

        var href = window.location.href;

        var dir = href.substring(0, href.lastIndexOf('/')) + '/';

        for (var i = 0; i < locals.length; i++) {

          var kmlLocalOverlayer = new google.maps.KmlLayer(dir + locals[i], {

            suppressInfoWindows: true,

            preserveViewport: true,

            map: map

          });

        }

        for (var i = 0; i < overlays.length; i++) {

          var kmlOverlayer = new google.maps.KmlLayer(overlayBase + overlays[i], {

            suppressInfoWindows: true,

            preserveViewport: true,

            map: map

          });

        }

      });

    }

 

    function setMarkers(map, callback) {

 

      // Adds markers to the map with a delay

 

      var delay = 50;

      for (var i = 0; i <= stops.length; i++) {

        var timeout = i * delay;

 

        // If this is the last segment, just add the line

 

        if (i === stops.length) {

          addConnectingLineWithTimeout(stops[i - 1], stops[0], timeout + delay);

          if (callback) {

            setTimeout(callback, timeout + delay);

          }

        } else if (i >= 0) {

 

          // Otherwise add a marker after a delay, followed by the

          // connecting line to the previous marker, if there is one

 

          addMarkerWithTimeout(stops[i], timeout);

          if (i > 0) {

            addConnectingLineWithTimeout(stops[i], stops[i - 1], timeout + delay);

          }

        }

      }

    }

 

    function addMarkerWithTimeout(stop, timeout) {

      setTimeout(function() {

        var marker = new google.maps.Marker({

          map: map,

          title: stop[0],

          placeId: stop[1],

          position: {

            lat: stop[2],

            lng: stop[3]

          },

          label: stop[4].toString(),

          zIndex: stop[4]

          //animation: google.maps.Animation.DROP, // Cool but too much

        });

 

        // If we have a label listed, find out which and add it to the map

 

        if (stop.length > 5) {

          var idx = labels.findIndex(function(val) {

            return val[0] === stop[5];

          });

          if (idx >= 0) {

            var label = labels[idx];

            addLabelWithTimeout(label[1], label[2], label[0], 0);

          }

        }

 

        // Register the callback for when the marker is clicked

 

        google.maps.event.addListener(marker, 'click', function() {

          onItemClick(event, marker);

        });

      }, timeout);

    }

 

    function addLabelWithTimeout(lat, long, text, timeout) {

      setTimeout(function() {

        var pos = new google.maps.LatLng(lat, long);

        var mapLabel = new MapLabel({

          text: text,

          position: pos,

          map: map,

          fontSize: 14

        });

        mapLabel.set('position', new google.maps.LatLng(lat, long));

      }, timeout);

    }

 

    function addConnectingLineWithTimeout(stop1, stop2, timeout) {

      setTimeout(function() {

        var flightPath = new google.maps.Polyline({

          path: [{

            lat: stop1[2],

            lng: stop1[3]

          }, {

            lat: stop2[2],

            lng: stop2[3]

          }],

          geodesic: true,

          strokeColor: '#D34038',

          strokeOpacity: 1.0,

          strokeWeight: 4

        });

 

        flightPath.setMap(map);

      }, timeout);

    }

 

    // Info window trigger function

 

    function onItemClick(event, pin) {

      service.getDetails({

        placeId: pin.placeId

      }, function(place, status) {

        var cont =

          '<div><h3>' + place.name + '</h3><p>' + place.formatted_address + '</p>' +

          (place.photos && place.photos.length > 0 ?

            ('<img src="' +

              place.photos[0].getUrl({

                'maxWidth': 300,

                'maxHeight': 200

              }) + '" />') : '') +

          '</div>'

        infowindow.setContent(cont);

        infowindow.open(map, pin);

      });

    }

    </script>

  </body>

</html>

Here's the embedded map (which you can also find on our trip's website).

The site seems to work well again, now: once the trip is done I'll integrate the last month of tracking data into the KMZ, to avoid any further visits to the Garmin service.

2 responses to “Displaying a round-the-world itinerary with Google Maps – Part 6”

  1. Very good Kean.
    A couple of comments:
    - I thought you had made an unplanned detour via Miami airport? Maybe you didn't have time to switch on your GPS in the middle of the night!!
    - Tsitsikamma (number 54 in your itinerary) is a lot nearer the coast than this.
    Best wishes for the closing stages of your grand tour.
    Doug

    1. Hi Doug,

      Nice to hear from you!

      You have a good memory: the GPS was on, but I couldn't get near enough to the outside to see sky (we were in a huge rush to make the connection).

      I remember having trouble with the location for Tsitsikamma - I'll take another look when I get some time.

      Have a great Christmas,

      Kean

Leave a Reply to Doug Hudson Cancel reply

Your email address will not be published. Required fields are marked *