Search code examples
javascriptgoogle-mapsgoogle-maps-api-3geocomplete

Get details for selected establishment on Google Maps


There's quite a few questions on this, and documentation available, however it seems that I can't find the solution for the use case I'm trying to solve.

What I'd like to do:

  • Have a Google Map with a search box so a user can search for a location (a city of more specific a point of interest). This is something that I can do.
  • When a user searches for a location, no marker is placed, the map only zooms in on the location
  • User can click on an establishment from the Places Library
  • When that place is clicked, I'd like to get details from this place

So it's basically extending the places autocomplete with the last bullet mentioned

-or-

using the geocomplete library, more specifically the form example, because it already gives all the details needed that are listed below. The thing I'm missing is how to not place a marker on search, and how to get details from an establishment only when the users clicks on it.

The details that I would like to get from the establishment when clicked, which are directly available through geocomplete, are:

UPDATE

With the geocomplete library you can bind a click event, like so:

$("#geocomplete_input_field").bind("geocode:click", function(event, latLng){
  $("input[name=lat]").val(latLng.lat());
  $("input[name=lng]").val(latLng.lng());
});

So with that we get the latitude and longitude. But can you also get the formatted_address, name, and url as happens in the form example?


Solution

  • Well, you don't need any external library to accomplish this. Since everything is already done (codes), all you need to do is just add a 'POI Click Event'. It listens for the click event on a POI icon and then uses the placeId for anything you wanted to achieve. In your case you wanted to acquire these: name, formatted_addres, url, lat, lng, and website.

    In the example that I will provide you, I just created a constructor 'ClickEventHandler'. You can check POI Click for sample and documentation.

      var ClickEventHandler = function(map, origin) {
        this.origin = origin;
        this.map = map;
        this.placesService = new google.maps.places.PlacesService(map);
        this.infowindow = new google.maps.InfoWindow();
        // Listen for clicks on the map.
        this.map.addListener('click', this.handleClick.bind(this));
      }; 
    

    Then, create a new instance inside your init() function and supply the arguments: map and origin.

    var clickHandler = new ClickEventHandler(map, {lat: -33.8688, lng: 151.2195});
    

    In the 'ClickEventHandler' object, create a prototype 'getPlaceInformation' that accepts an argument 'placeid'. You need to use Google Maps Javascript API Place Details in this section. Inside Google Maps Javascript API Place Details getDetails method, you will use the placeid you supplied as argument coming from the previous function (getPlaceInformation) you created. getDetails method needs 2 arguments: place and status. This method gets all the details from the placeid you supplied. Once you get the status === 'OK', it will return to you data that you are expecting.

    ClickEventHandler.prototype.getPlaceInformation = function(placeId) {  
       this.placesService.getDetails({placeId: placeId}, function(place, status) {
          if (status === 'OK') {         
          }
       });
    };  
    

    Working example below (with details in infowindow and input elements value supplied from the POI):

          // This example adds a search box to a map, using the Google Place Autocomplete
          // feature. People can enter geographical searches. The search box will return a
          // pick list containing a mix of places and predicted search terms.
    
          // This example requires the Places library. Include the libraries=places
          // parameter when you first load the API. For example:
          // <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
    
          function initAutocomplete() {
            var map = new google.maps.Map(document.getElementById('map'), {
              center: {lat: -33.8688, lng: 151.2195},
              zoom: 13,
              mapTypeId: 'roadmap'
            });
    
            // Create the search box and link it to the UI element.
            var input = document.getElementById('pac-input');
            var searchBox = new google.maps.places.SearchBox(input);
            map.controls[google.maps.ControlPosition.TOP_LEFT].push(input);
    
            // Bias the SearchBox results towards current map's viewport.
            map.addListener('bounds_changed', function() {
              searchBox.setBounds(map.getBounds());
            });
    
            
            // Listen for the event fired when the user selects a prediction and retrieve
            // more details for that place.
            searchBox.addListener('places_changed', function() {
              var places = searchBox.getPlaces();
    
              if (places.length == 0) {
                return;
              }
    
              // For each place, get the icon, name and location.
              var bounds = new google.maps.LatLngBounds();
              places.forEach(function(place) {
                if (!place.geometry) {
                  console.log("Returned place contains no geometry");
                  return;
                }
                if (place.geometry.viewport) {
                  // Only geocodes have viewport.
                  bounds.union(place.geometry.viewport);
                } else {
                  bounds.extend(place.geometry.location);
                }
              });
              map.fitBounds(bounds);
            });
            var clickHandler = new ClickEventHandler(map, {lat: -33.8688, lng: 151.2195});
          }
          var ClickEventHandler = function(map, origin) {
            this.origin = origin;
            this.map = map;
            this.placesService = new google.maps.places.PlacesService(map);
            this.infowindow = new google.maps.InfoWindow();
            // Listen for clicks on the map.
            this.map.addListener('click', this.handleClick.bind(this));
          }; 
    
          ClickEventHandler.prototype.getPlaceInformation = function(placeId) {
            var me = this;     
            this.placesService.getDetails({placeId: placeId}, function(place, status) {
              me.infowindow.close();
              if (status === 'OK') {
                var inputNames = [ 'name', 'formatted_address', 'url',  'website' ];
                for ( var val in inputNames ) {
                   document.getElementById(inputNames[val]).value = place[inputNames[val]];
                }
                document.getElementById('lat').value = place.geometry.location.lat();
                document.getElementById('lng').value = place.geometry.location.lng();
                var template = '<div id="infoContent">';
                template += '<ul>';
                template += '<li><span>Name: </span>'+place.name+'</li>';
                template += '<li><span>Formatted address: </span>'+place.name+'</li>';
                template += '<li><span>Google Maps URL: </span><a href="'+place.url+'" target="_blank">'+place.url+'</a></li>';
                template += '<li><span>Latitude: </span>'+place.geometry.location.lat()+'</li>';
                template += '<li><span>Longitude: </span>'+place.geometry.location.lng()+'</li>';
                template += '<li><span>Website: </span><a href="'+place.website+'" target="_blank">'+place.website+'</a></li>';
                template += '</ul>';
                me.infowindow.setContent(template);
                me.infowindow.setPosition(place.geometry.location);           
                me.infowindow.open(me.map);            
              }
            });
          };  
    
          ClickEventHandler.prototype.handleClick = function(event) {
            //console.log('You clicked on: ' + event.latLng);
            // If the event has a placeId, use it.
            if (event.placeId) {
                //console.log('You clicked on place:' + event.placeId);
              // Calling e.stop() on the event prevents the default info window from
              // showing.
              // If you call stop here when there is no placeId you will prevent some
              // other map click event handlers from receiving the event.
              event.stop();
              this.getPlaceInformation(event.placeId);
            }
          };      
          #map {
            height: 100%;
          }
          /* Optional: Makes the sample page fill the window. */
          html, body {
            height: 100%;
            margin: 0;
            padding: 0;
          }
          #description {
            font-family: Roboto;
            font-size: 15px;
            font-weight: 300;
          }
    
          .pac-card {
            margin: 10px 10px 0 0;
            border-radius: 2px 0 0 2px;
            box-sizing: border-box;
            -moz-box-sizing: border-box;
            outline: none;
            box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
            background-color: #fff;
            font-family: Roboto;
          }
    
          #pac-container {
            padding-bottom: 12px;
            margin-right: 12px;
          }
    
          .pac-controls {
            display: inline-block;
            padding: 5px 11px;
          }
    
          .pac-controls label {
            font-family: Roboto;
            font-size: 13px;
            font-weight: 300;
          }
    
          #pac-input {
            background-color: #fff;
            font-family: Roboto;
            font-size: 15px;
            font-weight: 300;
            margin-left: 12px;
            padding: 0 11px 0 13px;
            text-overflow: ellipsis;
            width: 400px;
          }
    
          #pac-input:focus {
            border-color: #4d90fe;
          }
    
          #title {
            color: #fff;
            background-color: #4d90fe;
            font-size: 25px;
            font-weight: 500;
            padding: 6px 12px;
          }
          #target {
            width: 345px;
          }
          #infoContent {
            width:100%;
            float:left;
            overflow:hidden;
            display:block;
          }
          #infoContent > ul {
            width:100%;
            float:left;
            overflow:hidden;
            display:block;
          }
          #infoContent > ul > li {
            width:100%;
            float:left;
            overflow:hidden;
            clear:both;
            font-size:12px;
          }
          #infoContent > ul > li span {
            font-weight:bold;
          }
          #infoContent > ul > li a {
            text-decoration:none;
          }
          #myForm {
            width:100%;
            float:left;
            overflow:hidden;
            display:block;
            box-sizing:border-box;
            padding: 10xp;
          }
          #myForm fieldset {
            display:block;
            clear:both;
            float:left;
            border:none;
          }
          #myForm fieldset label {
            width:100%;
            float:left;
            overflow:hidden;
            display:block;
            clear:both;
            line-height:24px;
          }
          #myForm fieldset input {
            width: 100%;
            float:left;
            overflow:hidden
            display:block;
            clear:both;
            line-height: 24px;
            padding: 0 5px;
          }
        <input id="pac-input" class="controls" type="text" placeholder="Search Box">
        <div id="map"></div>
        <form id="myForm">
          <fieldset>
            <label>Name:</label>
            <input type="text" name="name" id="name" />
          </fieldset>
          <fieldset>
            <label>Formatted address:</label>
            <input type="text" name="formatted_address" id="formatted_address" />
          </fieldset>
          <fieldset>
            <label>URL to Google Maps:</label>
            <input type="text" name="url" id="url" />
          </fieldset>
          <fieldset>
            <label>Latitude:</label>
            <input type="text" name="lat" id="lat" />
          </fieldset>
          <fieldset>
            <label>Longitude:</label>
            <input type="text" name="lng" id="lng" />
          </fieldset>
           <fieldset>
            <label>Website:</label>
            <input type="text" name="website" id="website" />
          </fieldset>     
        </form>
            <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyCzjs-bUR6iIl8yGLr60p6-zbdFtRpuXTQ&libraries=places&callback=initAutocomplete"
             async defer></script>

    Hope it helps and happy coding!