## Objective

This document walks through the key steps to develop an interactive store finder
application using Google Maps Platform, specifically the [Maps JavaScript
API](https://developers.google.com/maps/documentation/javascript) and the Places UI Kit: [Place Details
Element](https://developers.google.com/maps/documentation/javascript/places-ui-kit/place-details). 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](https://developers.google.com/maps/documentation/javascript) - Used to show a map on your page, and to import Places UI Kit.
- [Advanced Markers](https://developers.google.com/maps/documentation/javascript/advanced-markers/overview.md.txt) - Used to display markers on the map.
- [Places UI Kit](https://developers.google.com/maps/documentation/javascript/places-ui-kit/overview.md.txt) - Used to display information about your stores in the UI.

Enable [Maps JavaScript
API](https://console.cloud.google.com/apis/library/maps-backend.googleapis.com)
and [Places UI
Kit](https://console.cloud.google.com/apis/library/placewidgets.googleapis.com)
on your project.

Verify you have [loaded the Maps JavaScript
API](https://developers.google.com/maps/documentation/javascript/load-maps-js-api) and imported the required
libraries for [Advanced
Markers](https://developers.google.com/maps/documentation/javascript/advanced-markers/start#update_your_map_initialization_code)
and [Places UI
Kit](https://developers.google.com/maps/documentation/javascript/places-ui-kit/get-started#load-the-required-libraries)
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:

1. Using a gmp-map [HTML web component](https://developers.google.com/maps/documentation/javascript/add-google-map-wc-tut.md.txt)
2. Using [JavaScript](https://developers.google.com/maps/documentation/javascript/adding-a-google-map.md.txt)

Pick the method that works best for your use case. Both ways of implementing the
map will work with this guide.

## 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](https://developers.google.com/maps/documentation/javascript/place-id) 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.

```json
{
  "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.

```javascript
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:

![image](https://developers.google.com/static/maps/architecture/ui-kit-store-finder/images/store_finder_1.png)

### 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.

```javascript
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:

```javascript
if (map.getBounds().contains(storeLatLng)) {
  // logic
}
```

Within the if statement above, write code to [display the
markers](https://developers.google.com/maps/documentation/javascript/advanced-markers/overview) 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](https://developers.google.com/maps/documentation/javascript/places-ui-kit/place-details#place-details-compact-element).

Each store location in your data source must have a corresponding [Place
ID](https://developers.google.com/maps/documentation/places/web-service/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.

```javascript
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`](https://developers.google.com/maps/documentation/javascript/reference/places-widget.md.txt#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`](https://developers.google.com/maps/documentation/javascript/reference/places-widget.md.txt#PlaceDetailsCompactElement-CSS-Properties)
for the supported CSS properties.

An example of how your application could look at this stage:

![image](https://developers.google.com/static/maps/architecture/ui-kit-store-finder/images/store_finder_2.png)

## 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](https://developers.google.com/maps/documentation/javascript/place-autocomplete-overview). 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](https://developers.google.com/maps/documentation/javascript/geolocation). 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](https://developers.google.com/maps/documentation/routes). 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](https://developers.google.com/maps/documentation/javascript) and [Places UI
Kit](https://developers.google.com/maps/documentation/javascript/places-ui-kit/overview) 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](https://www.linkedin.com/in/henrikvalve/) \| DevX
Engineer