I'm working on a bicycle route planner using Google Maps API.
Everything works as expected, but only for first time a route request is made and displayed on the map. In subsequent requests the Bicycle Layer (shown with green lines on the map) disappears. I would like the Bicycle Layer to be always visible.
I suspect it is to do with the directionsDisplay.setMap(map), but I'm not sure.
I think that the bikeLayer variable needs to be used when the map gets redrawn, but I can't figure out how to do it.
It was created with the help of this video: https://www.youtube.com/watch?v=BkGtNBrOhKU&t=1843s
GitHub hosted version project: https://gregkaighin.github.io/bedfordshire-bicycle-club/routes.html
// Create Bedforshire variable
const bedfordshire = {
lat: 52.02973,
lng: -0.45303
};
// Set the map options
const mapOtions = {
center: bedfordshire,
zoom: 10,
// Disable the default map UI, enable the zoom control
disableDefaultUI: true,
zoomControl: true
};
// Create the map with the bicycle layer enabled
const map = new google.maps.Map(document.getElementById('googleMap'), mapOtions);
var bikeLayer = new google.maps.BicyclingLayer();
bikeLayer.setMap(map);
// Create variables for custom legend icons
const icons = {
bikeTrails: {
name: "Bike lanes & trails",
icon: "assets/img/icons/bike trail.png",
},
bikeFriendlyRoads: {
name: "Bike-friendly roads",
icon: "assets/img/icons/bike friendly road.png",
},
};
// Create the legend and place the icons
const legend = document.getElementById("legend");
for (const key in icons) {
const type = icons[key];
const name = type.name;
const icon = type.icon;
const div = document.createElement("div");
div.innerHTML = '<img src="' + icon + '"> ' + name;
legend.appendChild(div);
}
// Push the legend to the map
map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(legend);
// Create a DirectionsService object to use the route method and get a result for the request
var directionsService = new google.maps.DirectionsService();
// Create a DirectionsRenderer object to create the route
var directionsDisplay = new google.maps.DirectionsRenderer();
// Display the directions on the map
directionsDisplay.setMap(map);
// Define the calcRoute function
function calcRoute() {
// Create a route request
var request = {
origin: document.getElementById('from').value,
destination: document.getElementById('to').value,
travelMode: google.maps.TravelMode.BICYCLING,
unitSystem: google.maps.UnitSystem.IMPERIAL
}
// Pass the request to the .route method
directionsService.route(request, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
// Get the route distance and time and pass to the #output div
const output = document.querySelector('#output');
output.innerHTML = '<div class="alert-info">From: ' + document.getElementById('from').value + '.<br />To: ' + document.getElementById('to').value + '.<br /> Cycling distance <i class="fas fa-biking"></i> : ' + result.routes[0].legs[0].distance.text + '.<br />Duration <i class="fas fa-stopwatch"></i> : ' + result.routes[0].legs[0].duration.text + '.</div>';
// Display the route
directionsDisplay.setDirections(result);
} else {
// Delete the route
directionsDisplay.setDirections({
routes: []
});
// Recenter the map on Bedfordshire
map.setCenter(bedfordshire);
// Show an error message if the route is not possible
output.innerHTML = '<div class="alert-danger"><i class="fas fa-exclamation-triangle"></i> This route is not possible on a bicycle!</div>';
}
});
}
// Create searchBox1 object for the starting place
var input1 = document.getElementById('from');
var searchBox1 = new google.maps.places.SearchBox(input1);
// Bias the SearchBox1 results towards current map's viewport
map.addListener('bounds_changed', () => {
searchBox1.setBounds(map.getBounds());
});
// Create searchBox2 object for the destination
var input2 = document.getElementById('to');
var searchBox2 = new google.maps.places.SearchBox(input2);
// Bias the SearchBox2 results towards current map's viewport
map.addListener('bounds_changed', () => {
searchBox2.setBounds(map.getBounds());
});
/* Form placeholder text */
#from {
font-size: 0.8em;
}
#to {
font-size: 0.8em;
}
/* Size the map and apply box-shadow */
#googleMap {
width: 85vw;
height: 50vh;
margin: 12px auto;
border: 1px solid black;
border-radius: 2% 2% 2% 2%;
}
/* Style the map legend */
#legend {
font-family: Arial, sans-serif;
background: #fff;
background-color: lightgray;
padding: 4px;
margin: 4px;
border: 1px solid black;
border-radius: 2% 2% 2% 2%;
}
#legend img {
vertical-align: middle;
}
/* Style the alert box containing the route details */
.alert-info {
margin: auto;
max-width: fit-content;
text-align: center;
font-size: 0.8em;
background-color: #efefef;
}
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta name="viewport" content="initial-scale=1.0">
<form>
<label for="from" class="control-label direction-input"></label>
<input type="text" id="from" placeholder="Starting place" class="form-control">
<label for="to" class="control-label direction-input"></label>
<input type="text" id="to" placeholder="Destination" class="form-control">
</form>
<button onclick="calcRoute();">Show Route </button>
<div id="map-container">
<div id="googleMap"></div>
<div id="legend">
</div>
</div>
<div id="output">
</div>
</div>
<!-- Google Maps API -->
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCZA8vB1HcWG1pqWyUyBcyuRI2VDi_fU9U&callback&libraries=places">
</script>
<!-- Enables drawing of the bicycle layer on the map -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- JavaScript -->
<script src="assets/js/routes.js"></script>
</body>
</html>
There seems to be a race condition on showing the bicycle layer when showing BICYCLE directions. The DirectionsRenderer
tries to display it also.
Related question: How to turn off the Bicycling layer after it gets turned on by the DirectionsRenderer
The simplest solution, as you are displaying the BicyclingLayer
already, is to prevent the DirectionsRenderer
from trying to render it.
var directionsDisplay = new google.maps.DirectionsRenderer({
suppressBicyclingLayer: true
});
updated code snippet:
// Create Bedforshire variable
const bedfordshire = {
lat: 52.02973,
lng: -0.45303
};
// Set the map options
const mapOtions = {
center: bedfordshire,
zoom: 10,
// Disable the default map UI, enable the zoom control
disableDefaultUI: true,
zoomControl: true
};
// Create the map with the bicycle layer enabled
const map = new google.maps.Map(document.getElementById('googleMap'), mapOtions);
const bikeLayer = new google.maps.BicyclingLayer();
bikeLayer.setMap(map);
// Create variables for custom legend icons
const icons = {
bikeTrails: {
name: "Bike lanes & trails",
icon: "assets/img/icons/bike trail.png",
},
bikeFriendlyRoads: {
name: "Bike-friendly roads",
icon: "assets/img/icons/bike friendly road.png",
},
};
// Create the legend and place the icons
const legend = document.getElementById("legend");
for (const key in icons) {
const type = icons[key];
const name = type.name;
const icon = type.icon;
const div = document.createElement("div");
div.innerHTML = '<img src="' + icon + '"> ' + name;
legend.appendChild(div);
}
// Push the legend to the map
map.controls[google.maps.ControlPosition.LEFT_BOTTOM].push(legend);
// Create a DirectionsService object to use the route method and get a result for the request
var directionsService = new google.maps.DirectionsService();
// Create a DirectionsRenderer object to create the route
var directionsDisplay = new google.maps.DirectionsRenderer({
suppressBicyclingLayer: true
});
// Display the directions on the map
directionsDisplay.setMap(map);
// Define the calcRoute function
function calcRoute() {
// Create a route request
var request = {
origin: document.getElementById('from').value,
destination: document.getElementById('to').value,
travelMode: google.maps.TravelMode.BICYCLING,
unitSystem: google.maps.UnitSystem.IMPERIAL
}
// Pass the request to the .route method
directionsService.route(request, function (result, status) {
if (status == google.maps.DirectionsStatus.OK) {
// Get the route distance and time and pass to the #output div
const output = document.querySelector('#output');
output.innerHTML = '<div class="alert-info">From: ' + document.getElementById('from').value + '.<br />To: ' + document.getElementById('to').value + '.<br /> Cycling distance <i class="fas fa-biking"></i> : ' + result.routes[0].legs[0].distance.text + '.<br />Duration <i class="fas fa-stopwatch"></i> : ' + result.routes[0].legs[0].duration.text + '.</div>';
// Display the route
directionsDisplay.setDirections(result);
} else {
// Delete the route
directionsDisplay.setDirections({
routes: []
});
// Recenter the map on Bedfordshire
map.setCenter(bedfordshire);
// Show an error message if the route is not possible
output.innerHTML = '<div class="alert-danger"><i class="fas fa-exclamation-triangle"></i> This route is not possible on a bicycle!</div>';
}
});
}
// Create searchBox1 object for the starting place
var input1 = document.getElementById('from');
var searchBox1 = new google.maps.places.SearchBox(input1);
// Bias the SearchBox1 results towards current map's viewport
map.addListener('bounds_changed', () => {
searchBox1.setBounds(map.getBounds());
});
// Create searchBox2 object for the destination
var input2 = document.getElementById('to');
var searchBox2 = new google.maps.places.SearchBox(input2);
// Bias the SearchBox2 results towards current map's viewport
map.addListener('bounds_changed', () => {
searchBox2.setBounds(map.getBounds());
});
/* Form placeholder text */
#from {
font-size: 0.8em;
}
#to {
font-size: 0.8em;
}
/* Size the map and apply box-shadow */
#googleMap {
width: 85vw;
height: 50vh;
margin: 12px auto;
border: 1px solid black;
border-radius: 2% 2% 2% 2%;
}
/* Style the map legend */
#legend {
font-family: Arial, sans-serif;
background: #fff;
background-color: lightgray;
padding: 4px;
margin: 4px;
border: 1px solid black;
border-radius: 2% 2% 2% 2%;
}
#legend img {
vertical-align: middle;
}
/* Style the alert box containing the route details */
.alert-info {
margin: auto;
max-width: fit-content;
text-align: center;
font-size: 0.8em;
background-color: #efefef;
}
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta name="viewport" content="initial-scale=1.0">
<form>
<label for="from" class="control-label direction-input"></label>
<input type="text" id="from" placeholder="Starting place" class="form-control" value="Milton Keynes">
<label for="to" class="control-label direction-input"></label>
<input type="text" id="to" placeholder="Destination" class="form-control" value="Newport Pagnell">
</form>
<button onclick="calcRoute();">Show Route </button>
<div id="map-container">
<div id="googleMap"></div>
<div id="legend">
</div>
</div>
<div id="output">
</div>
</div>
<!-- Google Maps API -->
<script
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&callback&libraries=places">
</script>
<!-- Enables drawing of the bicycle layer on the map -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<!-- JavaScript -->
<script src="assets/js/routes.js"></script>
</body>
</html>