javascriptgoogle-maps-api-3google-directions-api

The Bicycle Layer disappears the 2nd time a route request is made and displayed. Google Maps API directions


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>


Solution

  • 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>