Objective
This document walks through the key steps to develop an interactive store finder application using Google Maps Platform, specifically the Maps JavaScript API and the Places UI Kit: Place Details Element. You'll learn how to create a map that displays store locations, dynamically update a list of visible stores, and display rich place information for each store.
Prerequisites
Familiarity with the following is recommended:
- Maps JavaScript API - Used to show a map on your page, and to import Places UI Kit.
- Advanced Markers - Used to display markers on the map.
- Places UI Kit - Used to display information about your stores in the UI.
Enable Maps JavaScript API and Places UI Kit on your project.
Verify you have loaded the Maps JavaScript API and imported the required libraries for Advanced Markers and Places UI Kit before getting started. This document also assumes a working knowledge of web development, including HTML, CSS, and JavaScript.
Initial setup
The first step is to add a map to the page. This map will be used to display pins relating to your store locations.
There are two ways to add a map to a page:
- Using a gmp-map HTML web component
- Using JavaScript
Pick the method that works best for your use case. Both ways of implementing the map will work with this guide.
Demo
This demo shows an example of the store finder in action, displaying Google office locations in the Bay Area. The Place Details Element is shown for each location, alongside some example attributes.
Load and Display Store Locations
In this section, we will load and display your store data on the map. This guide
assumes that you have a repository of information about your existing stores to
pull from. Your store data can come from various sources, such as your database.
For this example, we assume a local JSON file (stores.json
) with an array of
store objects, each representing one store location. Each object should contain
at least a name
, location
(with lat
and lng
), and a place_id
.
There are a variety of ways to retrieve the Place IDs for your store locations if you don't have these already. See the Place ID documentation for more information.
An example store details entry in your stores.json
file could look like this.
There are fields for Name, Location (lat/lng), and Place ID. There's an object
to hold the store opening hours (truncated). There's also two boolean values to
help describe bespoke features of the store location.
{
"name": "Example Store Alpha",
"location": { "lat": 51.51, "lng": -0.12 },
"place_id": "YOUR_STORE_PLACE_ID",
"opening_hours": { "Monday": "09:00 - 17:00", "...": "..." },
"new_store_design": true,
"indoor_seating": false
}
In your JavaScript code, fetch the data for your store locations, and display a pin on the map for each one.
An example of how to do this is as follows. This function takes an object containing the details for the stores, and creates a marker based on the location of each one.
function displayInitialMarkers(storeLocations) {
if (!AdvancedMarkerElement || !LatLng || !mapElement) return;
storeLocations.forEach(store => {
if (store.location) {
const marker = new AdvancedMarkerElement({
position: new LatLng(store.location.lat, store.location.lng),
title: store.name
});
mapElement.appendChild(marker);
}
});
}
Once you have loaded your stores and have pins representing their locations displayed on the map, create a sidebar using HTML and CSS to display details about your individual stores.
An example of how your store locator could look at this stage is as follows:
Listen for Map Viewport Changes
To optimize performance and user experience, update your application to display markers and details in the sidebar only when their corresponding locations are within the visible map area (viewport). This involves listening for map viewport changes, debouncing these events, and then redrawing only the necessary markers.
Attach an event listener to the map's idle event. This event fires after any panning or zooming operations have completed, providing a stable viewport for your calculations.
map.addListener('idle', debounce(updateMarkersInView, 300));
The above code snippet listens for the idle
event, and calls a debounce
function. Using a debounce function makes sure that your marker update logic
only runs after the user has stopped interacting with the map for a short
period, improving performance.
function debounce(func, delay) {
let timeoutId;
return function(...args) {
const context = this;
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}
The above code is an example debounce function. It takes a function and delay
argument, which can be seen passed in the idle listener. A 300ms delay is enough
to wait for the map to stop moving, without adding a noticeable delay to the UI.
Once this timeout has expired, the passed function is called, in this case,
updateMarkersInView
.
The updateMarkersInView
function should perform the following actions:
Clear all existing markers from the map
Check if the location of the store falls within the current map bounds, for example:
if (map.getBounds().contains(storeLatLng)) {
// logic
}
Within the if statement above, write code to display the markers and store details on the side bar, if the store location falls within the map viewport.
Display rich place details using Place Details Element
At this stage, the application displays all store locations, and users can filter them based on the map viewport. To enhance this, rich details about each store, such as photos, reviews, and accessibility information, is added using the Place Details Element. This example specifically utilizes the Place Details Compact Element.
Each store location in your data source must have a corresponding Place ID. This ID uniquely identifies the location on Google Maps and is essential for fetching its details. You would typically acquire these Place IDs beforehand, and store these against each of your store records.
Integrate Place Details Compact Element in the application
When a store is determined to be within the current map viewport and is being rendered in the sidebar, you can dynamically create and insert a Place Details Compact Element for it.
For the current store being processed, retrieve the Place ID from your data. The Place ID is used to control which place the element will display.
In JavaScript, dynamically create an instance of PlaceDetailsCompactElement
. A
new PlaceDetailsPlaceRequestElement
is also created, the Place ID passed to
it, and this is appended to the PlaceDetailsCompactElement
. Finally, use
PlaceContentConfigElement
to configure the content the element will display.
The following function assumes that the necessary Place UI Kit libraries are
imported and available in the scope where this function is called, and
storeData
passed to the function contains place_id
.
This function will return the element, and the calling code will be responsible for appending it to the DOM.
function createPlaceDetailsCompactElement(storeData) {
// Create the main details component
const detailsCompact = new PlaceDetailsCompactElement();
detailsCompact.setAttribute('orientation', 'vertical'); // Or 'horizontal'
// Specify the Place ID
const placeRequest = new PlaceDetailsPlaceRequestElement();
placeRequest.place = storeData.place_id;
detailsCompact.appendChild(placeRequest);
// Configure which content elements to display
const contentConfig = new PlaceContentConfigElement();
// For this example, we'll render media, rating, accessibility, and attribution:
contentConfig.appendChild(new PlaceMediaElement({ lightboxPreferred: true }));
contentConfig.appendChild(new PlaceRatingElement());
contentConfig.appendChild(new PlaceAccessibleEntranceIconElement());
// Configure attribution
const placeAttribution = new PlaceAttributionElement();
placeAttribution.setAttribute('light-scheme-color', 'gray');
placeAttribution.setAttribute('dark-scheme-color', 'gray');
contentConfig.appendChild(placeAttribution);
detailsCompact.appendChild(contentConfig);
// Return the element
return detailsCompact;
}
In the example code above, the element is configured to display the place
photos, review rating, and accessibility information. This can be customized by
adding or removing other available content elements, see the
PlaceContentConfigElement
documentation for all available options.
The Place Details Compact Element supports styling through CSS custom
properties. This lets you tailor its appearance (colors, fonts, etc.) to match
your application's design. Apply these custom properties in your CSS file. See
the reference documentation for
PlaceDetailsCompactElement
for the supported CSS properties.
An example of how your application could look at this stage:
Enhance the store finder
You've built a solid foundation for your store finder application. Now, consider several ways to extend its functionality and create an even richer, more user-centric experience.
Add Autocomplete
Improve how users find areas to search for stores by integrating a search input with Place Autocomplete. When users type an address, neighborhood, or point of interest and select a suggestion, program the map to automatically center on that location, triggering an update of nearby stores. Achieve this by adding an input field and attaching the Place Autocomplete functionality to it. On select of a suggestion, the map can be centred at that point. Remember to configure it to bias or restrict results to your operational area.
Detect location
Offer immediate relevance, especially for mobile users, by implementing functionality to detect their current geographical location. After obtaining browser permission to detect their location, automatically center the map on their position and display the closest stores. Users highly value this Near Me feature when looking for immediate options. Add a button or an initial prompt to request location access.
Show distance and directions
Once a user identifies a store of interest, significantly enhance their journey by integrating the Routes API. For each store you list, calculate and display the distance from the user's current location, or from the searched location. Furthermore, provide a button or link that uses the Routes API to generate a route from the user's location to the selected store. You can then display this route on your map or link out to Google Maps for navigation, creating a seamless transition from finding a store to actually getting there.
By implementing these extensions, you can use more capabilities of the Google Maps Platform to build a more comprehensive and convenient store locator that directly addresses common user needs.
Conclusion
This guide has demonstrated the core steps to build an interactive store finder. You've learned how to display your own store locations on a map using the Maps JavaScript API, dynamically update the visible stores based on viewport changes, and significantly, how to display your own store data in line with the Places UI Kit. By using your existing store information, including Place IDs, with the Place Details Element, you can present rich, standardized details for each of your locations, creating a robust foundation for a user-friendly store locator.
Try Maps JavaScript API and Places UI Kit to offer powerful, component-based tools for rapidly developing sophisticated location-based applications. By combining these features, you can create engaging and informative experiences for your users.
Contributors
Henrik Valve | DevX Engineer