Cómo trabajar con un procesador de tarjetas en 3D

Los mosaicos fotorrealistas en 3D están en Formato glTF estándar de OGC, lo que significa que puedes usar cualquier procesador compatible con la especificación de OGC 3D Tiles para compilar tus visualizaciones en 3D. Por ejemplo: Cesio es una biblioteca de código abierto fundamental para renderizar visualizaciones en 3D.

Trabaja con CesiumJS

CesiumJS es una biblioteca de código abierto de JavaScript para la visualización 3D en la Web. Para obtener más información sobre el uso de CesiumJS, consulta Aprende CesiumJS.

Controles de usuario

El renderizador de tarjetas CesiumJS tiene un conjunto estándar de controles de usuario.

Acción Descripción
Vista de desplazamiento lateral Haz clic con el botón izquierdo y arrastrar
Acercar la vista Haz clic con el botón derecho y arrastrar o desplazar la rueda del mouse
Girar la vista Ctrl + clic con el botón izquierdo o derecho y arrastrar o hacer clic con el botón central y arrastrar

Prácticas recomendadas

Existen varios enfoques que puedes adoptar para disminuir la carga de CesiumJS 3D. veces. Por ejemplo:

  • Para habilitar solicitudes simultáneas, agrega la siguiente declaración al código HTML de procesamiento:

    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = <REQUEST_COUNT>
    

    Cuanto más alto sea el REQUEST_COUNT, más rápido será se cargan las tarjetas. Sin embargo, cuando se realiza la carga en el navegador Chrome con REQUEST_COUNT superior a 10 y la caché inhabilitada, es posible que te encuentres con un Error de Chrome. En la mayoría de los casos de uso, recomendamos un REQUEST_COUNT de 18 para una rendimiento.

  • Habilitar la omisión de niveles de detalle Para obtener más información, consulta este Error de cesio.

Asegúrate de mostrar las atribuciones de datos correctamente habilitando showCreditsOnScreen: true Para obtener más información, consulta Políticas.

Métricas de renderización

Para conocer la velocidad de fotogramas, mira cuántas veces por segundo requestAnimationFrame método.

Para ver cómo se calcula la latencia de fotogramas, observa la PerformanceDisplay clase.

Ejemplos del renderizador CesiumJS

Puedes usar el renderizador CesiumJS con 3D Tiles de la API de Map Tiles simplemente que suministra la URL del conjunto de mosaicos raíz.

Ejemplo simple

En el siguiente ejemplo, se inicializa el procesador CesiumJS y, luego, se carga la raíz conjunto de mosaicos.

<!DOCTYPE html>
<head>
  <meta charset="utf-8">
  <title>CesiumJS 3D Tiles Simple Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>

    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer.
    const viewer = new Cesium.Viewer('cesiumContainer', {
      imageryProvider: false,
      baseLayerPicker: false,
      geocoder: false,
      globe: false,
      // https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/#enabling-request-render-mode
      requestRenderMode: true,
    });

    // Add 3D Tiles tileset.
    const tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
      url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
      // This property is needed to appropriately display attributions
      // as required.
      showCreditsOnScreen: true,
    }));
  </script>
</body>

Para obtener más información sobre requestRenderMode, consulta Habilita el modo de renderización de solicitudes.

La página HTML se procesa como se muestra aquí.

Integración de la API de Places

Puedes usar CesiumJS con el API de Places para recuperar más información. Puedes usar el widget de Autocomplete para volar a la viewport de Places. En este ejemplo, se usa la API de Place Autocomplete, habilitado por siguiendo estas instrucciones, y la API de Maps JavaScript, habilitada por siguiendo estas instrucciones.

<!DOCTYPE html>
<head>
 <meta charset="utf-8" />
 <title>CesiumJS 3D Tiles Places API Integration Demo</title>
 <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
 <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
 <label for="pacViewPlace">Go to a place: </label>
 <input
   type="text"
   id="pacViewPlace"
   name="pacViewPlace"
   placeholder="Enter a location..."
   style="width: 300px"
 />
 <div id="cesiumContainer"></div>
 <script>
   // Enable simultaneous requests.
   Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

   // Create the viewer.
   const viewer = new Cesium.Viewer("cesiumContainer", {
     imageryProvider: false,
     baseLayerPicker: false,
     requestRenderMode: true,
     geocoder: false,
     globe: false,
   });

   // Add 3D Tiles tileset.
   const tileset = viewer.scene.primitives.add(
     new Cesium.Cesium3DTileset({
       url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
       // This property is required to display attributions as required.
       showCreditsOnScreen: true,
     })
   );

   const zoomToViewport = (viewport) => {
     viewer.entities.add({
       polyline: {
         positions: Cesium.Cartesian3.fromDegreesArray([
           viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
           viewport.getSouthWest().lng(), viewport.getNorthEast().lat(),
           viewport.getSouthWest().lng(), viewport.getSouthWest().lat(),
           viewport.getNorthEast().lng(), viewport.getSouthWest().lat(),
           viewport.getNorthEast().lng(), viewport.getNorthEast().lat(),
         ]),
         width: 10,
         clampToGround: true,
         material: Cesium.Color.RED,
       },
     });
     viewer.flyTo(viewer.entities);
   };

   function initAutocomplete() {
     const autocomplete = new google.maps.places.Autocomplete(
       document.getElementById("pacViewPlace"),
       {
         fields: [
           "geometry",
           "name",
         ],
       }
     );
     autocomplete.addListener("place_changed", () => {
       viewer.entities.removeAll();
       const place = autocomplete.getPlace();
       if (!place.geometry || !place.geometry.viewport) {
         window.alert("No viewport for input: " + place.name);
         return;
       }
       zoomToViewport(place.geometry.viewport);
     });
   }
 </script>
 <script
   async=""
   src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"
 ></script>
</body>

Vista giratoria de dron

Puedes controlar la cámara para animar el conjunto de mosaicos. Cuando se combina con la API de Places y la API de Elevation, esta animación simula una forma el sobrevuelo de drones de cualquier lugar de interés.

Esta muestra de código te lleva por el lugar que seleccionaste en la Widget de Autocomplete.

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>CesiumJS 3D Tiles Rotating Drone View Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
</head>
<body>
  <label for="pacViewPlace">Go to a place: </label>
  <input type="text" id="pacViewPlace" name="pacViewPlace" placeholder="Enter a location..." style="width: 300px" />
  <div id="cesiumContainer"></div>
  <script>
    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer and remove unneeded options.
    const viewer = new Cesium.Viewer("cesiumContainer", {
      imageryProvider: false,
      baseLayerPicker: false,
      homeButton: false,
      fullscreenButton: false,
      navigationHelpButton: false,
      vrButton: false,
      sceneModePicker: false,
      geocoder: false,
      globe: false,
      infobox: false,
      selectionIndicator: false,
      timeline: false,
      projectionPicker: false,
      clockViewModel: null,
      animation: false,
      requestRenderMode: true,
    });

    // Add 3D Tile set.
    const tileset = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset({
        url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",
        // This property is required to display attributions.
        showCreditsOnScreen: true,
      })
    );

    // Point the camera at a location and elevation, at a viewport-appropriate distance.
    function pointCameraAt(location, viewport, elevation) {
      const distance = Cesium.Cartesian3.distance(
        Cesium.Cartesian3.fromDegrees(
          viewport.getSouthWest().lng(), viewport.getSouthWest().lat(), elevation),
        Cesium.Cartesian3.fromDegrees(
          viewport.getNorthEast().lng(), viewport.getNorthEast().lat(), elevation)
      ) / 2;
      const target = new Cesium.Cartesian3.fromDegrees(location.lng(), location.lat(), elevation);
      const pitch = -Math.PI / 4;
      const heading = 0;
      viewer.camera.lookAt(target, new Cesium.HeadingPitchRange(heading, pitch, distance));
    }

    // Rotate the camera around a location and elevation, at a viewport-appropriate distance.
    let unsubscribe = null;
    function rotateCameraAround(location, viewport, elevation) {
      if(unsubscribe) unsubscribe();
      pointCameraAt(location, viewport, elevation);
      unsubscribe = viewer.clock.onTick.addEventListener(() => {
        viewer.camera.rotate(Cesium.Cartesian3.UNIT_Z);
      });
    }

    function initAutocomplete() {
      const autocomplete = new google.maps.places.Autocomplete(
        document.getElementById("pacViewPlace"), {
          fields: [
            "geometry",
            "name",
          ],
        }
      );
      
      autocomplete.addListener("place_changed", async () => {
        const place = autocomplete.getPlace();
        
        if (!(place.geometry && place.geometry.viewport && place.geometry.location)) {
          window.alert(`Insufficient geometry data for place: ${place.name}`);
          return;
        }
        // Get place elevation using the ElevationService.
        const elevatorService = new google.maps.ElevationService();
        const elevationResponse =  await elevatorService.getElevationForLocations({
          locations: [place.geometry.location],
        });

        if(!(elevationResponse.results && elevationResponse.results.length)){
          window.alert(`Insufficient elevation data for place: ${place.name}`);
          return;
        }
        const elevation = elevationResponse.results[0].elevation || 10;

        rotateCameraAround(
          place.geometry.location,
          place.geometry.viewport,
          elevation
        );
      });
    }
  </script>
  <script async src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"></script>
</body>

Dibuja polilíneas y etiquetas

En esta muestra de código, se indica cómo agregar polilíneas y etiquetas a un mapa. Puedes agrega polilíneas a un mapa para mostrar instrucciones sobre cómo llegar en automóvil o a pie, o para mostrar límites de propiedades o para calcular la duración de los viajes en automóvil y a pie. También puedes obtener atributos sin renderizar realmente la escena.

Puedes llevar a los usuarios a un recorrido seleccionado por un vecindario, o bien propiedades vecinas que están en venta y, luego, podrías agregar imágenes 3D objetos, como vallas publicitarias, a la escena.

Podrías resumir un viaje, enumerar las propiedades que viste, mostrar estos detalles en objetos virtuales.

<!DOCTYPE html>
<head>
  <meta charset="utf-8" />
  <title>CesiumJS 3D Tiles Polyline and Label Demo</title>
  <script src="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Cesium.js"></script>
  <link 
    href="https://ajax.googleapis.com/ajax/libs/cesiumjs/1.105/Build/Cesium/Widgets/widgets.css"
    rel="stylesheet"
  />
</head>
<body>
  <div id="cesiumContainer"></div>
  <script>
    // Enable simultaneous requests.
    Cesium.RequestScheduler.requestsByServer["tile.googleapis.com:443"] = 18;

    // Create the viewer.
    const viewer = new Cesium.Viewer("cesiumContainer", {
      imageryProvider: false,
      baseLayerPicker: false,
      requestRenderMode: true,
      geocoder: false,
      globe: false,
    });

    // Add 3D Tiles tileset.
    const tileset = viewer.scene.primitives.add(
      new Cesium.Cesium3DTileset({
        url: "https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY",

        // This property is required to display attributions as required.
        showCreditsOnScreen: true,
      })
    );

    // Draws a circle at the position, and a line from the previous position.
    const drawPointAndLine = (position, prevPosition) => {
      viewer.entities.removeAll();
      if (prevPosition) {
        viewer.entities.add({
          polyline: {
            positions: [prevPosition, position],
            width: 3,
            material: Cesium.Color.WHITE,
            clampToGround: true,
            classificationType: Cesium.ClassificationType.CESIUM_3D_TILE,
          },
        });
      }
      viewer.entities.add({
        position: position,
        ellipsoid: {
          radii: new Cesium.Cartesian3(1, 1, 1),
          material: Cesium.Color.RED,
        },
      });
    };

    // Compute, draw, and display the position's height relative to the previous position.
    var prevPosition;
    const processHeights = (newPosition) => {
      drawPointAndLine(newPosition, prevPosition);

      const newHeight = Cesium.Cartographic.fromCartesian(newPosition).height;
      let labelText = "Current altitude (meters above sea level):\n\t" + newHeight;
      if (prevPosition) {
        const prevHeight =
          Cesium.Cartographic.fromCartesian(prevPosition).height;
        labelText += "\nHeight from previous point (meters):\n\t" + Math.abs(newHeight - prevHeight);
      }
      viewer.entities.add({
        position: newPosition,
        label: {
          text: labelText,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          pixelOffset: new Cesium.Cartesian2(0, -10),
          showBackground: true,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        }
      });

      prevPosition = newPosition;
    };

    const handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas);
    handler.setInputAction(function (event) {
      const earthPosition = viewer.scene.pickPosition(event.position);
      if (Cesium.defined(earthPosition)) {
        processHeights(earthPosition);
      }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
  </script>
</body>

Órbita de la cámara

En Cesio, puedes orbitar la cámara alrededor de un lugar de interés, lo que evita colisiones con edificios. También puedes hacer que los edificios sean transparentes cuando la cámara se mueve a través de ellos.

Primero, fija la cámara en un punto; luego, puedes crear una órbita de cámara para muestra tu recurso. Para ello, usa el botón de la cámara lookAtTransform con un objeto de escucha de eventos, como se demuestra en esta muestra de código.

// Lock the camera onto a point.
const center = Cesium.Cartesian3.fromRadians(
  2.4213211833389243,
  0.6171926869414084,
  3626.0426275055174
);

const transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);

viewer.scene.camera.lookAtTransform(
  transform,
  new Cesium.HeadingPitchRange(0, -Math.PI / 8, 2900)
);

// Orbit around this point.
viewer.clock.onTick.addEventListener(function (clock) {
  viewer.scene.camera.rotateRight(0.005);
});

Para obtener más información sobre cómo controlar la cámara, consulta Cómo controlar la cámara

Trabaja con Cesium for Unreal

Para usar el complemento Cesium for Unreal con la API de 3D Tiles, sigue estos pasos: a continuación.

  1. Instala el complemento Cesium for Unreal.

  2. Crea un nuevo proyecto de Unreal.

  3. Conéctate a la API de Photorealistic 3D Tiles de Google.

    1. Abre la ventana Cesio. Para ello, selecciona Cesio > Cesio en el menú.

    2. Selecciona Conjunto de mosaicos de mosaicos en 3D en blanco.

    3. En World Outliner, selecciona esta opción para abrir el panel Details. Cesium3DTileset.

    4. Cambia la Fuente de From Cesium Ion a From URL.

    5. Establece que la URL sea la de Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Habilita la opción Mostrar créditos en pantalla para mostrar las atribuciones correctamente.
  4. Esto carga todo el mundo. Para moverte a cualquier LatLng, selecciona CesiumGeoreference en el panel Outliner y, luego, edita el Origin Latitude/Longitude/Height en el panel Details.

Trabaja con Cesium para la unidad

Para usar tarjetas fotorrealistas con Cesio para Unity, sigue los pasos que se indican a continuación.

  1. Crea un nuevo proyecto de Unity.

  2. Agrega un nuevo registro con alcance en la sección Administrador de paquetes (a través de Editor > Configuración del proyecto).

    • Nombre: Cesium

    • URL: https://unity.pkg.cesium.com

    • Alcances: com.cesium.unity

  3. Instala el paquete de Cesium for Unity.

  4. Conéctate a la API de Photorealistic 3D Tiles de Google.

    1. Abre la ventana Cesio. Para ello, selecciona Cesio > Cesio en el menú.

    2. Haz clic en Conjunto de mosaicos de mosaicos en 3D en blanco.

    3. En el panel lateral izquierdo, ve a la opción Tileset Source en Source. selecciona Desde URL (en lugar de Desde iones de cesio).

    4. Establece la URL de la URL de mosaicos 3D de Google.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Habilita la opción Mostrar créditos en pantalla para mostrar las atribuciones correctamente.
  5. Esto carga todo el mundo. Para moverte a cualquier LatLng, selecciona CesiumGeoreference en Scene Hierarchy y, luego, edita el Latitud, longitud y altura de origen en el Inspector.

Cómo trabajar con deck.gl

deck.gl, con la tecnología de WebGL, es un framework de JavaScript de código abierto para visualizaciones de datos a gran escala.

Atribución

Asegúrate de mostrar las atribuciones de datos de forma correcta. Para ello, extrae copyright de mosaicos gltf asset y, luego, mostrarlo en la vista renderizada. Para más información, consulta Muestra las atribuciones de datos.

Ejemplos del renderizador de deck.gl

Ejemplo simple

En el siguiente ejemplo, se inicializa el renderizador deck.gl y, luego, se carga un lugar en 3D. En el código, asegúrate de reemplazar YOUR_API_KEY por el clave de API real.

<!DOCTYPE html>
<html>
 <head>
   <title>deck.gl Photorealistic 3D Tiles example</title>
   <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
   <style>
     body { margin: 0; padding: 0;}
     #map { position: absolute; top: 0;bottom: 0;width: 100%;}
     #credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
        text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
   </style>
 </head>

 <body>
   <div id="map"></div>
   <div id="credits"></div>
   <script>
     const GOOGLE_API_KEY = YOUR_API_KEY;
     const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
     const creditsElement = document.getElementById('credits');
     new deck.DeckGL({
       container: 'map',
       initialViewState: {
         latitude: 50.0890,
         longitude: 14.4196,
         zoom: 16,
         bearing: 90,
         pitch: 60,
         height: 200
       },
       controller: {minZoom: 8},
       layers: [
         new deck.Tile3DLayer({
           id: 'google-3d-tiles',
           data: TILESET_URL,
           loadOptions: {
            fetch: {
              headers: {
                'X-GOOG-API-KEY': GOOGLE_API_KEY
              }
            }
          },
           onTilesetLoad: tileset3d => {
             tileset3d.options.onTraversalComplete = selectedTiles => {
               const credits = new Set();
               selectedTiles.forEach(tile => {
                 const {copyright} = tile.content.gltf.asset;
                 copyright.split(';').forEach(credits.add, credits);
                 creditsElement.innerHTML = [...credits].join('; ');
               });
               return selectedTiles;
             }
           }
         })
       ]
     });
   </script>
 </body>
</html>

Visualiza capas 2D sobre los mosaicos fotorrealistas en 3D de Google

El archivo deck.gl TerrainExtension renderiza datos en 2D en una superficie 3D. Por ejemplo, puedes colocar las GeoJSON de la huella de un edificio sobre la geometría de mosaicos fotorrealistas en 3D.

En el siguiente ejemplo, se visualiza una capa de edificios con los polígonos adaptado a la superficie de los mosaicos fotorrealistas en 3D.

<!DOCTYPE html>
<html>
 <head>
   <title>Google 3D tiles example</title>
   <script src="https://unpkg.com/deck.gl@latest/dist.min.js"></script>
   <style>
     body { margin: 0; padding: 0;}
     #map { position: absolute; top: 0;bottom: 0;width: 100%;}
     #credits { position: absolute; bottom: 0; right: 0; padding: 2px; font-size: 15px; color: white;
        text-shadow: -1px 0 black, 0 1px black, 1px 0 black, 0 -1px black;}
   </style>
 </head>

 <body>
   <div id="map"></div>
   <div id="credits"></div>
   <script>
     const GOOGLE_API_KEY = YOUR_API_KEY;
     const TILESET_URL = `https://tile.googleapis.com/v1/3dtiles/root.json`;
     const BUILDINGS_URL = 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson'
     const creditsElement = document.getElementById('credits');
     const deckgl = new deck.DeckGL({
       container: 'map',
       initialViewState: {
         latitude: 50.0890,
         longitude: 14.4196,
         zoom: 16,
         bearing: 90,
         pitch: 60,
         height: 200
       },
       controller: true,
       layers: [
         new deck.Tile3DLayer({
           id: 'google-3d-tiles',
           data: TILESET_URL,
           loadOptions: {
            fetch: {
              headers: {
                'X-GOOG-API-KEY': GOOGLE_API_KEY
              }
            }
          },
          onTilesetLoad: tileset3d => {
             tileset3d.options.onTraversalComplete = selectedTiles => {
               const credits = new Set();
               selectedTiles.forEach(tile => {
                 const {copyright} = tile.content.gltf.asset;
                 copyright.split(';').forEach(credits.add, credits);
                 creditsElement.innerHTML = [...credits].join('; ');
               });
               return selectedTiles;
             }
           },
           operation: 'terrain+draw'
         }),
         new deck.GeoJsonLayer({
           id: 'buildings',
           // This dataset is created by CARTO, using other Open Datasets available. More info at: https://3dtiles.carto.com/#about.
           data: 'https://raw.githubusercontent.com/visgl/deck.gl-data/master/examples/google-3d-tiles/buildings.geojson',
           stroked: false,
           filled: true,
           getFillColor: ({properties}) => {
             const {tpp} = properties;
             // quantiles break
             if (tpp < 0.6249)
               return [254, 246, 181]
             else if (tpp < 0.6780)
               return [255, 194, 133]
             else if (tpp < 0.8594)
               return [250, 138, 118]
             return [225, 83, 131]
           },
           opacity: 0.2,
           extensions: [new deck._TerrainExtension()]
         })
       ]
     });
   </script>
 </body>
</html>