arrow_back

Build a Nearby Business Search Service with Google Maps Platform

Join Sign in

Build a Nearby Business Search Service with Google Maps Platform

1 hour 7 Credits

GSP600

Google Cloud Self-Paced Labs

Overview

Learn to use Google Maps Platform's Maps and Places APIs to build a local business search, which geolocates the user and shows interesting places around them. The app integrates location, place details, place photos, and more.

What is Google Maps Platform?

Google Maps Platform brings the wealth of Google's location-based information to your app. Millions of developers use Google Maps Platform to help their users navigate the world around them, on the web and on mobile devices. The Google Maps Platform offers three core products:

  • Maps enables you to build customized, agile experiences that bring the real world to your users with static and dynamic maps, and 360° Street View imagery.

  • Places helps your users discover the world with rich location data for over 150 million places by using phone numbers, addresses, and real-time signals.

  • Routes gives your users the best way to get from A to Z with high-quality directions that factor in real-time traffic updates. Determine the route a vehicle travels to create more precise itineraries.

What you'll build

In this lab you're going to build a webpage that displays a Google map centered on the user's location, finds nearby places, and displays the places as clickable markers to show more details about each place.

ae1caf211daa484d.png

What you'll learn

  • How to create a customizable map

  • How to geolocate the user

  • How to search for nearby places and display the results

  • How to fetch and display details about a place

  • How to deploy a web app using Google App Engine

What you'll need

  • Your favorite text or code editor
  • Basic knowledge of HTML, CSS, and JavaScript

The stepN folders contain the desired end state of each step of this codelab. They are there for reference. Do all your coding work in the directory called work.

This lab is focused on using the Google Maps JavaScript API and its Places Library. Non-relevant concepts and code blocks are glossed over and are provided for you to simply copy and paste.

Set up

Before you click the Start Lab button

Read these instructions. Labs are timed and you cannot pause them. The timer, which starts when you click Start Lab, shows how long Google Cloud resources will be made available to you.

This hands-on lab lets you do the lab activities yourself in a real cloud environment, not in a simulation or demo environment. It does so by giving you new, temporary credentials that you use to sign in and access Google Cloud for the duration of the lab.

What you need

To complete this lab, you need:

  • Access to a standard internet browser (Chrome browser recommended).
  • Time to complete the lab.

Note: If you already have your own personal Google Cloud account or project, do not use it for this lab.

Note: If you are using a Chrome OS device, open an Incognito window to run this lab.

How to start your lab and sign in to the Google Cloud Console

  1. Click the Start Lab button. If you need to pay for the lab, a pop-up opens for you to select your payment method. On the left is a panel populated with the temporary credentials that you must use for this lab.

    Open Google Console

  2. Copy the username, and then click Open Google Console. The lab spins up resources, and then opens another tab that shows the Sign in page.

    Sign in

    Tip: Open the tabs in separate windows, side-by-side.

  3. In the Sign in page, paste the username that you copied from the left panel. Then copy and paste the password.

    Important: You must use the credentials from the left panel. Do not use your Google Cloud Training credentials. If you have your own Google Cloud account, do not use it for this lab (avoids incurring charges).

  4. Click through the subsequent pages:

    • Accept the terms and conditions.
    • Do not add recovery options or two-factor authentication (because this is a temporary account).
    • Do not sign up for free trials.

After a few moments, the Cloud Console opens in this tab.

Start App Engine

In the Cloud Console, from the Navigation menu, select App Engine. This brings up the "Welcome to App Engine" screen. Click Create Application.

ddb57cee088543ca.png

Accept the default region (or select a new one) and click Create app.

e8329cc3051c3315.png

Accept the standard settings (us-central, Python, Standard) then Next to continue.

Once you get a notification that "Your App Engine app has successfully been created", open the Cloud Shell by clicking the Activate Cloud Shell button in the upper right corner of the console.

dd53bbfdfebf62a8.png

Then click Continue.

In the Cloud Shell, download all the sample code needed for this lab by running this command:

git clone https://github.com/googlecodelabs/google-maps-nearby-search-js/

Move into the repo you just cloned.

cd google-maps-nearby-search-js

Copy the index.html file from the repo into the work/ folder, then move into your work/ folder. Throughout the rest of the lab, make your edits in the version in the work/ folder.

cp index.html work/ cd work

The work folder contains an app.yaml file. This is the file that configures your App Engine app's settings, such as specifying how URL paths correspond to request handlers and static files.

For this lab you do not need to modify this file. This is what the app.yaml looks like:

runtime: python37 handlers: - url: / static_files: index.html upload: index.html - url: /index\.html static_files: index.html upload: index.html - url: /.* secure: always script: auto

Before deploying your app, grant Cloud Storage > Storage Object Viewer permission to the Cloud Build Service in IAM Roles. Your service will look similar like: 896890020855@cloudservices.gserviceaccount.com

Now you are ready to deploy your app to App Engine and host it on Google Cloud.

Use this command, which looks for the app.yaml file in the current directory and deploys the app according to the settings specified within:

gcloud app deploy

Type "y" when asked if you want to continue.

To view your new app, get the URL for your web app with this command:

gcloud app browse

This returns a URL that you can paste into your web browser.

In the case of this lab, it follows the format https://your-project-id.appspot.com

Right now there's nothing to look at.

Each time you want to view changes to your app based on edits in your code, run gcloud app deploy to deploy the changes, and reload the page in your browser.

Click Check my progress to verify the objective. Start App Engine

Enable Google Maps Platform APIs and get an API key

This section explains how to authenticate your app to the Maps JavaScript API and Places API using your own API key.

Follow these steps to enable the APIs used in this lab and get an API key:

  1. In the Cloud Console, from the Navigation menu, select APIs & Services > Library.
  2. Click the Maps JavaScript API tile and click the Enable button.
  3. Repeat steps 1 and 2 to enable the Places API.
  4. From the Navigation menu, select APIs & Services > Credentials.
  5. Click Create Credentials and choose API key.
  6. You will see an "API key created" confirmation screen and your new key is listed on the Credentials page. You will need this key in the next step to add a map to your webpage.

Click Check my progress to verify the objective. Enable Google Maps Platform APIs and get an API key

Step 1: Create a Map with a Default Center

There are three steps to creating a Google map on your web page:

  • Create an HTML page

  • Get an API key

  • Add a map

Create an HTML page

Below is the map you'll create for this step. The map is centered on the Sydney Opera House in Sydney, Australia. If the user denies permission to get their location, the map defaults to this location and still be able to provide interesting search results.

569b9781658fec74.png

The code for a basic HTML web page is provided in the index.html file, which is in the repo you downloaded. You'll be making modifications to this file, and can use the Cloud Shell Editor as a code editor to modify the code in the work/index.html file, or your favorite text editor. At the end of each step in this lab, there is a link to a sample index.html that you can compare your own code against.

There are two ways to open the Cloud Shell Editor:

  • From the Cloud Shell command line, type edit index.html and confirm that you want to open this file in the Cloud Shell Editor.
  • In the upper right of the Cloud Shell, click Open Editor and then click Open in new window.

cloudshelleditor.png

For your reference, the current contents of index.html are provided below:

<!DOCTYPE html> <html> <head> <title>Google Maps Place Picker</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; } </style> </head> <body> <!-- Map appears here --> <div id="map"></div> </body> </html>

Note that this is a very basic page with a single div element and a style element. You can add any content you like to the web page. You should see a large div with a grey background spanning the full visible window.

Add a map

This section shows you how to load the Maps JavaScript API into your web page, and how to write your own JavaScript that uses the API to add a map to the webpage.

Add this script code after the map <div> and before the close </body> tag.

<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; function initMap() { // Set the default location and initialize all variables pos = { lat: -33.857, lng: 151.213 }; map = new google.maps.Map(document.getElementById('map'), { center: pos, zoom: 15 }); } </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"> </script>

Retrieve your API key

  1. In the Console you should still see the APIs & Services > Credentials.

  2. Copy the value of the key parameter and replace "YOUR_API_KEY" in the script source URL with the API key you just created:

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&callback=initMap"> </script>

Save the file.

Test It Out

In your Cloud Shell, ensure you are in the work/ directory. Run the following to deploy your updated code:

gcloud app deploy

Reload the browser tab that contains your web app. You should see a map appear now where the grey rectangle was before. If you see an error message instead, make sure you have replaced "YOUR_API_KEY" in the final <script> tag with your own API key. See above for how to get an API key if you don't already have one.

Full Sample Code

The full code for this project through Step 1 is available on Github.

Step 2: Geolocate Your User

Next, you'll want to display the geographic location of the user or device on a Google map, using your browser's HTML5 Geolocation feature along with the Maps JavaScript API.

If you were standing in San Jose, CA, you'd see a map that displays your geographic location:

1dbb3fec117cd895.png

What is Geolocation?

Geolocation refers to the identification of the geographic location of a user or computing device via a variety of data collection mechanisms. Typically, most geolocation services use network routing addresses or internal GPS devices to determine this location. This application uses the web browser's W3C Geolocation standard navigator.geolocation property to determine the user's location.

Try it yourself

Here's the entire code you need to request permission to get the user's location and re-center the map on that location. Modify the code inside the main <script> tag:

<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 bounds = new google.maps.LatLngBounds(); infoWindow = new google.maps.InfoWindow; currentInfoWindow = infoWindow; // Try HTML5 geolocation if (navigator.geolocation) { navigator.geolocation.getCurrentPosition(function (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); }, function () { 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; } </script>

Test it out

Deploy your changes:

gcloud app deploy

Reload your page, your browser should now ask you for permission to share your location with the app.

  • Try clicking "Block" one time to see if it handles the error gracefully and remains centered on Sydney.
  • Reload again and try clicking "Allow" to see if the geolocation works and moves the map to your current location.

Note: Your browser might save the last setting of permissions you granted to this page. In order to choose a different permission (allow when you previously blocked, or block when you previously allowed), you may need to clear your browser settings. In Chrome, there is a location icon at the right side of the address bar. Clicking the icon brings up a window that describes your current setting and allows you to clear that setting for future visits. Click Clear these settings for future visits any time you want to test handling of different block/allow permissions of geolocation.

99b0008cb56dcfc6.png

Full Sample Code

The full code for this project through Step 2 is available on Github.

Step 3: Search for Nearby Places

A Nearby Search lets you search for places within a specified area by keyword or type. A Nearby Search must always include a location, which can be specified in one of two ways:

  • a LatLngBounds object defining a rectangular search area.
  • a circular area defined as the combination of the location property — specifying the center of the circle as a LatLng object — and a radius, measured in meters.

Initiate a Nearby Search with a call to the PlacesService's nearbySearch() method, which returns an array of PlaceResult objects.

Load the Places Library

First, to access the Places Library services, update the <script> tag where you included the maps javascript api to introduce the libraries parameter and add places as a value:

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initMap">

If you are loading multiple libraries, you would separate them with a comma (no spaces). For example:

<script async defer src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places,geometry&callback=initMap">

Call the Places Nearby search request and handle the response

Next, form a PlaceSearch Request. The minimum required fields are:

  • Either of:
  • bounds, which must be a google.maps.LatLngBounds object defining the rectangular search area; or
  • a location and a radius; the former takes a google.maps.LatLng object, and the latter takes a simple integer, representing the circle's radius in meters. The maximum allowed radius is 50 000 meters. Note that when rankBy is set to DISTANCE, you must specify a location but you cannot specify a radius or bounds.
  • Either of:
  • A keyword - A term to be matched against all available fields, including but not limited to name, type, and address, as well as customer reviews and other third-party content.
  • A type - Restricts the results to places matching the specified type. Only one type may be specified (if more than one type is provided, all types following the first entry are ignored). See the list of supported types.

For this lab you will use the user's current position as the location for the search and rank results by distance.

Add the following to the main script to write two functions to call the search and handle the response. The keyword sushi is used as the search term, but you can change it to any keyword you like.

// 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); } }

The code to define the createMarkers function is provided in the next section, titled "Generate markers for search results".

Now you can call getNearbyPlaces after attempting geolocation.

Add this line in two places:

  1. To the end of the if (navigator.geolocation) block, above //Browser doesn't support geolocation block, beneath the call map.setCenter(pos);:

// Call Places Nearby Search on user's location getNearbyPlaces(pos);
  1. At the end of the handleLocationError function:

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

Generate markers for search results

A marker identifies a location on a map. By default, a marker uses a standard image. See the Markers guide for information about customizing marker images.

The google.maps.Marker constructor takes a single Marker options object literal, specifying the initial properties of the marker.

The following fields are particularly important and commonly set when constructing a marker:

  • position (required) specifies a LatLng identifying the initial location of the marker.
  • map (optional) specifies the Map on which to place the marker. If you do not specify the map on construction of the marker, the marker is created but is not attached to (or displayed on) the map. You may add the marker later by calling the marker's setMap() method.

Add the code below to set the position, map, and title for one marker per place returned in the response. You also use the extend method of our bounds variable to ensure that our center and all the markers are visible on the map.

// 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 }); // 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); }

Test it out

  • Run gcloud app deploy in your Cloud Shell.

  • Reload the page and click "Allow" to give geolocation permissions.

  • You should see red marker around the center location of the map.

  • Reload again and block geolocation permissions this time. Do you still get results at the default center of your map (in our sample, the default is in Sydney, Australia)?

Full sample code

The full code for this project through Step 3 is available on Github.

Step 4: Show Place Details on Demand

Once you have a place's Place ID (delivered as one of the fields in the results of your nearby search), you can request additional details about the place, such as its complete address, phone number, user rating and reviews, etc. In this lab you'll make a sidebar to display place detail information and make the markers interactive so the user can select places to view details.

Make a generic sidebar

Here's some simple code for a sidebar that you can use to slide out and display the place details when the user clicks on a marker.

Add these to the end of the style tag:

/* 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; }

In the body section just before the map div, add a div for the details panel:

<!-- The slide-out panel for showing place details --> <div id="panel"></div>

In the script section, in the initMap() function, initialize the infoPane variable by adding:

infoPane = document.getElementById('panel');

Add click listeners to the markers

In the createMarkers function, add a click listener to each marker as you create them. The click listener fetches details about the place associated with that marker and calls the function to display the details.

Add the following at the end of the forEach loop inside createMarkers. The showDetails method will be implemented in the next section.

// Add click listener to each marker google.maps.event.addListener(marker, 'click', () => { let request = { placeId: place.place_id, fields: ['name', 'formatted_address', 'geometry', 'rating', 'website', 'photos'] }; /* 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) }); });

In the getDetails request above, the placeId property specifies a single place for the details request, and the fields property is an array of field names for information you want returned about the place. A full list of fields you can request is in the PlaceResult interface documentation.

Show place details in an info window

An info window displays content (usually text or images) in a pop-up window above the map, at a given location. The info window has a content area and a tapered stem. The tip of the stem is attached to a specified location on the map.

Add the following below the "Adjust the map bounds..." section to attach an info window to a marker (you could also attach an info window to a specific latitude/longitude).

// 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(); placeInfowindow.setContent('<div><strong>' + placeResult.name + '</strong><br>' + 'Rating: ' + placeResult.rating + '</div>'); placeInfowindow.open(marker.map, marker); currentInfoWindow.close(); currentInfoWindow = placeInfowindow; showPanel(placeResult); } else { console.log('showDetails failed: ' + status); } }

Here, create an InfoWindow that displays the name and rating of the business, and attaches that window to the marker. You will define showPanel in the next section for displaying details in a sidebar.

Load place details in a sidebar

Use the same details returned in the PlaceResult object to populate another div. In this sample, use infoPane which is an arbitrary variable name for the div with the ID "panel". Each time the user clicks on a new marker, this code closes the sidebar if it was already open, erases the old details, adds the new details, and opens the sidebar.

Add the following below the InfoWindow that you just added:

// 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); } // 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 != null) { 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"); }

Display a Place Photo with the Place Details

The getDetails result returns an array of up to 10 photos associated with the placeId.

Here, we display the first photo above the place name in the sidebar.

Place this code above creation of the "name" element if you want the photo to appear at the top of the sidebar.

// Add the primary photo, if there is one if (placeResult.photos != null) { let firstPhoto = placeResult.photos[0]; let photo = document.createElement('img'); photo.classList.add('hero'); photo.src = firstPhoto.getUrl(); infoPane.appendChild(photo); }

Test it out

  • Run gcloud app deploy in your Cloud Shell.
  • Reload the page in your browser and allow geolocation permissions.
  • Click on a marker to see the InfoWindow pop up from the marker displaying a few details, and the sidebar slide out from the left to display more details.
  • Test whether the search also works if you reload and deny geolocation permissions.
  • Edit your search keyword for a different query and explore the result returned for that search.

Click Check my progress to verify the objective. Deploy the updated app engine application

Full sample code

The full code for this project through Step 4 is available on Github.

Congratulations

Congratulations! You've used many features of the Google Maps JavaScript API, including the Places Library. Here are some of the API features you learned about:

MapMaking_125.png

Finish Your Quest

This self-paced lab is part of the Creating with Google Maps Quest. A Quest is a series of related labs that form a learning path. Completing this Quest earns you the badge above, to recognize your achievement. You can make your badge public and link to it in your online resume or social media account. Enroll in this Quest and get immediate completion credit if you've taken this lab. See other available Qwiklabs Quests.

Take your next lab

Continue your Quest with Google Maps Web Services Proxy for Mobile Applications, or check out these suggestions:

Next steps

To do even more with maps, explore the Maps JavaScript API documentation and Places Library documentation which contain guides, tutorials, the API reference, more code samples, and support channels. Some popular features are Importing Data into Maps, Styling Your Map, and adding the Street View Service.

Resources

Google Cloud Training & Certification

...helps you make the most of Google Cloud technologies. Our classes include technical skills and best practices to help you get up to speed quickly and continue your learning journey. We offer fundamental to advanced level training, with on-demand, live, and virtual options to suit your busy schedule. Certifications help you validate and prove your skill and expertise in Google Cloud technologies.

Manual Last Updated April 30, 2021
Lab Last Tested April 30, 2021

Copyright 2022 Google LLC All rights reserved. Google and the Google logo are trademarks of Google LLC. All other company and product names may be trademarks of the respective companies with which they are associated.