ai-powered-summaries

Sample code supporting Google Maps Platform JavaScript API documentation. author(s): Geo Developer IX Documentation Team

HTML

<!doctype html>
<!--
 @license
 Copyright 2025 Google LLC. All Rights Reserved.
 SPDX-License-Identifier: Apache-2.0
-->

<html>
  <head>
    <title>AI Place Summaries</title>
    <script>
      // prettier-ignore
      (g=>{var h,a,k,p="The Google Maps JavaScript API",c="google",l="importLibrary",q="__ib__",m=document,b=window;b=b[c]||(b[c]={});var d=b.maps||(b.maps={}),r=new Set,e=new URLSearchParams,u=()=>h||(h=new Promise(async(f,n)=>{await (a=m.createElement("script"));e.set("libraries",[...r]+"");for(k in g)e.set(k.replace(/[A-Z]/g,t=>"_"+t[0].toLowerCase()),g[k]);e.set("callback",c+".maps."+q);a.src=`https://maps.${c}apis.com/maps/api/js?`+e;d[q]=f;a.onerror=()=>h=n(Error(p+" could not load."));a.nonce=m.querySelector("script[nonce]")?.nonce||"";m.head.append(a)}));d[l]?console.warn(p+" only loads once. Ignoring:",g):d[l]=(f,...n)=>r.add(f)&&u().then(()=>d[l](f,...n))})({
            });
    </script>
  </head>
  <body>
    <gmp-map center="37.80, -122.425" zoom="14" map-id="DEMO_MAP_ID">
      <!-- Search Input Card -->
      <div
        class="place-autocomplete-card"
        slot="control-inline-start-block-start">
        <gmp-place-autocomplete
          placeholder="Find places with AI summaries"></gmp-place-autocomplete>
      </div>

      <!-- Summary text panel (initially hidden) -->
      <div
        id="summary-panel"
        class="summary-card hidden"
        slot="control-inline-end-block-start">
        <div id="place-header">
          <h2 id="place-name"></h2>
          <p id="place-address"></p>
        </div>

        <!-- Tabs for toggling summary types -->
        <div class="tab-container" id="tab-container"></div>

        <!-- Content display area -->
        <div id="summary-content" class="content-area"></div>

        <!-- Legal/AI Disclosure -->
        <div id="ai-disclosure" class="disclosure-footer"></div>

        <!-- Flag content link -->
        <a id="flag-content-link" class="flag-content-link"></a>
      </div>
   ...

CSS

/**
 * @license
 * Copyright 2025 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

/* Reuse existing map height */
gmp-map {
  height: 100%;
}

html,
body {
  height: 100%;
  margin: 0;
  padding: 0;
}

/* Existing Autocomplete Card Style */
.place-autocomplete-card {
  background-color: #fff;
  border-radius: 5px;
  box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
  margin: 10px;
  padding: 2px;
  font-family: Roboto, sans-serif;
  font-size: 1rem;
}

gmp-place-autocomplete {
  width: 330px;
}

/* New: Summary Panel Styles */
.summary-card {
  background-color: #fff;
  border-radius: 5px;
  box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
  margin: 10px;
  padding: 0; /* Padding handled by children */
  font-family: Roboto, sans-serif;
  width: 350px;
  max-height: 80vh; /* Prevent overflow on small screens */
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.hidden {
  display: none;
}

#place-header {
  padding: 15px;
  background-color: #f8f9fa;
  border-bottom: 1px solid #ddd;
}

#place-header h2 {
  margin: 0 0 5px 0;
  font-size: 1.2rem;
}

#place-address {
  margin: 0;
  color: #555;
  font-size: 0.9rem;
}

/* Tab Navigation */
.tab-container {
  display: flex;
  border-bottom: 1px solid #ddd;
  background-color: #fff;
}

.tab-button {
  flex: 1;
  background: none;
  border: none;
  padding: 10px;
  cursor: pointer;
  font-weight: 500;
  color: #555;
  border-bottom: 3px solid transparent;
}

.tab-button:hover {
  background-color: #f1f1f1;
}

.tab-button.active {
  font-weight: bold;
  border-bottom: 3px solid #000000;
}

.tab-button.active:hover {
  background-color: #ffffff;
  cursor: default;
}

/* Content Area */
.content-area {
  padding: 15px;
  line-height: 1.5;
  font-size: 0.95rem;
  color: #333;
}

.disclosure-footer {
  font-size: 0.75rem;
  color: #666;
  padding: 10px 15px;
  border-top: 1px solid #eee;
  font-style: italic;
}

.flag-content-link {
  font-size: 0.75rem;
  color: #666;
  padding: 10px...

JavaScript

'use strict';
/*
 * @license
 * Copyright 2025 Google LLC. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

// Define DOM elements.
const mapElement = document.querySelector('gmp-map');
const placeAutocomplete = document.querySelector('gmp-place-autocomplete');
const summaryPanel = document.getElementById('summary-panel');
const placeName = document.getElementById('place-name');
const placeAddress = document.getElementById('place-address');
const tabContainer = document.getElementById('tab-container');
const summaryContent = document.getElementById('summary-content');
const aiDisclosure = document.getElementById('ai-disclosure');
const flagContentLink = document.getElementById('flag-content-link');

let innerMap;
let marker;

async function init() {
  // Request needed libraries.
  const [{ AdvancedMarkerElement }] = await Promise.all([
    google.maps.importLibrary('marker'),
    google.maps.importLibrary('places'),
  ]);

  innerMap = mapElement.innerMap;
  innerMap.setOptions({
    mapTypeControl: false,
    streetViewControl: false,
    fullscreenControl: false,
  });

  // Bind autocomplete bounds to map bounds.
  innerMap.addListener('bounds_changed', () => {
    placeAutocomplete.locationRestriction = innerMap.getBounds();
  });

  // Create the marker.
  marker = new AdvancedMarkerElement({
    map: innerMap,
  });

  // Handle selection of an autocomplete result.
  placeAutocomplete.addEventListener(
    'gmp-select',
    async ({ placePrediction }) => {
      const place = placePrediction.toPlace();

      // Fetch all summary fields.

      await place.fetchFields({
        fields: [
          'displayName',
          'formattedAddress',
          'location',
          'generativeSummary',
          'neighborhoodSummary',
          'reviewSummary',
          'evChargeAmenitySummary',
        ],
      });

      // Update the map viewport and position the marker.
      if (place.viewport) {
        innerMap.fitBounds(place.viewport);
   ...