Elemento de Place Search

El PlaceSearchElement es un elemento HTML que renderiza los resultados de una búsqueda de lugares en una lista. Existen dos formas de configurar el elemento gmp-place-search:

Solicitud de búsqueda cercana

Selecciona un tipo de lugar en el menú para ver los resultados de la búsqueda cercanos para ese tipo de lugar.

En el siguiente ejemplo, se renderiza el elemento Place Search en respuesta a una búsqueda cercana. Para simplificar, solo se enumeran tres tipos de lugares: cafetería, restaurante y estación de carga de vehículos eléctricos. Cuando se selecciona un resultado, se muestran un marcador y un PlaceDetailsCompactElement para el lugar seleccionado. Para agregar el elemento Place Search al mapa, agrega un elemento gmp-place-search que contenga un elemento gmp-place-nearby-search-request a la página HTML, como se muestra en el siguiente fragmento:

<div class="list-container">
  <div id="map-container"></div>
  <div class="controls">
      <select name="types" class="type-select">
      <option value="">Select a place type</option>
      <option value="cafe">Cafe</option>
      <option value="restaurant">Restaurant</option>
      <option value="electric_vehicle_charging_station">EV charging station</option>
      </select>
  </div>

<div class="list-container">
  <gmp-place-search orientation="vertical" selectable>
      <gmp-place-all-content> </gmp-place-all-content>
      <gmp-place-nearby-search-request
      ></gmp-place-nearby-search-request>
  </gmp-place-search>
</div>

<div id="details-container">
   <gmp-place-details-compact orientation="horizontal">
      <gmp-place-details-place-request></gmp-place-details-place-request>
      <gmp-place-all-content></gmp-place-all-content>
  </gmp-place-details-compact>
</div>

Se usan varias llamadas a querySelector para seleccionar los elementos de la página con los que se puede interactuar:

  const mapContainer = document.getElementById("map-container");
  const placeSearch = document.querySelector("gmp-place-search");
  const placeSearchQuery = document.querySelector("gmp-place-nearby-search-request");
  const detailsContainer = document.getElementById("details-container");
  const placeDetails = document.querySelector("gmp-place-details-compact");
  const placeRequest = document.querySelector("gmp-place-details-place-request");
  const typeSelect = document.querySelector(".type-select");

Cuando el usuario selecciona un tipo de lugar en el menú, se actualiza el elemento gmp-place-nearby-search-request y el elemento Place Search muestra los resultados (se agregan marcadores en la función de ayuda addMarkers):

  typeSelect.addEventListener('change', (event) => {
    event.preventDefault();
    searchPlaces();
});

function searchPlaces(){
  const bounds = gMap.getBounds();
  placeDetailsPopup.map = null;

  if (typeSelect.value) {
  placeSearch.style.display = 'block';
  placeSearchQuery.maxResultCount = 10;
  placeSearchQuery.locationRestriction = { center: cent, radius: 1000 };
  placeSearchQuery.includedTypes = [typeSelect.value];

  placeSearch.addEventListener('gmp-load', addMarkers, { once: true });
  }
}

Consulta el ejemplo de código completo

 
   

JavaScript

   
  const mapContainer = document.getElementById("map-container");
  const placeSearch = document.querySelector("gmp-place-search");
  const placeSearchQuery = document.querySelector("gmp-place-nearby-search-request");
  const detailsContainer = document.getElementById("details-container");
  const placeDetails = document.querySelector("gmp-place-details-compact");
  const placeRequest = document.querySelector("gmp-place-details-place-request");
  const typeSelect = document.querySelector(".type-select");

  let markers = {};
  let gMap;
  let placeDetailsPopup;
  let spherical;
  let AdvancedMarkerElement;
  let LatLngBounds;
  let LatLng;

  async function init() {
      console.log("init");
      ({ spherical } = await google.maps.importLibrary('geometry'));
      const {Map} = await google.maps.importLibrary("maps");
      await google.maps.importLibrary("places");
      ({AdvancedMarkerElement} = await google.maps.importLibrary("marker"));
      ({LatLngBounds, LatLng} = await google.maps.importLibrary("core"));

      let mapOptions = {
          center: {lat: -37.813, lng: 144.963},
          zoom: 16,
          mapTypeControl: false,
          clickableIcons: false,
          mapId: 'DEMO_MAP_ID'
      };

      gMap = new Map(mapContainer, mapOptions);

      placeDetailsPopup = new AdvancedMarkerElement({
          map: null,
          content: placeDetails,
          zIndex: 100
      });

      findCurrentLocation();

      gMap.addListener('click', (e) => {
          hidePlaceDetailsPopup();
      });

      typeSelect.addEventListener('change', (event) => {
              event.preventDefault();
              searchPlaces();
      });

      placeSearch.addEventListener("gmp-select", ({ place }) => {
          if (markers[place.id]) {
              markers[place.id].click();
          }
      });
  }

  function searchPlaces(){
      const bounds = gMap.getBounds();
      const cent = gMap.getCenter();
      const ne = bounds.getNorthEast();
      const sw = bounds.getSouthWest();
      const diameter = spherical.computeDistanceBetween(ne, sw);
      const cappedRadius = Math.min((diameter / 2 ), 50000); // Radius cannot be more than 50000.

      placeDetailsPopup.map = null;

      for(const markerId in markers){
          if (Object.prototype.hasOwnProperty.call(markers, markerId)) {
                  markers[markerId].map = null;
              }
      }

      markers = {};
      if (typeSelect.value) {
          mapContainer.style.height = '75vh';
          placeSearch.style.display = 'block';
          placeSearchQuery.maxResultCount = 10;
          placeSearchQuery.locationRestriction = { center: cent, radius: cappedRadius };
          placeSearchQuery.includedTypes = [typeSelect.value];
          placeSearch.addEventListener('gmp-load', addMarkers, { once: true });
          console.log("selection!");
          console.log(cappedRadius);
      }
  }

  async function addMarkers(){
      const bounds = new LatLngBounds();
        placeSearch.style.display = 'block';

      if(placeSearch.places.length > 0){
          placeSearch.places.forEach((place) => {
              let marker = new AdvancedMarkerElement({
                  map: gMap,
                  position: place.location
              });

              marker.metadata = {id: place.id};
              markers[place.id] = marker;
              bounds.extend(place.location);

              marker.addListener('click',(event) => {
                  placeRequest.place = place;
                  placeDetails.style.display = 'block';

                  placeDetailsPopup.position = place.location;
                  placeDetailsPopup.map = gMap;

                  gMap.fitBounds(place.viewport, {top: 0, left: 400});

                  placeDetails.addEventListener('gmp-load',() => {
                      gMap.fitBounds(place.viewport, {top: 0, right: 450});
                  }, { once: true });

              });
              gMap.setCenter(bounds.getCenter());
              gMap.fitBounds(bounds);
          });
      }
  }

  async function findCurrentLocation(){
      const { LatLng } = await google.maps.importLibrary("core");
      if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(
            (position) => {
              const pos = new LatLng(position.coords.latitude,position.coords.longitude);
              gMap.panTo(pos);
              gMap.setZoom(16);
            },
            () => {
              console.log('The Geolocation service failed.');
              gMap.setZoom(16);
            },
          );
        } else {
          console.log("Your browser doesn't support geolocation");
          gMap.setZoom(16);
        }
  }

  function hidePlaceDetailsPopup() {
      if (placeDetailsPopup.map) {
          placeDetailsPopup.map = null;
          placeDetails.style.display = 'none';
      }
  }

  init();
  
 
 
   

CSS

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

  body {
      display: flex;
      flex-direction: column;
      font-family: Arial, Helvetica, sans-serif;
  }

  h1 {
      font-size: large;
      text-align: center;
  }

  #map-container {
      flex-grow: 1;
      max-height:600px;
      box-sizing: border-box;
      width: 100%;
      height: 100vh;
  }

  .controls {
      position: absolute;
      top: 40px;
      right: 40px;
  }

  .list-container {
      display: flex;
      position: absolute;
      max-height: 500px;
      top: 80px;
      right: 40px;
      overflow-y: none;
  }

  .type-select {
      width: 400px;
      height: 32px;
      border: 1px solid #000;
      border-radius: 10px;
      flex-grow: 1;
      padding: 0 10px;
    }

  gmp-place-search {
        width: 400px;
        margin: 0;
        border-radius: 10px;
        display: none;
        border: none;
  }

  gmp-place-details-compact {
      width: 350px;
      max-height: 800px;
      margin-right: 20px;
      display: none;
      border: none;
  }

  gmp-place-details-compact::after {
      content: '';
      position: absolute;
      bottom: -18px;
      left: 50%;
      transform: translateX(-50%);
      width: 20px;
      height: 20px;
      background-color: white;
      box-shadow: 2px 2px 5px 0 rgba(0,0,0,0.2);
      z-index: 1;
      clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
      transform-origin: center center;
  }

  @media (prefers-color-scheme: dark) {
      /* Style for Dark mode */
      gmp-place-details-compact::after {
          background-color: #131314;
      }
  }

  
 
 
   

HTML

   
<!DOCTYPE html>
  <html>
    <head>
      <title>Place Search with Compact Place Details Element</title>
      <meta charset="utf-8">
      <link rel="stylesheet" href="style.css">
      <script type="module" src="./index.js"></script>
    </head>
    <body>
      <div id="map-container"></div>
      <div class="controls">
          <select name="types" class="type-select">
          <option value="">Select a place type</option>
          <option value="cafe">Cafe</option>
          <option value="restaurant">Restaurant</option>
          <option value="electric_vehicle_charging_station">EV charging station</option>
          </select>
      </div>
      <div class="list-container">
          <gmp-place-search orientation="vertical" selectable>
              <gmp-place-all-content> </gmp-place-all-content>
              <gmp-place-nearby-search-request
              ></gmp-place-nearby-search-request>
          </gmp-place-search>
      </div>
      <div id="details-container">
          <gmp-place-details-compact orientation="horizontal">
            <gmp-place-details-place-request></gmp-place-details-place-request>
            <gmp-place-all-content></gmp-place-all-content>
          </gmp-place-details-compact>
      </div>
      <script>(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))})
        ({key: "YOUR_API_KEY", v: "weekly"});
      </script>
    </body>
  </html>
 

Solicitud de búsqueda por texto

Ingresa un término de búsqueda en el campo de entrada y haz clic en el botón Buscar para obtener una lista de lugares que coincidan con el término.

En este ejemplo, se renderiza el elemento Place Search en respuesta a una búsqueda de texto del usuario. Cuando se selecciona un resultado, se muestran un marcador y un PlaceDetailsCompactElement para el lugar seleccionado. Para agregar el elemento Place Search al mapa, agrega un elemento gmp-place-search que contenga un elemento gmp-place-search-text-search-request a la página HTML, como se muestra en el siguiente fragmento:

  <div id="map-container"></div>
  <div class="controls">
  <input type="text" class="query-input" />
  <button class="search-button">Search</button>
  </div>

  <div class="list-container">
    <gmp-place-search orientation="vertical" selectable>
        <gmp-place-all-content> </gmp-place-all-content>
        <gmp-place-text-search-request></gmp-place-text-search-request>
    </gmp-place-search>
  </div>

  <div id="details-container">
      <gmp-place-details-compact orientation="horizontal">
          <gmp-place-details-place-request></gmp-place-details-place-request>
          <gmp-place-all-content></gmp-place-all-content>
      </gmp-place-details-compact>
  </div>
  

Se usan varias llamadas a querySelector para seleccionar los elementos de la página con los que se puede interactuar:

  const mapContainer = document.getElementById("map-container");
  const placeSearch = document.querySelector("gmp-place-search");
  const placeSearchQuery = document.querySelector("gmp-place-text-search-request");
  const queryInput = document.querySelector(".query-input");
  const searchButton = document.querySelector(".search-button");
  const detailsContainer = document.getElementById("details-container");
  const placeDetails = document.querySelector("gmp-place-details-compact");
  const placeRequest = document.querySelector("gmp-place-details-place-request");

Cuando se ejecuta la función de búsqueda después de que el usuario ingresa una búsqueda, se actualiza el elemento gmp-place-text-search-request y el elemento Place Search muestra los resultados (se agregan marcadores en la función de ayuda addMarkers):

searchButton.addEventListener("click", searchPlaces);
queryInput.addEventListener("keydown", (event) => {
    if (event.key == 'Enter') {
        event.preventDefault();
        searchPlaces();
    }
  });

function searchPlaces(){
  if (queryInput.value) {
      placeSearch.style.display = 'block';
      placeSearchQuery.textQuery = queryInput.value;
      placeSearchQuery.locationBias = gMap.getBounds();
      placeSearch.addEventListener('gmp-load', addMarkers, { once: true });
  }
}

Consulta el ejemplo de código completo

 
   

JavaScript

   
  const mapContainer = document.getElementById("map-container");
  const placeSearch = document.querySelector("gmp-place-search");
  const placeSearchQuery = document.querySelector("gmp-place-text-search-request");
  const queryInput = document.querySelector(".query-input");
  const searchButton = document.querySelector(".search-button");
  const detailsContainer = document.getElementById("details-container");
  const placeDetails = document.querySelector("gmp-place-details-compact");
  const placeRequest = document.querySelector("gmp-place-details-place-request");
  let markers = {};
  let previousSearchQuery = '';
  let gMap;
  let placeDetailsPopup;

  let AdvancedMarkerElement;
  let LatLngBounds;
  let LatLng;

  async function init() {
      const {Map} = await google.maps.importLibrary("maps");
      await google.maps.importLibrary("places");
      ({AdvancedMarkerElement} = await google.maps.importLibrary("marker"));
      ({LatLngBounds, LatLng} = await google.maps.importLibrary("core"));

      let mapOptions = {
          center: {lat: 37.422, lng: -122.085},
          zoom: 2,
          mapTypeControl: false,
          clickableIcons: false,
          mapId: 'DEMO_MAP_ID'
      };

      gMap = new Map(mapContainer, mapOptions);

      placeDetailsPopup = new AdvancedMarkerElement({
          map: null,
          content: placeDetails,
          zIndex: 100
      });

      findCurrentLocation();

      gMap.addListener('click', (e) => {
          hidePlaceDetailsPopup();
      });

      searchButton.addEventListener("click", searchPlaces);
      queryInput.addEventListener("keydown", (event) => {
          if (event.key == 'Enter') {
              event.preventDefault();
              searchPlaces();
          }
      });

      placeSearch.addEventListener("gmp-select", ({ place }) => {
          if (markers[place.id]) {
              markers[place.id].click();
          }
      });
  }

  function searchPlaces(){
      if (queryInput.value.trim() === previousSearchQuery) {
          return;
      }
      previousSearchQuery = queryInput.value.trim();
      placeDetailsPopup.map = null;

      for(const markerId in markers){
          if (Object.prototype.hasOwnProperty.call(markers, markerId)) {
                  markers[markerId].map = null;
              }
      }
      markers = {};
      if (queryInput.value) {
          // mapContainer.style.height = '75vh';
          placeSearch.style.display = 'block';
          placeSearchQuery.textQuery = queryInput.value;
          placeSearchQuery.locationBias = gMap.getBounds();
          placeSearch.addEventListener('gmp-load', addMarkers, { once: true });
      }
  }

  async function addMarkers(){
      const bounds = new LatLngBounds();

      if(placeSearch.places.length > 0){
          placeSearch.places.forEach((place) => {
              let marker = new AdvancedMarkerElement({
                  map: gMap,
                  position: place.location
              });

              marker.metadata = {id: place.id};
              markers[place.id] = marker;
              bounds.extend(place.location);

              marker.addListener('click',(event) => {
                  placeRequest.place = place;
                  placeDetails.style.display = 'block';

                  placeDetailsPopup.position = place.location;
                  placeDetailsPopup.map = gMap;

                  gMap.fitBounds(place.viewport, {top: 200, right: 450});

              });

              gMap.setCenter(bounds.getCenter());
              gMap.fitBounds(bounds);
          });
      }
  }

  async function findCurrentLocation(){
      const { LatLng } = await google.maps.importLibrary("core");
      if (navigator.geolocation) {
          navigator.geolocation.getCurrentPosition(
            (position) => {
              const pos = new LatLng(position.coords.latitude,position.coords.longitude);
              gMap.panTo(pos);
              gMap.setZoom(16);
            },
            () => {
              console.log('The Geolocation service failed.');
              gMap.setZoom(16);
            },
          );
        } else {
          console.log("Your browser doesn't support geolocation");
          gMap.setZoom(16);
        }
  }

  function hidePlaceDetailsPopup() {
      if (placeDetailsPopup.map) {
          placeDetailsPopup.map = null;
          placeDetails.style.display = 'none';
      }
  }

  init();
 
 
   

CSS

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

  body {
      display: flex;
      flex-direction: column;
      font-family: Arial, Helvetica, sans-serif;
  }

  h1 {
      font-size: large;
      text-align: center;
  }

  #map-container {
      flex-grow: 1;
      max-height:600px;
      box-sizing: border-box;
      width: 100%;
      height: 100vh;
  }

  .controls {
      border-radius: 5px;
      position: absolute;
      top: 40px;
      right: 40px;
  }

  .search-button {
      background-color: #4b4b4b;
      color: #fff;
      border: 1px solid #000;
      border-radius: 10px;
      width: 80px;
      height: 40px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.35);
  }

  .query-input {
      border: 1px solid #ccc;
      border-radius: 10px;
      width: 315px;
      height: 40px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.35);
  }

  .list-container {
      display: flex;
      position: absolute;
      max-height: 500px;
      top: 100px;
      right: 40px;
      overflow-y: none;
  }

  gmp-place-search {
       width: 400px;
       margin: 0;
        border-radius: 10px;
       display: none;
       border: none;
  }

  gmp-place-details-compact {
      width: 350px;
      max-height: 800px;
      display: none;
      border: none;
      transform: translateY(calc(-40%));
  }

  gmp-place-details-compact::after {
      content: '';
      position: absolute;
      bottom: -18px;
      left: 50%;
      transform: translateX(-50%);
      width: 20px;
      height: 20px;
      background-color: white;
      box-shadow: 2px 2px 5px 0 rgba(0,0,0,0.2);
      z-index: 1;
      clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
      transform-origin: center center;
  }

  @media (prefers-color-scheme: dark) {
      /* Style for Dark mode */
      gmp-place-details-compact::after {
          background-color: #131314;
      }
  }
 
 
   

HTML

   
<!DOCTYPE html>
<html>
  <head>
    <title>Place Search with a Details Popup</title>
    <meta charset="utf-8">
    <link rel="stylesheet" href="style.css">
    <script type="module" src="./index.js"></script>
  </head>

  <body>
    <div id="map-container"></div>
    <div class="controls">
    <input type="text" class="query-input" />
    <button class="search-button">Search</button>
    </div>

    <div class="list-container">
      <gmp-place-search orientation="vertical" selectable>
          <gmp-place-all-content> </gmp-place-all-content>
          <gmp-place-text-search-request></gmp-place-text-search-request>
      </gmp-place-search>
    </div>

    <div id="details-container">
        <gmp-place-details-compact orientation="horizontal">
            <gmp-place-details-place-request></gmp-place-details-place-request>
            <gmp-place-all-content></gmp-place-all-content>
        </gmp-place-details-compact>
    </div>

    <script>(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))})
      ({key: "YOUR_API_KEY", v: "weekly"});
    </script>

  </body>
</html>