After creating a basic map for our round-the-world itinerary and then adding photos when placemarkers are clicked, today we’re going to add some handy labels to the map to show rough dates for when we’ll be arriving in a particular location.
This actually proved trickier than expected: there’s an old library called MapLabel that works well with the Google Maps API, but it doesn’t load asynchronously in a straightforward way. So I had to jump through some hoops to load it only when the main Google Maps API is available, avoiding some irritating load errors. I used some code from Stack Overflow for this.
With the loading working well, it was a fairly simple matter to create the text we need at fixed locations in the map. To make it look nice I did complicate my life slightly, and have the label creation triggered by the creation of a particular placemarker. It just makes the whole thing look cleaner.
Here’s the updated code:
<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;
// 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 Nacional Talampaya', 'ChIJUUxbf6rPgpYRaEkBxpGDANQ', -29.8906226, -67.853468, 26],
['Valle de la Luna', 'ChIJ33tfOKhoLZQRTn9HuPJlE5g', -22.9257639, -68.2879926, 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 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 a simple animation of the improved loading:
Here’s the embedded map for you to take a look at yourself:
All that remains with this project is to integrate data from our new Garmin inReach Explorer+ showing where we are on the trip. We’ll see that next time – first I need to pick the device up from a friend’s place in West Hartford (he’s been acting as our Amazon shipping outpost for the trip :-).
In terms of the trip: we’ve just spent our first night in DC, having arrived late on Sunday night and checked into our hotel in Foggy Bottom. In a few short hours we’ll be heading to The Smithsonian, which I’m really looking forward to experiencing with the kids.