Utiliser un moteur de rendu de tuiles 3D

Les tuiles 3D photoréalistes sont au format glTF standard OGC, ce qui signifie que vous pouvez utiliser n'importe quel moteur de rendu compatible avec la spécification OGC 3D Tiles pour créer vos visualisations 3D. Par exemple, Cesium est une bibliothèque Open Source fondamentale pour le rendu des visualisations 3D.

Travailler avec CesiumJS

CesiumJS est une bibliothèque JavaScript Open Source pour la visualisation 3D sur le Web. Pour en savoir plus sur l'utilisation de CesiumJS, consultez la page Learn CesiumJS.

Contrôle utilisateur

Le moteur de rendu de tuiles CesiumJS dispose d'un ensemble standard de commandes utilisateur.

Action Description
Vue panoramique Cliquez avec le bouton gauche et faites glisser
Vue Zoom Effectuez un clic droit et faites glisser le curseur, ou faites rouler la molette de la souris.
Faire pivoter la vue Ctrl+clic gauche/droit et faire glisser ou clic au milieu et faire glisser

Bonnes pratiques

Vous pouvez adopter plusieurs approches pour réduire les temps de chargement de CesiumJS 3D. Exemple :

  • Activez les demandes simultanées en ajoutant l'instruction suivante à votre code HTML d'affichage:

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

    Plus la valeur de REQUEST_COUNT est élevée, plus les cartes se chargent vite. Toutefois, lors du chargement dans un navigateur Chrome avec REQUEST_COUNT supérieur à 10 et le cache désactivé, vous pouvez rencontrer un problème Chrome connu. Dans la plupart des cas d'utilisation, nous recommandons une REQUEST_COUNT de 18 pour des performances optimales.

  • Activer l'omission des niveaux de détail Pour en savoir plus, consultez ce problème de Cesium.

Assurez-vous d'afficher correctement les attributions de données en activant showCreditsOnScreen: true. Pour en savoir plus, consultez la section Règles.

Métriques d'affichage

Pour connaître la fréquence d'images, regardez le nombre d'appels de la méthode requestAnimationFrame.

Pour voir comment la latence des frames est calculée, consultez la classe PerformanceDisplay.

Exemples de moteurs de rendu CesiumJS

Vous pouvez utiliser le moteur de rendu CesiumJS avec les tuiles 3D de l'API Map Tiles en fournissant simplement l'URL du jeu de tuiles racine.

Exemple simple

L'exemple suivant initialise le moteur de rendu CesiumJS, puis charge le jeu de tuiles racine.

<!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>

Pour en savoir plus sur requestRenderMode, consultez la section Activer le mode de rendu des requêtes.

La page HTML s'affiche comme indiqué ici.

Intégration de l'API Places

Vous pouvez utiliser CesiumJS avec l'API Places pour récupérer plus d'informations. Vous pouvez utiliser le widget Autocomplete pour accéder à la fenêtre d'affichage de Places. Cet exemple utilise l'API Places Autocomplete, qui peut être activée en suivant ces instructions, et l'API Maps JavaScript, qui est activée en suivant ces instructions.

<!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>

Vue pivotante de drone

Vous pouvez contrôler l'appareil photo pour effectuer une animation dans le jeu de tuiles. Lorsqu'elle est combinée à l'API Places et à l'API Elevation, cette animation simule le survol interactif d'un drone à n'importe quel point d'intérêt.

Cet exemple de code vous permet de parcourir le lieu que vous avez sélectionné dans le widget 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>

Dessiner des polylignes et des libellés

Cet exemple de code montre comment ajouter des polylignes et des libellés à une carte. Vous pouvez ajouter des polylignes à une carte pour afficher des itinéraires en voiture et à pied, pour afficher les limites d'une propriété ou pour calculer la durée des trajets en voiture et à pied. Vous pouvez également obtenir des attributs sans afficher la scène.

Vous pouvez emmener les utilisateurs faire une visite guidée d'un quartier ou leur montrer les propriétés voisines qui sont actuellement en vente, puis ajouter des objets 3D tels que des panneaux d'affichage à la scène.

Vous pouvez résumer un trajet en listant les propriétés que vous avez consultées et en affichant ces détails dans des objets virtuels.

<!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>

Orbite de la caméra

Dans Cesium, vous pouvez faire pivoter la caméra autour d'un point d'intérêt afin d'éviter les collisions avec les bâtiments. Vous pouvez également rendre les bâtiments transparents lorsque la caméra les traverse.

Tout d'abord, verrouillez la caméra sur un point, puis créez une orbite de caméra pour présenter votre ressource. Pour ce faire, utilisez la fonction lookAtTransform de la caméra avec un écouteur d'événements, comme illustré dans cet exemple de code.

// 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);
});

Pour en savoir plus sur le contrôle de la caméra, consultez Contrôler la caméra.

Travailler avec Cesium pour Unreal

Pour utiliser le plug-in Cesium pour Unreal avec l'API 3D Tiles, suivez les étapes ci-dessous.

  1. Installez le plug-in Cesium pour Unreal.

  2. Créez un projet Unreal.

  3. Connectez-vous à l'API Google Photorealistic 3D Tiles.

    1. Ouvrez la fenêtre Cesium en sélectionnant Cesium > Cesium dans le menu.

    2. Sélectionnez Blank 3D Tiles Tileset (Jeu de tuiles 3D vides).

    3. Dans World Outliner, ouvrez le panneau Details (Détails) en sélectionnant ce Cesium3DTileset.

    4. Dans le champ Source, remplacez From Cesium Ion par From URL.

    5. Définissez l'URL en tant qu'URL de Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Pour afficher correctement les attributions, activez l'option Show Credits On Screen (Afficher les crédits à l'écran).
  4. Le monde entier s'en charge. Pour passer à n'importe quelle valeur LatLng, sélectionnez l'élément CesiumGeoreference dans le panneau Idéateur, puis modifiez la valeur Latitude/Longitude/Hauteur d'origine dans le panneau Détails.

Travailler avec Cesium pour Unity

Pour utiliser des tuiles photoréalistes avec Cesium pour Unity, suivez les étapes ci-dessous.

  1. Créez un projet Unity.

  2. Ajoutez un registre délimité dans la section "Package Manager" (Gestionnaire de packages) via Editor > Project Settings (Éditeur > Paramètres du projet).

    • Nom: Cesium

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

    • Champ(s) d'application : com.cesium.unity

  3. Installez le package Cesium pour Unity.

  4. Connectez-vous à l'API Google Photorealistic 3D Tiles.

    1. Ouvrez la fenêtre Cesium en sélectionnant Cesium > Cesium dans le menu.

    2. Cliquez sur Blank 3D Tiles Tiles (Ensemble de tuiles 3D vides).

    3. Dans le panneau de gauche, dans l'option Tileset Source sous Source, sélectionnez From URL (À partir de l'URL) (au lieu de From Cesium Ion).

    4. Définissez l'URL sur celle de Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Pour afficher correctement les attributions, activez l'option Show Credits On Screen (Afficher les crédits à l'écran).
  5. Le monde entier s'en charge. Pour passer à n'importe quel LatLng, sélectionnez l'élément CesiumGeoreference dans la Scene Hierarchy (Hiérarchie de la scène), puis modifiez la latitude/longitude et la hauteur d'origine dans l'outil d'inspection.

Utiliser deck.gl

deck.gl, fourni par WebGL, est un framework JavaScript Open Source permettant de visualiser des données à grande échelle et hautes performances.

Attribution

Assurez-vous d'afficher correctement les attributions de données en extrayant le champ copyright des tuiles gltf asset, puis en l'affichant dans la vue affichée. Pour en savoir plus, consultez la section Afficher les attributions de données.

Exemples de moteur de rendu deck.gl

Exemple simple

L'exemple suivant initialise le moteur de rendu deck.gl, puis charge un lieu en 3D. Dans votre code, veillez à remplacer YOUR_API_KEY par votre clé API.

<!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>

Affichez des calques 2D sur des tuiles photoréalistes Google 3D

L'extension TerrainExtension de deck.gl affiche des données en 2D sur une surface 3D. Par exemple, vous pouvez projeter le GeoJSON d'une empreinte de bâtiment sur la géométrie des tuiles 3D photoréalistes.

Dans l'exemple suivant, un calque de bâtiments est visualisé avec les polygones adaptés à la surface des tuiles 3D photoréalistes.

<!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>