Search code examples
javascripthtmlgoogle-mapsgeolocation

Google Map API Failed on Marker Click - Default Polyline Disappears - JS


I'm combining Google Map API Directions and Places together and my target is :

1.Geolocate my position;

2.Search nearby places, "sushi" in my codes below;

3.Create markers on the map;

4(1).Everytime when I click one marker, the critical information of the marker displays on the left sidebar;

4(2).And there's a default blue polyline showing the route between my position and the clicked marker;

5.The route refreshes itself upon the next click.

However, my problem is that the default blue polyline couldn't show in the map that I made. I checked the console and there are no errors and Direction API can get a response from my code. The response even includes the code of the default blue polyline but somehow it disappears from the map. Response from Google Map Direction API

I've also posted my codes on GitHub and thank you to all the people who show interest in my question!!

https://github.com/Sam5208/Self-study/blob/4bc218d4a3625b3e9dfc5eea1726455353a1f648/test-nearby.html

References:

1.https://github.com/googlecodelabs/google-maps-nearby-search-js/blob/master/step4/index.html 2.Google map driving direction source code for their example? 3.JS - Google Maps API - Directions on Marker Click

let pos;
let map;
let bounds;
let infoWindow;
let currentInfoWindow;
let service;
let infoPane;
function initMap() {
  // Initialize variables
  directionsService = new google.maps.DirectionsService;
  directionsDisplay = new google.maps.DirectionsRenderer({
    map: map
  });
  bounds = new google.maps.LatLngBounds();
  infoWindow = new google.maps.InfoWindow;
  currentInfoWindow = infoWindow;
  /* TODO: Step 4A3: Add a generic sidebar */
  infoPane = document.getElementById('panel');

  // Try HTML5 geolocation
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(position => {
      pos = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
      };
      map = new google.maps.Map(document.getElementById('map'), {
        center: pos,
        zoom: 15
      });
      bounds.extend(pos);

      infoWindow.setPosition(pos);
      infoWindow.setContent('Location found.');
      infoWindow.open(map);
      map.setCenter(pos);

      // Call Places Nearby Search on user's location
      getNearbyPlaces(pos);
    }, () => {
      // Browser supports geolocation, but user has denied permission
      handleLocationError(true, infoWindow);
    });
  } else {
    // Browser doesn't support geolocation
    handleLocationError(false, infoWindow);
  }
}

// Handle a geolocation error
function handleLocationError(browserHasGeolocation, infoWindow) {
  // Set default location to Sydney, Australia
  pos = { lat: -33.856, lng: 151.215 };
  map = new google.maps.Map(document.getElementById('map'), {
    center: pos,
    zoom: 15
  });

  // Display an InfoWindow at the map center
  infoWindow.setPosition(pos);
  infoWindow.setContent(browserHasGeolocation ?
    'Geolocation permissions denied. Using default location.' :
    'Error: Your browser doesn\'t support geolocation.');
  infoWindow.open(map);
  currentInfoWindow = infoWindow;

  // Call Places Nearby Search on the default location
  getNearbyPlaces(pos);
}

// Perform a Places Nearby Search Request
function getNearbyPlaces(position) {
  let request = {
    location: position,
    rankBy: google.maps.places.RankBy.DISTANCE,
    keyword: 'sushi'
  };

  service = new google.maps.places.PlacesService(map);
  service.nearbySearch(request, nearbyCallback);
}

// Handle the results (up to 20) of the Nearby Search
function nearbyCallback(results, status) {
  if (status == google.maps.places.PlacesServiceStatus.OK) {
    createMarkers(results);
  }
}

// Set markers at the location of each place result
function createMarkers(places) {
  places.forEach(place => {
    let marker = new google.maps.Marker({
      position: place.geometry.location,
      map: map,
      title: place.name
    });

    /* TODO: Step 4B: Add click listeners to the markers */
    // Add click listener to each marker
    google.maps.event.addListener(marker, 'click', () => {
        clicked = marker.getPosition();
      let request = {
        placeId: place.place_id,
        fields: ['name', 'formatted_address', 'geometry', 'rating',
          'website', 'photos']
      };
      
      
      calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked);
      console.log(clicked);

      /* Only fetch the details of a place when the user clicks on a marker.
       * If we fetch the details for all place results as soon as we get
       * the search response, we will hit API rate limits. */
      service.getDetails(request, (placeResult, status) => {
        showDetails(placeResult, marker, status)
      });
    });

    // Adjust the map bounds to include the location of this marker
    bounds.extend(place.geometry.location);
  });
  /* Once all the markers have been placed, adjust the bounds of the map to
   * show all the markers within the visible area. */
  map.fitBounds(bounds);
}


function calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked) {
  directionsService.route({
    origin: pos,
    destination: clicked,
    avoidTolls: true,
    avoidHighways: false,
    travelMode: google.maps.TravelMode.WALKING
  }, function(response, status) {
      console.log(response, status);
    if (status == google.maps.DirectionsStatus.OK) {
      directionsDisplay.setDirections(response);
    } else {
      window.alert('Directions request failed due to ' + status);
    }
  });
}

/* TODO: Step 4C: Show place details in an info window */
// Builds an InfoWindow to display details above the marker
function showDetails(placeResult, marker, status) {
  if (status == google.maps.places.PlacesServiceStatus.OK) {
    let placeInfowindow = new google.maps.InfoWindow();
    let rating = "None";
    if (placeResult.rating) rating = placeResult.rating;
    placeInfowindow.setContent('<div><strong>' + placeResult.name +
      '</strong><br>' + 'Rating: ' + rating + '</div>');
    placeInfowindow.open(marker.map, marker);
    currentInfoWindow.close();
    currentInfoWindow = placeInfowindow;
    showPanel(placeResult);
  } else {
    console.log('showDetails failed: ' + status);
  }
}

/* TODO: Step 4D: Load place details in a sidebar */
// Displays place details in a sidebar
function showPanel(placeResult) {
  // If infoPane is already open, close it
  if (infoPane.classList.contains("open")) {
    infoPane.classList.remove("open");
  }

  // Clear the previous details
  while (infoPane.lastChild) {
    infoPane.removeChild(infoPane.lastChild);
  }

  /* TODO: Step 4E: Display a Place Photo with the Place Details */
  // Add the primary photo, if there is one
  if (placeResult.photos) {
    let firstPhoto = placeResult.photos[0];
    let photo = document.createElement('img');
    photo.classList.add('hero');
    photo.src = firstPhoto.getUrl();
    infoPane.appendChild(photo);
  }

  // Add place details with text formatting
  let name = document.createElement('h1');
  name.classList.add('place');
  name.textContent = placeResult.name;
  infoPane.appendChild(name);
  if (placeResult.rating) {
    let rating = document.createElement('p');
    rating.classList.add('details');
    rating.textContent = `Rating: ${placeResult.rating} \u272e`;
    infoPane.appendChild(rating);
  }
  let address = document.createElement('p');
  address.classList.add('details');
  address.textContent = placeResult.formatted_address;
  infoPane.appendChild(address);
  if (placeResult.website) {
    let websitePara = document.createElement('p');
    let websiteLink = document.createElement('a');
    let websiteUrl = document.createTextNode(placeResult.website);
    websiteLink.appendChild(websiteUrl);
    websiteLink.title = placeResult.website;
    websiteLink.href = placeResult.website;
    websitePara.appendChild(websiteLink);
    infoPane.appendChild(websitePara);
  }

  // Open the infoPane
  infoPane.classList.add("open");
}

Solution

  • You are not setting the map property of the directionsService.

    directionsDisplay.setMap(map);
    

    Needs to be done in two places (after you create the map):

    1. the nominal case (where the geolocation works)
    2. the off-nominal case (where the geolocation fails)

    proof of concept fiddle

    screenshot of map with a route displayed

    code snippet:

    <!DOCTYPE html>
    <html>
    
    <head>
      <title>Sushi Finder</title>
      <meta name="viewport" content="initial-scale=1.0, user-scalable=no">
      <meta charset="utf-8">
      <style>
        /* Always set the map height explicitly to define the size of the div
         * element that contains the map. */
        #map {
          height: 100%;
          background-color: grey;
        }
    
        /* Optional: Makes the sample page fill the window. */
        html,
        body {
          height: 100%;
          margin: 0;
          padding: 0;
        }
    
        /* TODO: Step 4A1: Make a generic sidebar */
        /* Styling for an info pane that slides out from the left. 
         * Hidden by default. */
        #panel {
          height: 100%;
          width: null;
          background-color: white;
          position: fixed;
          z-index: 1;
          overflow-x: hidden;
          transition: all .2s ease-out;
        }
    
        .open {
          width: 250px;
        }
    
        /* Styling for place details */
        .hero {
          width: 100%;
          height: auto;
          max-height: 166px;
          display: block;
        }
    
        .place,
        p {
          font-family: 'open sans', arial, sans-serif;
          padding-left: 18px;
          padding-right: 18px;
        }
    
        .details {
          color: darkslategrey;
        }
    
        a {
          text-decoration: none;
          color: cadetblue;
        }
      </style>
    </head>
    
    <body>
      <!-- TODO: Step 4A2: Add a generic sidebar -->
      <!-- The slide-out panel for showing place details -->
      <div id="panel"></div>
    
      <!-- Map appears here -->
      <div id="map"></div>
    
      <script>
        /* Note: This example requires that you consent to location sharing when
         * prompted by your browser. If you see the error "Geolocation permission
         * denied.", it means you probably did not give permission for the browser * to locate you. */
        let pos;
        let map;
        let bounds;
        let infoWindow;
        let currentInfoWindow;
        let service;
        let infoPane;
        function initMap() {
          // Initialize variables
          directionsService = new google.maps.DirectionsService;
          directionsDisplay = new google.maps.DirectionsRenderer({
            map: map
          });
          bounds = new google.maps.LatLngBounds();
          infoWindow = new google.maps.InfoWindow;
          currentInfoWindow = infoWindow;
          /* TODO: Step 4A3: Add a generic sidebar */
          infoPane = document.getElementById('panel');
    
          // Try HTML5 geolocation
          if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(position => {
              pos = {
                lat: position.coords.latitude,
                lng: position.coords.longitude
              };
              map = new google.maps.Map(document.getElementById('map'), {
                center: pos,
                zoom: 15
              });
              directionsDisplay.setMap(map);
              bounds.extend(pos);
    
              infoWindow.setPosition(pos);
              infoWindow.setContent('Location found.');
              infoWindow.open(map);
              map.setCenter(pos);
    
              // Call Places Nearby Search on user's location
              getNearbyPlaces(pos);
            }, () => {
              // Browser supports geolocation, but user has denied permission
              handleLocationError(true, infoWindow);
            });
          } else {
            // Browser doesn't support geolocation
            handleLocationError(false, infoWindow);
          }
        }
    
        // Handle a geolocation error
        function handleLocationError(browserHasGeolocation, infoWindow) {
          // Set default location to Sydney, Australia
          pos = { lat: -33.856, lng: 151.215 };
          map = new google.maps.Map(document.getElementById('map'), {
            center: pos,
            zoom: 15
          });
          directionsDisplay.setMap(map);
          // Display an InfoWindow at the map center
          infoWindow.setPosition(pos);
          infoWindow.setContent(browserHasGeolocation ?
            'Geolocation permissions denied. Using default location.' :
            'Error: Your browser doesn\'t support geolocation.');
          infoWindow.open(map);
          currentInfoWindow = infoWindow;
    
          // Call Places Nearby Search on the default location
          getNearbyPlaces(pos);
        }
    
        // Perform a Places Nearby Search Request
        function getNearbyPlaces(position) {
          let request = {
            location: position,
            rankBy: google.maps.places.RankBy.DISTANCE,
            keyword: 'sushi'
          };
    
          service = new google.maps.places.PlacesService(map);
          service.nearbySearch(request, nearbyCallback);
        }
    
        // Handle the results (up to 20) of the Nearby Search
        function nearbyCallback(results, status) {
          if (status == google.maps.places.PlacesServiceStatus.OK) {
            createMarkers(results);
          }
        }
    
        // Set markers at the location of each place result
        function createMarkers(places) {
          places.forEach(place => {
            let marker = new google.maps.Marker({
              position: place.geometry.location,
              map: map,
              title: place.name
            });
    
            /* TODO: Step 4B: Add click listeners to the markers */
            // Add click listener to each marker
            google.maps.event.addListener(marker, 'click', () => {
                clicked = marker.getPosition();
              let request = {
                placeId: place.place_id,
                fields: ['name', 'formatted_address', 'geometry', 'rating',
                  'website', 'photos']
              };
              
                  console.log("marker "+clicked.toUrlValue(6)+" clicked")
              calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked);
              console.log(clicked);
    
              /* Only fetch the details of a place when the user clicks on a marker.
               * If we fetch the details for all place results as soon as we get
               * the search response, we will hit API rate limits. */
              service.getDetails(request, (placeResult, status) => {
                showDetails(placeResult, marker, status)
              });
            });
    
            // Adjust the map bounds to include the location of this marker
            bounds.extend(place.geometry.location);
          });
          /* Once all the markers have been placed, adjust the bounds of the map to
           * show all the markers within the visible area. */
          map.fitBounds(bounds);
        }
        
        
        function calculateAndDisplayRoute(directionsService, directionsDisplay, pos, clicked) {
          directionsService.route({
            origin: pos,
            destination: clicked,
            avoidTolls: true,
            avoidHighways: false,
            travelMode: google.maps.TravelMode.WALKING
          }, function(response, status) {
              console.log(response, status);
            if (status == google.maps.DirectionsStatus.OK) {
            console.log("directionsDisplay.getMap="+directionsDisplay.getMap());
              directionsDisplay.setDirections(response);
            } else {
              window.alert('Directions request failed due to ' + status);
            }
          });
        }
    
        /* TODO: Step 4C: Show place details in an info window */
        // Builds an InfoWindow to display details above the marker
        function showDetails(placeResult, marker, status) {
          if (status == google.maps.places.PlacesServiceStatus.OK) {
            let placeInfowindow = new google.maps.InfoWindow();
            let rating = "None";
            if (placeResult.rating) rating = placeResult.rating;
            placeInfowindow.setContent('<div><strong>' + placeResult.name +
              '</strong><br>' + 'Rating: ' + rating + '</div>');
            placeInfowindow.open(marker.map, marker);
            currentInfoWindow.close();
            currentInfoWindow = placeInfowindow;
            showPanel(placeResult);
          } else {
            console.log('showDetails failed: ' + status);
          }
        }
    
        /* TODO: Step 4D: Load place details in a sidebar */
        // Displays place details in a sidebar
        function showPanel(placeResult) {
          // If infoPane is already open, close it
          if (infoPane.classList.contains("open")) {
            infoPane.classList.remove("open");
          }
    
          // Clear the previous details
          while (infoPane.lastChild) {
            infoPane.removeChild(infoPane.lastChild);
          }
    
          /* TODO: Step 4E: Display a Place Photo with the Place Details */
          // Add the primary photo, if there is one
          if (placeResult.photos) {
            let firstPhoto = placeResult.photos[0];
            let photo = document.createElement('img');
            photo.classList.add('hero');
            photo.src = firstPhoto.getUrl();
            infoPane.appendChild(photo);
          }
    
          // Add place details with text formatting
          let name = document.createElement('h1');
          name.classList.add('place');
          name.textContent = placeResult.name;
          infoPane.appendChild(name);
          if (placeResult.rating) {
            let rating = document.createElement('p');
            rating.classList.add('details');
            rating.textContent = `Rating: ${placeResult.rating} \u272e`;
            infoPane.appendChild(rating);
          }
          let address = document.createElement('p');
          address.classList.add('details');
          address.textContent = placeResult.formatted_address;
          infoPane.appendChild(address);
          if (placeResult.website) {
            let websitePara = document.createElement('p');
            let websiteLink = document.createElement('a');
            let websiteUrl = document.createTextNode(placeResult.website);
            websiteLink.appendChild(websiteUrl);
            websiteLink.title = placeResult.website;
            websiteLink.href = placeResult.website;
            websitePara.appendChild(websiteLink);
            infoPane.appendChild(websitePara);
          }
    
          // Open the infoPane
          infoPane.classList.add("open");
        }
      </script>
    
      <script async defer src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk&libraries=places&callback=initMap">
      </script>
    </body>
    
    </html>