I really thought this series was finished, but there you go. In the first part we saw a basic, embedded map and then added information windows and labels. In the most recent part we added overlay data being recorded by a tracking device to show our progress in real-time.
Here’s the thing: a few days ago I realised the tracking information wasn’t being displayed. Initially I thought it might be due to limited connectivity or bandwidth – I was in Peru, at the time – but I then realised it was probably due to the volume of points being queried and overlaid… we’ve come far enough now that this has become a problem. I haven’t checked to see whether the problem was on Garmin’s side or on Google’s – whether the issue was querying the points or processing/displaying the overlay – but the below solution worked well enough for my immediate needs.
To fix the situation, I simply went and split the queries into 6: one for each month of our journey. I might have done this automatically (and I may yet do this in a future post, we’ll see) but the basic manual implementation works well. Basically this means that instead of 2 overlays – one for the planned journey, one for the “actual” one – we now have 7 (i.e. 1 + 6).
Here’s the updated source:
<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=[ENTER_YOUR_KEY_HERE]&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 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);
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) {
// Adds markers to the map with a delay
var delay = 100;
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);
} 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 area of the map we’re in at the moment – we arrived yesterday in Paraty, a lovely town along the coast between Sao Paolo and Rio de Janeiro. I’ll talk more about this in the next post.
Here’s the embedded map (which you can also find on our trip’s website).
Here’s an interesting side-note – well, a cautionary tale, really – for people considering doing something similar for their own trips. If you want to upload tracking information wherever you are in the world, make sure you have the right plan with Garmin. I did not. I signed up for the basic, “safety” plan at $20 a month (at least for the “freedom” version, which allows you to modify your choice month-by-month rarher than being locked in for a year at a time). It turns out that this basic plan does not include unlimited tracking uploads: these get charged at $0.10 each. I only realised this when I received my bill for the month of July, which amounted to $160! Yeesh.
It turns out you need the $35 a month “recreation” plan to avoid these charges. I promptly upgraded and contacted Garmin to see what could be done about the bill. After a few emails they agreed to credit some of the $160 cost (not all of it, but then ultimately it was my own fault for not reading the fine – or even standard-sized – print carefully enough). So if you’re planning on getting a Garmin inReach device to track your location live, in the way we have, please make sure you get the right plan!