Agrega marcadores y animación a un mapa 3D fotorrealista

1. Antes de comenzar

En este instructivo, se explora cómo agregar marcadores en 3D y aplicarles diseño en tu aplicación. También aprenderás a animar tu aplicación volando a ubicaciones específicas y alrededor de ellas.

En este instructivo, se amplían los conceptos que se abordaron en el primer codelab. Si aún no lo hiciste, completa ese codelab para obtener los conocimientos básicos necesarios para esta aplicación.

Actividades

“El mapa completo con marcadores.

Esta aplicación proporciona una descripción general de las principales oficinas de Google en Europa. Los usuarios pueden seleccionar una oficina, volar dentro y alrededor de ella para explorarla y, luego, alejar la imagen para volver a la vista general. Estas funciones, que suelen encontrarse en aplicaciones de viajes y exploración, ofrecen una experiencia más envolvente para los usuarios.

En este codelab, crearás una app web en 3D que haga lo siguiente:

  • Carga la API de Maps JavaScript de forma dinámica.
  • Agrega marcadores en 3D al mapa.
  • Aplica diseño a los marcadores con SVG.
  • Agrega la capacidad de volar hacia los marcadores y alrededor de ellos.
  • Abstrae las ubicaciones del código en un array.

Qué aprenderás

  • Cómo funcionan los marcadores
  • Cómo aplicar diseño a los marcadores
  • Cómo funciona la animación con las funciones integradas
  • Posiciones de la cámara en comparación con las posiciones de punto para mejorar el encuadre
  • Trucos útiles para capturar parámetros de la cámara y encuadrar mejor los elementos.

Requisitos previos

Deberás familiarizarte con los elementos que se indican aquí para completar este codelab. Si ya sabes cómo trabajar con Google Maps Platform, pasa directamente al codelab.

Productos obligatorios de Google Maps Platform

En este codelab, usarás los siguientes productos de Google Maps Platform:

  • API de Maps JavaScript

Otros requisitos de este codelab

Para completar este codelab, necesitarás las siguientes cuentas, servicios y herramientas:

  • Una cuenta de Google Cloud con la facturación habilitada
  • Una clave de API de Google Maps Platform con la API de Maps JavaScript habilitada
  • Conocimientos básicos de JavaScript, HTML y CSS
  • Un editor de texto o IDE que prefieras para guardar y editar un archivo para verlo
  • Un navegador web para ver el archivo mientras trabajas

2. Prepárate

Configura Google Maps Platform

Si todavía no tienes una cuenta de Google Cloud Platform y un proyecto con la facturación habilitada, consulta la guía Cómo comenzar a utilizar Google Maps Platform para crear una cuenta de facturación y un proyecto.

  1. En Cloud Console, haz clic en el menú desplegable del proyecto y selecciona el proyecto que deseas usar para este codelab.

  1. Habilita las API y los SDK de Google Maps Platform necesarios para este codelab en Google Cloud Marketplace. Para hacerlo, sigue los pasos que se indican en este video o esta documentación.
  2. Genera una clave de API en la página Credenciales de Cloud Console. Puedes seguir los pasos que se indican en este video o esta documentación. Todas las solicitudes a Google Maps Platform requieren una clave de API.

3. Globo terráqueo simple

Para comenzar a compilar la aplicación, es fundamental establecer la configuración básica. Esto producirá una vista de "azulejos azules" de la Tierra en su forma más esencial, como se muestra en la imagen:

“Imagen que muestra el globo terráqueo tal como se configura inicialmente.

Agrega el código de la página de partida

Para agregar el globo terráqueo al sitio, deberás agregar el siguiente código a tu página. Esto agregará una sección para el cargador de la API de Maps JavaScript y una función de inicialización que crea el elemento de mapa 3D dentro de la página en la que agregarás el código de los marcadores.

Asegúrate de agregar tu propia clave (creada en la sección de configuración) a la página. De lo contrario, no se podrá inicializar el elemento 3D.

<!DOCTYPE html>
<html>
   <head>
       <title>Step 1 - Simple Globe</title>
       <style>
           body {
               height: 100vh;
               margin: 0;
           }
       </style>
   </head>

   <body>
       <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: "<INSERT API KEY>",
               v: "alpha",
               // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
               // Add other bootstrap parameters as needed, using camel case.
           });
       </script>
       <script>
           let map3D = null;

           async function init() {
               const { Map3DElement, MapMode } = await google.maps.importLibrary("maps3d");

               map3D = new Map3DElement({
                   mode: MapMode.HYBRID,
               });

               document.body.append(map3D);
           }
           init();
       </script>
   </body>
</html>

Con esto listo, puedes comenzar a encuadrar la ubicación de interés, lo que harás en el siguiente paso.

4. Enmarca la primera vista

Ahora que creaste un mapa con una vista de globo terráqueo, el siguiente paso de la implementación es enmarcar la ubicación de partida correcta. Esto permite que el usuario obtenga una descripción general instantánea de dónde está trabajando.

Si bien este ejemplo se enfoca en las oficinas de Google en Europa, puedes aplicar este enfoque a cualquier ubicación del mundo, desde un país completo hasta un solo bloque de una ciudad. La velocidad y la flexibilidad del producto te permiten escalar tu aplicación de global a local con cambios mínimos en el código.

Comenzarás con el encuadre inicial para que el mapa 3D se vea de la siguiente manera:

“El globo terráqueo centrado en Europa.

Encuadra la cámara en Europa

Para obtener la vista que se muestra, debes encuadrar la pantalla correctamente, como si estuvieras colocando una cámara en el espacio que mira hacia abajo en la ubicación.

Para ello, se pueden usar varios parámetros del control de mapa para establecer los detalles de la cámara. En el diagrama, puedes ver cómo interactúan los parámetros en el mundo "real". Específicamente, está el punto central al que está apuntando la cámara y la distancia desde la que estás mirando (el rango). También debes configurar la inclinación de la perspectiva de la cámara (de lo contrario, mirarás directamente hacia abajo a la Tierra).

&quot;Una imagen que muestra los parámetros de la cámara.

El parámetro de configuración final, heading, determina la dirección de la cámara. Se mide como la compensación desde el norte. Estos valores se aplican al elemento del mapa 3D como un objeto para configurar la visualización inicial. Puedes ver esto en el código con el constructor de elementos 3D actualizado.

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.HYBRID
});

Captura los parámetros de la cámara

Encuadrar una vista en un mapa 3D requiere una ubicación precisa de la cámara, lo que puede ser difícil de lograr solo con código. Para simplificar este proceso, usa este truco útil: agrega una función a tu página que capture los parámetros de la cámara cuando hagas clic en la vista requerida. Los parámetros se mostrarán en la consola, listos para copiarse en la configuración de la cámara de tu objeto.

Puedes encontrar el código que podrías querer usar más adelante, ya que se agrega al ejemplo de esta página que se muestra, aunque no estará en el ejemplo de las páginas posteriores, ya que no es necesario para el codelab, pero es algo que debes recordar si quieres crear demostraciones más envolventes a través de una mejor posición de la cámara.

map3D.addEventListener('gmp-click', (event) => {
   console.log("camera: { center: { lat: " + map3D.center.lat + ", lng : " + map3D.center.lng + ", altitude: " + map3D.center.altitude + " }, range: " + map3D.range + ", tilt: " + map3D.tilt + " ,heading: " + map3D.heading + ", }");
   console.log("{ lat: " + event.position.lat + ", lng : " + event.position.lng + ", altitude: " + event.position.altitude + " }");
   // Stop the camera animation when the map is clicked.
   map3D.stopCameraAnimation();
});

Observa el uso de la función stopCameraAnimation. Si la página se acerca o se aleja, es útil poder detener la animación para capturar la ubicación en la pantalla en ese momento. Este código te permite hacerlo. Hay más detalles en la documentación de stopCameraAnimation.

Ejemplo del resultado del clic, como se muestra en la consola.

camera: { center: { lat: 51.39870122020001, lng : -0.08573187165829443, altitude: 51.66845062662254 }, range: 716.4743880553578, tilt: 50.5766672986501 ,heading: -1.048260134782318, }
step2.html:40 { lat: 51.398158351120536, lng : -0.08561139388593597, altitude: 51.860469133677626 }

El texto de la cámara se puede usar como entrada JSON en una variedad de objetos en los mapas en 3D. El segundo resultado es la ubicación real del punto en el que se produjo el clic, que también es útil para crear puntos o cualquier elemento para el posicionamiento de marcadores.

Ahora que la página está encuadrada correctamente, puedes agregar marcadores. Continúa con el siguiente paso para obtener información.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

<!DOCTYPE html>
<html>

<head>
   <title>Step 2 - Europe View</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
<script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode } = await google.maps.importLibrary("maps3d");

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.HYBRID,
            });

           map3D.addEventListener('gmp-click', (event) => {
               console.log("camera: { center: { lat: " + map3D.center.lat + ", lng : " + map3D.center.lng + ", altitude: " + map3D.center.altitude + " }, range: " + map3D.range + ", tilt: " + map3D.tilt + " ,heading: " + map3D.heading + ", }");
               console.log("{ lat: " + event.position.lat + ", lng : " + event.position.lng + ", altitude: " + event.position.altitude + " }");

               map3D.stopCameraAnimation();
           });

           document.body.append(map3D);
       }
       init();
   </script>

</body>

</html>

5. Marcador simple

En esta sección, aprenderás a agregar tu primer marcador. Primero, aprenderás detalles generales sobre los marcadores.

Los mapas 3D admiten la creación de dos clases de marcadores diferentes: Marker3DElement y Marker3DInteractiveElement. La elección de una de ellas depende de si deseas habilitar los clics en los marcadores o no. Aparte de esto, son esencialmente iguales, por lo que primero crearás un Marker3DElement y, luego, lo “actualizarás” a Marker3DInteractiveElement en pasos futuros.

Puedes ver la solución completa de este paso aquí:

“Un globo terráqueo con un marcador que muestra el paso completo.

Agrega altura a tus marcadores

Lo primero que debes saber es que los marcadores son 3D, al igual que todo lo demás en el mapa 3D. Eso significa que la ubicación puede tener una altura (altitud) y que esa altura puede ser representativa como una posición en relación con el nivel del mar, el suelo, la malla o establecerse para fijarse en el suelo y, así, ignorar la ubicación de altitud. Puedes ver más detalles en la sección Constantes de altitud de la documentación de AltitudeMode.

También puedes establecer si el marcador se extruye o no con el valor extruded. Esto determinará si el marcador tendrá una pequeña línea dibujada hasta el suelo para ayudar a mostrar la posición real en relación con la altura, lo que es útil para elegir puntos en el suelo. Puedes ver un ejemplo de esto con la ubicación de Google en el Reino Unido. Ambos se extruyen y tienen su posición establecida en una altura absoluta. El primero a 75 metros y el segundo a 125 metros.

Marcador a 75 metros

Marcador a 125 metros

Altitud: 75 metros

Altitud: 125 metros

Oculta o muestra marcadores con oclusión y colisión

Si bien es posible que no sea importante en nuestra demostración, ya que las posiciones están bastante separadas, para los marcadores que pueden superponerse entre sí o quedar detrás de los edificios, puedes controlar lo que les sucede con los valores collisionBehavior o drawsWhenOccluded.

Para el comportamiento de colisión, tienes las siguientes opciones:

  • REQUIRED: (opción predeterminada) Indica que el marcador debe mostrarse siempre, independientemente de las superposiciones.
  • OPTIONAL_AND_HIDES_LOWER_PRIORITY: Indica que el marcador solo debe mostrarse si no se superpone con otros. Si dos marcadores de este tipo se superponen, se mostrará el que tenga el valor de zIndex más alto. Si tienen el mismo valor de zIndex, se mostrará el que tenga la posición vertical más baja en la pantalla.
  • REQUIRED_AND_HIDES_OPTIONAL: Indica que el marcador debe mostrarse siempre, independientemente de las superposiciones, y que se deben ocultar los marcadores OPTIONAL_AND_HIDES_LOWER_PRIORITY que se superpongan con él.

En las imágenes, se muestran las diferencias en la forma en que se muestran los marcadores según el comportamiento de colisión definido. Cuando se configura REQUIRED, se muestran todos los marcadores, pero si usas REQUIRED_AND_HIDES_OPTIONAL, en este caso, se mostrarán los marcadores más abajo en la pantalla (puedes jugar con el zIndex para hacer que otros marcadores se muestren en la parte superior si lo deseas).

Los marcadores se muestran como obligatorios

Marcadores que ocultan otros marcadores

OBLIGATORIO

REQUIRED_AND_HIDES_OPTIONAL

En el caso de la oclusión, puedes elegir si quieres que los marcadores se dibujen detrás de los edificios o no. Esto se muestra en la siguiente imagen. Cuando drawsWhenOccluded se establece como verdadero, muestra los marcadores ligeramente atenuados cuando se dibujan detrás de los edificios. Cuando se establece como falso, oculta los marcadores cuando están detrás de un edificio. Puedes encontrar más detalles en la siguiente tabla:

Imagen que muestra el mapa que oculta los marcadores ocluidos

Imagen que muestra un mapa con marcadores ocluidos

drawsWhenOccluded : false

drawsWhenOccluded : true

Como se mencionó, los marcadores ocultos por una colisión se mostrarán atenuados si se permite el dibujo de marcadores ocluidos. En la imagen, puedes ver algunos de los marcadores que ocultan los edificios y otros que ocultan otros marcadores.

&quot;Una imagen que muestra una serie de marcadores y el efecto de la oclusión.

Consulta el ejemplo de comportamiento de colisión en un mapa 2D para obtener más detalles.

Borra el lienzo

Ahora es el momento de crear tu primer marcador. Para asegurarte de que el usuario se enfoque en los marcadores, puedes inhabilitar las etiquetas predeterminadas en el mapa 3D.

Establece el valor mode del elemento de mapa 3D en SATELLITE.

Para obtener más información, consulta mode.

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.SATELLITE
});

El resultado es el siguiente mapa 3D:

&quot;Imagen de Europa sin bordes ni texto.

Agrega el primer marcador

Con un lienzo limpio, ahora puedes agregar el primer marcador. Entre los parámetros clave, se incluyen la posición y la etiqueta.

Para agregar un marcador, configura su posición. También puedes incluir una etiqueta, que aparece sobre el marcador, y otros elementos, como se describe en la documentación de Marker3DElement.

Para agregar nuestro marcador, agrega el siguiente código después de la línea que oculta las etiquetas predeterminadas, como se muestra a continuación:

const marker = new Marker3DElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

map3D.append(marker);

Después de crear el marcador, agrégalo al mapa 3D con el método de adición. Recuerda que los marcadores se almacenan como un array de elementos secundarios dentro del mapa 3D. Para modificar un marcador, deberás acceder a él a través de este array.

Para asegurarte de que Marker3DElement se cargue desde la API de Maps JavaScript, agrégalo a la lista de bibliotecas cuando cargues la API.

const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");

Ahora, cuando se cargue la página, se podrá ver toda Europa con un marcador sobre la oficina de Londres. Como se muestra en la animación, puedes acercar la imagen de forma manual para ver el marcador sobre la ubicación creada.

&quot;Animación que muestra el zoom manual en Google Reino Unido.

Ahora que cargaste tu primer marcador, el siguiente paso es hacer que se vea mejor.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

<!DOCTYPE html>
<html>

<head>
   <title>Step 3 - Simple Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
            const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");

            map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
            });

           const marker = new Marker3DElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });
           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

6. Marcador SVG

En este paso, harás que el marcador se vea mejor cambiando el marcador para agregar una bandera que represente el país en el que se encuentra. Veamos cómo se hace y, para ello, debes familiarizarte con PinElement.

Al final, tendrás un nuevo aspecto, como se muestra a continuación:

&quot;Imagen con un marcador con la bandera del Reino Unido&quot;

Personalización básica con PinElement

Uno de los elementos que se comparten entre los marcadores en la API de JavaScript, ya sean mapas 2D o 3D, es PinElement. Mientras agregas un Marker3DElement al Map3DElement , agregas un PinElement al Marker3DElement como elemento secundario.

PinElement contiene la capacidad, a un nivel básico, de alterar el marcador normal para establecer su color de borde, color de punto interior (o glifo) y color de fondo. Puedes verlos en la imagen que muestra un marcador 2D.

&quot;Imagen con opciones para la personalización de pines de marcador&quot;

También puedes establecer un tamaño de marcador a través del elemento configurando su valor de escala (>1 es más grande de lo normal y <1 es más pequeño como proporción).

También puedes reemplazar el glifo por una imagen o un archivo SVG si deseas darle un aspecto más personalizado, pero sin dejar de lado el aspecto estándar del pin de mapa de PinElement.

Más allá de PinElements

En este paso, actualizarás el PinElement estándar con una marca svg y diferentes colores, pero también es bueno tener en cuenta que puedes cambiar por completo el aspecto de un marcador para que ni siquiera se vea como un pin de mapa. Dentro del marcador, también puedes insertar gráficos nuevos con el uso de plantillas, como HTMLImageElement y SVGElement. Puedes obtener más detalles sobre cómo hacerlo en la documentación Marker3DElement-Slots.

Para ver lo que es posible hacer, consulta los siguientes ejemplos que muestran cómo aplicar diseño a marcadores con varias técnicas diferentes.

Imagen que muestra la personalización básica de los marcadores.

Imagen en la que se muestra la personalización de marcadores complejos.

Marcadores con personalización básica a través de PinElement, consulta los ejemplos.

Marcadores con personalización compleja a través de plantillas con SVG e imágenes. Consulta los ejemplos.

Agrega PinElement.

Para cambiar el aspecto del marcador, lo primero que debes hacer es asegurarte de que la biblioteca de PinElement se haya agregado a la página. Para ello, agrega la siguiente línea de código después de importar la biblioteca maps3d:

const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");
const { PinElement } = await google.maps.importLibrary('marker');

Ahora que se cargó el elemento, se puede crear y hacer referencia a PinElement. Observa el código, agrégalo entre el lugar donde se crea el marcador y adjúntalo al mapa 3D.

const marker = new Marker3DElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

const markerPin = new PinElement({
   "background": 'white',
   "glyph": new URL(base + '/images/gb.svg'),
   "scale": 1.0,
});
marker.append(markerPin);

map3D.append(marker);

Como no solo estás cargando un pin básico, hay varias cosas que debes hacer más allá de configurar el PinElement, con su escala y color de fondo asociados.

Primero, se debe hacer referencia a una imagen svg para el ícono de la bandera, en este caso, la Union Jack. Puedes obtenerlos de una colección como esta en https://flagicons.lipis.dev/.

Una vez que tengas el ícono, puedes colocarlo en un lugar que el sitio pueda encontrar. En este caso, puedes codificar la ubicación de la imagen o usar la ubicación actual del sitio como el stub del directorio, como se muestra aquí con la variable base. Luego, puedes vincular esto a la ubicación en el servidor con la marca correcta, que aquí se encuentra en '/images/gb.svg'.

Esto crea un PinElement que se ve como el que se muestra a continuación:

&quot;Marcador que muestra el símbolo de la bandera del Reino Unido&quot;.

Una vez que hayas colocado la marca en el lugar correcto y el código en el lugar correcto, deberías tener un mapa 3D que se vea de la siguiente manera:

&quot;Acercamiento al nuevo marcador.

Ahora que tenemos nuestro marcador listo, también se puede cambiar para que se pueda hacer clic en él y permitir que se agregue interactividad. Esto se hará en el siguiente paso.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

Además, no olvides que deberás obtener el archivo de bandera svg (o el archivo png que elijas) y almacenarlo en un directorio que tu página pueda encontrar (aquí se almacena en la carpeta de imágenes).

<!DOCTYPE html>
<html>

<head>
   <title>Step 4 - SVG Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

7. Marcador interactivo

En el último paso, se agregó un marcador a la página, pero, además de verse bien, no hace mucho y aún tienes que interactuar con el mapa 3D de la misma manera. El siguiente paso es agregar la capacidad de hacer algo con el marcador cuando hagas clic en él, lo que le permitirá reaccionar a la interacción del usuario.

Para agregar esta función, debes transformar el Marker3DElement en un Marker3DInteractiveElement. Al final, tendrás una página de aspecto similar, pero en la que, con un clic en el marcador, aparecerá una alerta que se verá de la siguiente manera:

&quot;Imagen que muestra la respuesta cuando se hace clic en ella.

Primero, cambia la clase de marcador

Para agregar interactividad a un marcador, debes asegurarte de que use la clase correcta. Marker3DInteractiveElement es el que se necesita, pero como es una extensión de Marker3DElement, no necesitas hacer nada más que cargar la clase nueva y cambiar el nombre de la clase en el constructor.

const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
const { PinElement } = await google.maps.importLibrary('marker');

map3D = new Map3DElement({
    center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
    range: 5814650,
    tilt: 33,
    heading: 4.36,
    mode: MapMode.SATELLITE,
});

const marker = new Marker3DInteractiveElement({
   position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
   label: 'Google UK',
   altitudeMode: 'ABSOLUTE',
   extruded: true,
});

En segundo lugar, agrega el evento de clic al marcador.

A continuación, agrega un evento de clic al marcador para controlar la interacción del usuario y responder. En el fragmento, puedes ver que el evento de clic se agrega al marcador. En este caso, se activa una alerta y aparece el texto que muestra la etiqueta del marcador, obtenida del objetivo del evento activado, lo que nos permite acceder a la propiedad de la etiqueta. Agrega el siguiente código a tu aplicación justo después de que se construya el marcador.

marker.addEventListener('gmp-click', (event) => {
   alert('You clicked on : ' + event.target.label);
   event.stopPropagation();
});

Ten en cuenta que el evento stopPropagation se usa para asegurarse de que cualquier otro objeto de escucha de clics en la pila se active en objetos subyacentes, como el lienzo principal del mapa 3D.

Ahora, cuando ejecutes tu aplicación, deberías obtener el siguiente resultado:

&quot;Imagen que muestra la respuesta cuando se hace clic en ella.

Con la capacidad de hacer algo cuando se hace clic en el marcador, ahora es posible agregar una animación a la página en el siguiente paso.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

<!DOCTYPE html>
<html>

<head>
   <title>Step 5 - Interactive Marker</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               alert('You clicked on : ' + event.target.label);
               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

8. Visualizar

En este paso, usarás la capacidad de hacer clic en el marcador para agregar una animación que te lleve a su ubicación. Puedes ver esto en acción aquí.

&quot;Animación que muestra el marcador en el que se hizo clic y el desplazamiento a la ubicación.

Cómo animar con flyCameraTo

Para agregar esto a la página, usarás el método flyCameraTo de Maps 3D, en el que la cámara se anima entre la ubicación en la que te encuentras y la que quieres ver, interpolando entre ambas y animando el vuelo dentro del mapa 3D.

Cuando usas flyCameraTo, debes especificar FlyToAnimationOptions, que tiene dos propiedades: endCamera, que es la ubicación a la que debe apuntar la cámara al final de la animación, y durationMillis, que es la duración en milisegundos que tardará en realizarse la transición.

En el ejemplo, configura la cámara para que mire el edificio que es la posición del marcador, con una inclinación de 65 grados, un rango de 500 metros y que apunte hacia el norte con un rumbo de 0 grados. Establece el tiempo de la animación en 12,500 milisegundos (12.5 s).

Reemplaza el evento de alerta actual en la página por el fragmento flyCameraTo:

marker.addEventListener('gmp-click', (event) => {
   map3D.flyCameraTo({
       endCamera: {
           center: marker.position,
           tilt: 65,
           range: 500,
           heading: 0,
       },
       durationMillis: 12500,
   });

   event.stopPropagation();
});

Eso es todo. Ahora deberías poder actualizar la página, hacer clic en el marcador y volar a Google Reino Unido, como se muestra en la animación:

&quot;Animación que muestra el marcador en el que se hizo clic y el desplazamiento a la ubicación.

En este paso, agregaste un marcador en el que se puede hacer clic que lleva la cámara a la ubicación del marcador. En el siguiente paso, agregarás la capacidad de volar la cámara alrededor del punto para que orbite la ubicación.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

<!DOCTYPE html>
<html>

<head>
   <title>Step 6 - Zoom To</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
                center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
                range: 5814650,
                tilt: 33,
                heading: 4.36,
                mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               map3D.flyCameraTo({
                   endCamera: {
                       center: marker.position,
                       tilt: 65,
                       range: 500,
                       heading: 0,
                   },
                   durationMillis: 12500,
               });

               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

9. Desplazarse

El último elemento de nuestra animación es usar el método flyCameraAround para animar una órbita alrededor del edificio. Al final, tendrás una animación que volará al edificio y luego volará a su alrededor como se muestra en la animación. Es probable que sea un poco rápido para un ejemplo real, pero es útil para mostrar cómo funciona la acción, sin que sea demasiado larga. Puedes experimentar con los tiempos hasta obtener un valor que te resulte útil.

&quot;Animación que muestra cómo se hace clic en un marcador y, luego, se vuela hacia una ubicación y alrededor de ella.

¡Vamos a volar!

El método flyCameraAround es similar a la función flyCameraTo en el sentido de que toma una serie de opciones como entrada que controlan qué ubicación orbitar, como los parámetros de la cámara y el tiempo en milisegundos que tarda en orbitar. Por último, también puedes especificar la cantidad de rotaciones que pueden ocurrir en el tiempo especificado. Puedes ver todas las opciones aquí en FlyAroundAnimationOptions.

Pero espera un minuto.

En la animación, puedes ver cómo vuela a la ubicación y, luego, alrededor de ella, encadenando las animaciones. Para ello, usa el evento gmp-animationend de Maps 3D para asegurarte de que la animación actual haya terminado antes de activar la siguiente. Esta animación solo debe ocurrir una vez antes de detenerse.

Observa el código y, luego, insértalo después del código agregado en la sección anterior.

marker.addEventListener('gmp-click', (event) => {
   map3D.flyCameraTo({
       endCamera: {
           center: marker.position,
           tilt: 65,
           range: 500,
           heading: 0,
       },
       durationMillis: 5000,
   });

   map3D.addEventListener('gmp-animationend', () => {
       map3D.flyCameraAround({
           camera: {
               center: marker.position,
               tilt: 65,
               range: 500,
               heading: 0,
           },
           durationMillis: 5000,
           rounds: 1
       });
   }, { once: true });

   event.stopPropagation();
});

Agregar la capacidad de escuchar el evento gmp-animationend le permite invocar el evento flyCameraAround. Si estableces el punto de partida como el mismo que se usó para la cámara final del método de desplazamiento, se producirá una transición fluida (para no causar movimientos bruscos a una ubicación nueva). Una vez más, durationMillis se establece para controlar el tiempo que tarda en realizarse la animación. En este caso, el método también toma otra opción, rounds, que se establece en 1.

Esto significa que la cámara rotará alrededor del punto una vez cada 5 segundos. Puedes experimentar con estos valores para encontrar el número que mejor se adapte a tus necesidades.

En este punto, finalizará la animación, pero no quieres que el evento gmp-animationend se vuelva a activar con este fragmento de código, lo que provocará que la órbita se produzca de forma infinita. Para evitar esto, se debe establecer la opción once en true. Esto significa que el evento se quitará una vez que se complete, lo que evitará el bucle infinito.

Una vez que lo hagas, deberías poder ejecutar la solución y ver que la animación ahora vuela alrededor del marcador al final, como se muestra en la animación:

&quot;Animación que muestra un vuelo alrededor de un marcador.

En este paso, agregaste un marcador en el que se puede hacer clic. Luego, la cámara vuela hacia la ubicación del marcador y alrededor de ella. En la siguiente etapa, es hora de comenzar a agregar más puntos y permitirnos movernos entre ellos.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

<!DOCTYPE html>
<html>

<head>
   <title>Step 7 - Zoom Around</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           const marker = new Marker3DInteractiveElement({
               position: { lat: 51.5332, lng: -0.1260, altitude: 75 },
               label: 'Google UK',
               altitudeMode: 'ABSOLUTE',
               extruded: true,
           });

           marker.addEventListener('gmp-click', (event) => {
               map3D.flyCameraTo({
                   endCamera: {
                       center: marker.position,
                       tilt: 65,
                       range: 500,
                       heading: 0,
                   },
                   durationMillis: 5000,
               });

               map3D.addEventListener('gmp-animationend', () => {
                   map3D.flyCameraAround({
                       camera: {
                           center: marker.position,
                           tilt: 65,
                           range: 500,
                           heading: 0,
                       },
                       durationMillis: 5000,
                       rounds: 1
                   });
               }, { once: true });

               event.stopPropagation();
           });

           const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

           const markerPin = new PinElement({
               "background": 'white',
               "glyph": new URL(base + '/images/gb.svg'),
               "scale": 1.0,
           });
           marker.append(markerPin);

           map3D.append(marker);

           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

10. ¡París!

Si bien Londres es una gran ciudad, parece un poco sola en la página, así que comencemos a agregar algunas ubicaciones nuevas, empezando por París. Para ello, se puede usar un array para contener todos los detalles específicos de la ubicación y, luego, usarlo como entrada para las funciones y variables que establecen los parámetros de visualización de marcadores y también el desplazamiento hacia las ubicaciones de la cámara y alrededor de ellas. Que, como se mencionó, puede diferir de la ubicación del punto del marcador para encuadrar mejor una toma de cámara de un edificio, por ejemplo.

“Animación que muestra un clic y un vuelo a Google Francia y alrededor de esta ubicación.

Array de ubicación

Para no tener que codificar de forma fija todos los detalles de una ubicación en particular, como la cámara de visualización, el punto de marcador y las opciones de visualización, puedes usar un pequeño array de objetos JSON para contener estos datos. Esto se puede aplicar cuando se crean y usan los marcadores en la aplicación. Puedes ver este ejemplo en el fragmento de código, en el que se crea una variable llamada officeLocations para contener el array.

Agrega el siguiente código justo antes de la función init. Además, ten en cuenta que la variable base se movió fuera de la función init para que se pueda aplicar a todas las ubicaciones de las oficinas.

const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

const europeCamera = {
   center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
   range: 5814650,
   tilt: 33,
   heading: 4.36,
};

const officeLocations = [
   {
       "name": "Google France",
       "camera": {
           "center": { lat: 48.877276, lng: 2.329978, altitude: 48 },
           "range": 178,
           "tilt": 57.48,
           "heading": -17,
       },
       "point": { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       "pin": {
           "background": 'white',
           "glyph": new URL(base + '/images/fr.svg'),
           "scale": 1.0,
       },
   },
   {
       "name": "Google UK",
       "camera": {
           "center": { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           "range": 500,
           "tilt": 56.21672368296945,
           "heading": -31.15763027564165,
       },
       "point": { lat: 51.5332, lng: -0.1260, altitude: 75 },
       "pin": {
           "background": 'white',
           "glyph": new URL(base + '/images/gb.svg'),
           "scale": 1.0,
       },
   }]
       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

Cada ubicación de oficina tiene las siguientes propiedades:

  • name : Es el nombre de la ubicación.
  • camera : Es la vista inicial para ver la ubicación a la que se volará y a su alrededor.
  • point : Es la ubicación en la que se colocará el marcador.
  • pin : Los detalles del color del pin del marcador y las propiedades del glifo

Un ángulo diferente

Aquí puedes notar que, para el Reino Unido, el centro de la cámara y el punto del marcador son los mismos (aparte de la altitud), mientras que, para Francia, la cámara y el punto son diferentes. Esto se debe a que, para la ubicación de Francia, el marcador debe estar en una ubicación diferente a la vista inicial de la cámara, lo que proporciona una mejor vista de todo el edificio cuando se vuela hacia él y alrededor de él que la que se podría obtener si se usara el punto del marcador.

Volver a Europa

Una función de tener más puntos es que agrega un requisito para poder moverse entre ellos. Puedes usar un menú desplegable para permitir la selección, pero en este ejemplo, la cámara volverá a la vista europea cada vez que el usuario seleccione otra ubicación.

Para ello, la vista inicial debe almacenarse en una variable que se pueda usar para restablecer la cámara a la vista completa de Europa. En este ejemplo, agrega una variable nueva llamada europeCamera para almacenarla y usarla más adelante.

Actualiza la función init

La primera edición que debes hacer es usar el objeto europeCamera como entrada cuando crees el Map3DElement.

La segunda edición que debes hacer es unir la sección de creación de marcadores en un bucle para actualizarla con los parámetros almacenados en las variables. Puedes ver esto en el código que se muestra a continuación:

  • office.point : Es la ubicación del marcador.
  • office.name : Es el nombre de la oficina que se usa para la etiqueta del marcador.
  • office.camera : Es la ubicación inicial de la cámara.
  • office.pin : Son las opciones de PinElement para las diferencias de visualización.

Además, no olvides obtener un archivo o una imagen SVG para la bandera de Francia.

async function init() {
   const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
   const { PinElement } = await google.maps.importLibrary('marker');

   map3D = new Map3DElement({
       ...europeCamera,
       mode: MapMode.SATELLITE,
   });

   officeLocations.forEach(office => {
       const marker = new Marker3DInteractiveElement({
           position: office.point,
           label: office.name,
           altitudeMode: 'ABSOLUTE',
           extruded: true,
       });

       marker.addEventListener('gmp-click', (event) => {
           map3D.flyCameraTo({
               endCamera: office.camera,
               durationMillis: 5000,
           });

           map3D.addEventListener('gmp-animationend', () => {
               map3D.flyCameraAround({
                   camera: office.camera,
                   durationMillis: 5000,
                   rounds: 1
               });

               map3D.addEventListener('gmp-animationend', () => {
                   map3D.flyCameraTo({
                       endCamera: europeCamera,
                       durationMillis: 5000,
                   });
               }, { once: true });

           }, { once: true });

           event.stopPropagation();
       });

       const markerPin = new PinElement(office.pin);
       marker.append(markerPin);

       map3D.append(marker);
   });
   document.body.append(map3D);
}

Ten en cuenta que se agrega una segunda función gmp-animationend después de la animación flyCameraAround para controlar el regreso a la vista europea con la variable europeCamera almacenada. Como se muestra en la animación:

“Animación de un avión volando entre oficinas y alrededor de ellas en Francia y el Reino Unido.

En esta etapa, la aplicación se amplió para tener dos ubicaciones y la capacidad de volar entre ellas con animación y un array de ubicaciones. En la siguiente etapa, se agregará el resto de las ubicaciones de las oficinas al array.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

Además, no olvides que deberás obtener el archivo de SVG de la bandera (o los archivos PNG que elijas) y guardarlo en un directorio que tu página pueda encontrar (aquí se almacena en la carpeta de imágenes).

<!DOCTYPE html>
<html>

<head>
   <title>Step 8 - Paris!</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

       const officeLocations = [
           {
               "name": "Google France",
               "camera": {
                   "center": { lat: 48.877276, lng: 2.329978, altitude: 48 },
                   "range": 178,
                   "tilt": 57.48,
                   "heading": -17,
               },
               "point": { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
               "pin": {
                   "background": 'white',
                   "glyph": new URL(base + '/images/fr.svg'),
                   "scale": 1.0,
               },
           },
           {
               "name": "Google UK",
               "camera": {
                   "center": { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
                   "range": 500,
                   "tilt": 56.21672368296945,
                   "heading": -31.15763027564165,
               },
               "point": { lat: 51.5332, lng: -0.1260, altitude: 75 },
               "pin": {
                   "background": 'white',
                   "glyph": new URL(base + '/images/gb.svg'),
                   "scale": 1.0,
               },
           }]

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           officeLocations.forEach(office => {
               const marker = new Marker3DInteractiveElement({
                   position: office.point,
                   label: office.name,
                   altitudeMode: 'ABSOLUTE',
                   extruded: true,
               });

               marker.addEventListener('gmp-click', (event) => {
                   map3D.flyCameraTo({
                       endCamera: office.camera,
                       durationMillis: 5000,
                   });

                   map3D.addEventListener('gmp-animationend', () => {
                       map3D.flyCameraAround({
                           camera: office.camera,
                           durationMillis: 5000,
                           rounds: 1
                       });

                       map3D.addEventListener('gmp-animationend', () => {
                           map3D.flyCameraTo({
                               endCamera: europeCamera,
                               durationMillis: 5000,
                           });
                       }, { once: true });

                   }, { once: true });

                   event.stopPropagation();
               });

               const markerPin = new PinElement(office.pin);
               marker.append(markerPin);

               map3D.append(marker);
           });
           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

11. Más lugares

Si bien la aplicación ahora tiene todas las funciones necesarias, el mapa en 3D aún se ve un poco escaso, por lo que ahora agregarás algunas ubicaciones más para que se vea un poco más ocupado. Con el uso de un array, es sencillo seguir poblando ubicaciones nuevas, con sus propios marcadores únicos. El último paso es seguir agregando marcadores hasta que se muestre la siguiente vista.

“Una imagen que muestra todas las oficinas.

Agregar más marcadores

Google tiene varias oficinas en muchos países de Europa, así que agreguemos algunas de ellas al mapa. Solo es cuestión de actualizar el array. Esto puede provenir de un servicio web o entregarse desde un archivo estático en algún lugar. En nuestro caso, para simplificar, se mantendrá en la misma página.

Puedes agregar tantos marcadores como quieras, que la página detectará y, luego, agregará automáticamente a la vista. Recuerda obtener las marcas correctas y almacenarlas en el directorio de imágenes (o en cualquier lugar que sea conveniente).

const officeLocations = [
   {
       name: "Google France",
       camera: {
           center: { lat: 48.877276, lng: 2.329978, altitude: 48 },
           range: 178,
           tilt: 57.48,
           heading: -17,
       },
       point: { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/fr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google UK",
       camera: {
           center: { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 51.5332, lng: -0.1260, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gb.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Belgium",
       camera: {
           center: { lat: 50.83930408436509, lng: 4.38052394507952, altitude: 64.38932203802196},
           range: 466.62899893119175,
           tilt: 43.61569474716178,
           heading: 51.805907046332074,
       },
       point: { lat: 50.8392653, lng: 4.3808751, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/be.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Czechia",
       camera: {
           center: {
               lat: 50.07004093853976,
               lng: 14.402871475443956,
               altitude: 223.39574818495532
           },
           range: 522.0365799222782,
           tilt: 62.39511972890614,
           heading: -39.150149539328304,
       },
       point: { lat: 50.0703122, lng: 14.402668199999999, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/cz.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Denmark",
       details: "2, Sankt Petri Passage 5, 1165 København",
       camera: {
           center: {
               lat: 55.680359539635866,
               lng: 12.570460204526002,
               altitude: 30.447654757346044
           },
           range: 334.8786935049066,
           tilt: 55.38819319004654,
           heading: 149.63867461295067,
       },
       point: { lat: 55.6804504, lng: 12.570279099999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/dk.svg'),
           scale: 1.0,
       },
   },
   ,
   {
       name: "Google Greece",
       camera: {
           center: {
               lat: 38.038634694028055,
               lng: 23.802924946201266,
               altitude: 196.45884670344995
           },
           range: 343.57226336076565,
           tilt: 54.97375927639567,
           heading: -33.26775344055724,
       },
       point: { lat: 38.038619, lng: 23.8031622, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Germany",
       camera: {
           center: {
               lat: 53.55397683312404,
               lng: 9.986350507286808,
               altitude: 44.83610870143956
           },
           range: 375.3474077822466,
           tilt: 71.35078443829818,
           heading: -160.76930098951416,
       },
       point: { lat: 53.5540227, lng: 9.9863, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/de.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Ireland",
       camera: {
           center: { lat: 53.339816899999995, lng: -6.2362644, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 53.339816899999995, lng: -6.2362644, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ie.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Italy",
       camera: {
           center: {
               lat: 45.486361346538224,
               lng: 9.18995496294455,
               altitude: 138.55834058400072
           },
           range: 694.9398023590038,
           tilt: 57.822470255679114,
           heading: 84.10194883488619,
       },
       point: { lat: 45.4863064, lng: 9.1894762, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/it.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Lithuania",
       camera: {
           center: {
               lat: 54.698040606567965,
               lng: 25.30965338542576,
               altitude: 111.80276944294413
           },
           range: 412.5808304977545,
           tilt: 43.50793332082195,
           heading: -29.181098269421028,
       },
       point: { lat: 54.6981204, lng: 25.3098617, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/at.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Netherlands",
       camera: {
           center: {
               lat: 52.33773837150874,
               lng: 4.871754560171063,
               altitude: 53.68063996154723
           },
           range: 473.1982259177312,
           tilt: 56.216523350388634,
           heading: 71.78252318033718,
       },
       point: { lat: 52.337801, lng: 4.872065999999999, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/nl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Norway",
       camera: {
           center: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/no.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Poland",
       camera: {
           center: { lat: 52.22844380000001, lng: 20.9851819, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 52.22844380000001, lng: 20.9851819, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Portugal",
       camera: {
           center: {
               lat: 38.7240122810727,
               lng: -9.150628263172639,
               altitude: 55.299662291551044
           },
           range: 337.7474313328639,
           tilt: 56.79772652682846,
           heading: 176.0722118222208,
       },
       point: { lat: 38.723915999999996, lng: -9.150629, altitude: 35 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pt.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Romania",
       camera: {
           center: {
               lat: 44.43076650172983,
               lng: 26.109700164718586,
               altitude: 125.57895810814505
           },
           range: 364.25249956711923,
           tilt: 38.517539223834326,
           heading: -38.81294924429363,
       },
       point: { lat: 44.4309897, lng: 26.1095719, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ro.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Spain",
       camera: {
           center: {
               lat: 40.450078762608875,
               lng: -3.6930085080020856,
               altitude: 753.6446342341894
           },
           range: 845.7279793010093,
           tilt: 46.752510050599746,
           heading: 4.718779524265234,
       },
       point: { lat: 40.450294199999995, lng: -3.6927915, altitude: 175 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/es.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Sweden",
       camera: {
           center: {
               lat: 59.33313751316038,
               lng: 18.054618219238293,
               altitude: 16.728213706832868
           },
           range: 377.5210725830039,
           tilt: 63.59478230626709,
           heading: 98.53138488367703,
       },
       point: { lat: 59.3332093, lng: 18.0536386, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/se.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Switzerland",
       camera: {
           center: {
               lat: 47.365411056285275,
               lng: 8.525063594405356,
               altitude: 419.2348376754488
           },
           range: 166.74918737631742,
           tilt: 59.31431457129067,
           heading: -32.620415961949206,
       },
       point: { lat: 47.365452, lng: 8.5249253, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ch.svg'),
           scale: 1.0,
       },
   }
]

Una vez que hayas hecho esto, debería aparecer una página completa como la que se muestra en la imagen, que permite que un usuario haga clic en cualquier ubicación y vuele hacia ella y alrededor de ella, y luego salga.

“Animación de un vuelo entre oficinas y alrededor de ellas en España y Suecia.

¡Felicitaciones! Completaste el codelab. En la siguiente sección, terminaremos y buscaremos otras funciones nuevas para probar.

Solución de la sección

En este paso, la página completa se proporciona como solución para verificar tu implementación. (si copias, asegúrate de usar tu propia clave de API).

Además, no olvides que deberás obtener el archivo de SVG de la bandera (o los archivos PNG que elijas) y guardarlo en un directorio que tu página pueda encontrar (aquí se almacena en la carpeta de imágenes).

<!DOCTYPE html>
<html>

<head>
   <title>Step 9 - More Places!</title>
   <style>
       body {
           height: 100vh;
           margin: 0;
       }
   </style>
</head>

<body>
   <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: "<INSERT API KEY>",
           v: "alpha",
           // Use the 'v' parameter to indicate the version to use (weekly, beta, alpha, etc.).
           // Add other bootstrap parameters as needed, using camel case.
       });
   </script>
   <script>
       let map3D = null;

       const base = document.location.href.substr(0, document.location.href.lastIndexOf("/"));

       const europeCamera = {
           center: { lat: 46.717, lng: 7.075, altitude: 2175.130 },
           range: 5814650,
           tilt: 33,
           heading: 4.36,
       };

const officeLocations = [
   {
       name: "Google France",
       details: "8 Rue de Londres, 75009 Paris, France",
       camera: {
           center: { lat: 48.877276, lng: 2.329978, altitude: 48 },
           range: 178,
           tilt: 57.48,
           heading: -17,
       },
       point: { lat: 48.8775183, lng: 2.3299791, altitude: 60 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/fr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google UK",
       details: "6 Pancras Square, London N1C 4AG, UK",
       camera: {
           center: { lat: 51.5332, lng: -0.1260, altitude: 38.8 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 51.5332, lng: -0.1260, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gb.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Belgium",
       details: "Chau. d'Etterbeek 180, 1040 Brussel",
       camera: {
           center: { lat: 50.83930408436509, lng: 4.38052394507952, altitude: 64.38932203802196},
           range: 466.62899893119175,
           tilt: 43.61569474716178,
           heading: 51.805907046332074,
       },
       point: { lat: 50.8392653, lng: 4.3808751, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/be.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Czechia",
       details: "Stroupežnického 3191/17, 150 00 Praha 5-Smíchov",
       camera: {
           center: {
               lat: 50.07004093853976,
               lng: 14.402871475443956,
               altitude: 223.39574818495532
           },
           range: 522.0365799222782,
           tilt: 62.39511972890614,
           heading: -39.150149539328304,
       },
       point: { lat: 50.0703122, lng: 14.402668199999999, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/cz.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Denmark",
       details: "2, Sankt Petri Passage 5, 1165 København",
       camera: {
           center: {
               lat: 55.680359539635866,
               lng: 12.570460204526002,
               altitude: 30.447654757346044
           },
           range: 334.8786935049066,
           tilt: 55.38819319004654,
           heading: 149.63867461295067,
       },
       point: { lat: 55.6804504, lng: 12.570279099999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/dk.svg'),
           scale: 1.0,
       },
   },
   ,
   {
       name: "Google Greece",
       details: "Fragkokklisias 6, Athina 151 25",
       camera: {
           center: {
               lat: 38.038634694028055,
               lng: 23.802924946201266,
               altitude: 196.45884670344995
           },
           range: 343.57226336076565,
           tilt: 54.97375927639567,
           heading: -33.26775344055724,
       },
       point: { lat: 38.038619, lng: 23.8031622, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/gr.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Germany",
       details: "ABC-Straße 19, 20354 Hamburg",
       camera: {
           center: {
               lat: 53.55397683312404,
               lng: 9.986350507286808,
               altitude: 44.83610870143956
           },
           range: 375.3474077822466,
           tilt: 71.35078443829818,
           heading: -160.76930098951416,
       },
       point: { lat: 53.5540227, lng: 9.9863, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/de.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Ireland",
       details: "Gordon House, 4 Barrow St, Grand Canal Dock, Dublin 4, D04 V4X7",
       camera: {
           center: { lat: 53.339816899999995, lng: -6.2362644, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 53.339816899999995, lng: -6.2362644, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ie.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Italy",
       details: "Isola Building C, Via Federico Confalonieri, 4, 20124 Milano",
       camera: {
           center: {
               lat: 45.486361346538224,
               lng: 9.18995496294455,
               altitude: 138.55834058400072
           },
           range: 694.9398023590038,
           tilt: 57.822470255679114,
           heading: 84.10194883488619,
       },
       point: { lat: 45.4863064, lng: 9.1894762, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/it.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Lithuania",
       details: "Vilnius Tech Park, Antakalnis st. 17, 2nd building, LT-10312, Vilnius",
       camera: {
           center: {
               lat: 54.698040606567965,
               lng: 25.30965338542576,
               altitude: 111.80276944294413
           },
           range: 412.5808304977545,
           tilt: 43.50793332082195,
           heading: -29.181098269421028,
       },
       point: { lat: 54.6981204, lng: 25.3098617, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/at.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Netherlands",
       details: "Claude Debussylaan 34, 1082 MD Amsterdam",
       camera: {
           center: {
               lat: 52.33773837150874,
               lng: 4.871754560171063,
               altitude: 53.68063996154723
           },
           range: 473.1982259177312,
           tilt: 56.216523350388634,
           heading: 71.78252318033718,
       },
       point: { lat: 52.337801, lng: 4.872065999999999, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/nl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Norway",
       details: "Bryggegata 6, 0250 Oslo",
       camera: {
           center: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 59.90991209999999, lng: 10.726020799999999, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/no.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Poland",
       details: "Rondo Daszynskiego 2, 00-843 Warsaw",
       camera: {
           center: { lat: 52.22844380000001, lng: 20.9851819, altitude: 38.777415761228006 },
           range: 500,
           tilt: 56.21672368296945,
           heading: -31.15763027564165,
       },
       point: { lat: 52.22844380000001, lng: 20.9851819, altitude: 25 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pl.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Portugal",
       details: "R. Duque de Palmela 37 Piso 4, 1250-097 Lisboa",
       camera: {
           center: {
               lat: 38.7240122810727,
               lng: -9.150628263172639,
               altitude: 55.299662291551044
           },
           range: 337.7474313328639,
           tilt: 56.79772652682846,
           heading: 176.0722118222208,
       },
       point: { lat: 38.723915999999996, lng: -9.150629, altitude: 35 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/pt.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Romania",
       details: "Bulevardul Corneliu Coposu 6-8, București 030167",
       camera: {
           center: {
               lat: 44.43076650172983,
               lng: 26.109700164718586,
               altitude: 125.57895810814505
           },
           range: 364.25249956711923,
           tilt: 38.517539223834326,
           heading: -38.81294924429363,
       },
       point: { lat: 44.4309897, lng: 26.1095719, altitude: 75 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ro.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Spain",
       details: "Torre Picasso, Pl. Pablo Ruiz Picasso, 1, Tetuán, 28020 Madrid",
       camera: {
           center: {
               lat: 40.450078762608875,
               lng: -3.6930085080020856,
               altitude: 753.6446342341894
           },
           range: 845.7279793010093,
           tilt: 46.752510050599746,
           heading: 4.718779524265234,
       },
       point: { lat: 40.450294199999995, lng: -3.6927915, altitude: 175 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/es.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Sweden",
       details: "Kungsbron 2, 111 22 Stockholm",
       camera: {
           center: {
               lat: 59.33313751316038,
               lng: 18.054618219238293,
               altitude: 16.728213706832868
           },
           range: 377.5210725830039,
           tilt: 63.59478230626709,
           heading: 98.53138488367703,
       },
       point: { lat: 59.3332093, lng: 18.0536386, altitude: 50 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/se.svg'),
           scale: 1.0,
       },
   },
   {
       name: "Google Switzerland",
       details: "Brandschenkestrasse 110, 8002 Zürich",
       camera: {
           center: {
               lat: 47.365411056285275,
               lng: 8.525063594405356,
               altitude: 419.2348376754488
           },
           range: 166.74918737631742,
           tilt: 59.31431457129067,
           heading: -32.620415961949206,
       },
       point: { lat: 47.365452, lng: 8.5249253, altitude: 100 },
       pin: {
           background: 'white',
           glyph: new URL(base + '/images/ch.svg'),
           scale: 1.0,
       },
   }
]

       async function init() {
           const { Map3DElement, MapMode, Marker3DInteractiveElement } = await google.maps.importLibrary("maps3d");
           const { PinElement } = await google.maps.importLibrary('marker');

           map3D = new Map3DElement({
               ...europeCamera,
               mode: MapMode.SATELLITE,
           });

           officeLocations.forEach(office => {
               const marker = new Marker3DInteractiveElement({
                   position: office.point,
                   label: office.name,
                   altitudeMode: 'RELATIVE_TO_GROUND',
                   extruded: true,
               });

               marker.addEventListener('gmp-click', (event) => {
                   map3D.flyCameraTo({
                       endCamera: office.camera,
                       durationMillis: 2000,
                   });

                   map3D.addEventListener('gmp-animationend', () => {
                       map3D.flyCameraAround({
                           camera: office.camera,
                           durationMillis: 2000,
                           rounds: 1
                       });

                       map3D.addEventListener('gmp-animationend', () => {
                           map3D.flyCameraTo({
                               endCamera: europeCamera,
                               durationMillis: 2000,
                           });
                       }, { once: true });

                   }, { once: true });

                   event.stopPropagation();
               });

               const markerPin = new PinElement(office.pin);
               marker.append(markerPin);

               map3D.append(marker);
           });
           document.body.append(map3D);
       }
       init();
   </script>
</body>

</html>

12. Próximos pasos

En este codelab, viste los conceptos básicos de lo que puedes hacer con la API de Maps JavaScript en 3D. Ahora, intenta agregar algunas de estas funciones al mapa:

  • Agrega una lista desplegable para permitir la selección de una oficina.
  • Usa algunas de las otras opciones de diseño de marcadores para mostrar más detalles.
  • Consulta las otras bibliotecas disponibles para la API de Maps JavaScript, las cuales habilitan funciones adicionales, como Places para mostrar la calificación de cada oficina con su ID de Place.

Si quieres obtener más información sobre cómo trabajar con Google Maps Platform y 3D en la Web, consulta estos vínculos: