3D Karo Oluşturucu ile çalışma

Avrupa Ekonomik Alanı (AEA) geliştiricileri

Gerçekçi Fotoğraf 3B Parçalar, OGC standardı glTF biçimindedir. Bu nedenle, 3B görselleştirmelerinizi oluşturmak için OGC 3B Parçalar spesifikasyonunu destekleyen herhangi bir oluşturucuyu kullanabilirsiniz. Örneğin, Cesium, 3D görselleştirmeleri oluşturmak için kullanılan temel bir açık kaynak kitaplıktır.

CesiumJS ile çalışma

CesiumJS, web'de 3D görselleştirme için açık kaynaklı bir JavaScript kitaplığıdır. CesiumJS'yi kullanma hakkında daha fazla bilgi için Learn CesiumJS başlıklı makaleyi inceleyin.

Kullanıcı denetimleri

CesiumJS karo oluşturucuda standart bir kullanıcı kontrolü grubu bulunur.

İşlem Açıklama
Görünümü kaydırma Sol tıklama ve sürükleme
Yakınlaştırma görünümü Sağ tıklayıp sürükleyin veya fare tekerleğini kaydırın.
Görünümü döndürme Ctrl + sol/sağ tıklama ve sürükleme veya orta tıklama ve sürükleme

En iyi uygulamalar

CesiumJS 3D yükleme sürelerini azaltmak için uygulayabileceğiniz çeşitli yaklaşımlar vardır. Örneğin:

  • Aşağıdaki ifadeyi oluşturma HTML'nize ekleyerek eşzamanlı istekleri etkinleştirin:

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

    REQUEST_COUNT değeri ne kadar yüksek olursa kutular o kadar hızlı yüklenir. Ancak REQUEST_COUNT10'dan büyük bir değerle ve önbellek devre dışı bırakılmış şekilde Chrome tarayıcıda yükleme yaparken bilinen bir Chrome sorunuyla karşılaşabilirsiniz. Çoğu kullanım alanında optimum performans için REQUEST_COUNT değerinin 18 olmasını öneririz.

  • Ayrıntı düzeylerini atlamayı etkinleştirin. Daha fazla bilgi için bu Cesium sorununa bakın.

showCreditsOnScreen: true seçeneğini etkinleştirerek veri ilişkilendirmelerini düzgün şekilde gösterdiğinizden emin olun. Daha fazla bilgi için Politikalar başlıklı makaleyi inceleyin.

Oluşturma metrikleri

Kare hızını bulmak için requestAnimationFrame yönteminin saniyede kaç kez çağrıldığına bakın.

Kare gecikmesinin nasıl hesaplandığını görmek için PerformanceDisplay sınıfına göz atın.

CesiumJS oluşturucu örnekleri

Yalnızca kök döşeme grubu URL'sini sağlayarak CesiumJS oluşturucuyu Map Tiles API'nin 3D Tiles'ı ile kullanabilirsiniz.

Basit örnek

Aşağıdaki örnekte CesiumJS oluşturucu başlatılır ve ardından kök döşeme grubu yüklenir.

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

requestRenderMode hakkında bilgi için İstek oluşturma modunu etkinleştirme başlıklı makaleyi inceleyin.

HTML sayfası burada gösterildiği gibi oluşturulur.

Places API entegrasyonu

Daha fazla bilgi almak için CesiumJS'yi Places API ile birlikte kullanabilirsiniz. Yerlerin görünüm alanına gitmek için otomatik tamamlama widget'ını kullanabilirsiniz. Bu örnekte, bu talimatları uygulayarak etkinleştirilen Places Autocomplete API'si ve bu talimatları uygulayarak etkinleştirilen Maps JavaScript API'si kullanılır.

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

Dönen drone görünümü

Kamerayı kontrol ederek döşeme setinde animasyon oluşturabilirsiniz. Bu animasyon, Places API ve Elevation API ile birlikte kullanıldığında herhangi bir önemli yerin etkileşimli drone ile üzerinden geçişini simüle eder.

Bu kod örneği, otomatik tamamlama widget'ında seçtiğiniz yerde gezinmenizi sağlar.

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

Çoklu çizgiler ve etiketler çizme

Bu kod örneğinde, haritaya çoklu çizgilerin ve etiketlerin nasıl ekleneceği gösterilmektedir. Araba ve yürüyüş yol tariflerini göstermek, mülk sınırlarını göstermek veya araba ve yürüyüş sürelerini hesaplamak için haritaya çoklu çizgiler ekleyebilirsiniz. Ayrıca, sahneyi gerçekten oluşturmadan da özellikleri alabilirsiniz.

Kullanıcıları bir mahallede özel bir tura çıkarabilir veya şu anda satışta olan komşu mülkleri gösterebilir, ardından sahneye bilboard gibi 3D nesneler ekleyebilirsiniz.

Görüntülediğiniz tesisleri listeleyerek bir geziyi özetleyebilir, bu ayrıntıları sanal nesnelerde gösterebilirsiniz.

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

Kamera yörüngesi

Cesium'da kamerayı ilgi çekici bir noktanın etrafında döndürebilir, binalarla çarpışmayı önleyebilirsiniz. Alternatif olarak, kamera binaların içinden geçerken binaları şeffaf hale getirebilirsiniz.

Önce kamerayı bir noktaya kilitleyin, ardından öğenizi sergilemek için kamera yörüngesi oluşturabilirsiniz. Bunu, bu kod örneğinde gösterildiği gibi, kameranın lookAtTransform işlevini bir etkinlik dinleyicisiyle kullanarak yapabilirsiniz.

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

Kamerayı kontrol etme hakkında daha fazla bilgi için Kamerayı kontrol etme başlıklı makaleyi inceleyin.

Unreal için Cesium ile çalışma

3D Tiles API ile Cesium for Unreal eklentisini kullanmak için aşağıdaki adımları uygulayın.

  1. Cesium for Unreal eklentisini yükleyin.

  2. Yeni bir Unreal projesi oluşturun.

  3. Google Photorealistic 3D Tiles API'ye bağlanın.

    1. Menüden Cesium > Cesium'u seçerek Cesium penceresini açın.

    2. Boş 3B Parçalar Parça Seti'ni seçin.

    3. World Outliner'da Details panelini açmak için bu Cesium3DTileset'i seçin.

    4. KaynakCesium Ion'dan URL'den olarak değiştirin.

    5. URL'yi Google 3D Tiles URL'si olarak ayarlayın.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Atıfların düzgün şekilde gösterilmesi için Ekran Üzerinde Katkı Yapanları Göster'i etkinleştirin.
  4. Bu işlemle dünya yüklenir. Herhangi bir LatLng'ye gitmek için Anahat panelinde CesiumGeoreference öğesini seçin ve ardından Ayrıntılar panelinde Başlangıç Enlemi/Boylamı/Yüksekliği'ni düzenleyin.

Unity için Cesium ile çalışma

Cesium for Unity ile fotogerçekçi döşemeleri kullanmak için aşağıdaki adımları uygulayın.

  1. Yeni bir Unity projesi oluşturun.

  2. Paket Yöneticisi bölümüne yeni bir Kapsamlı Kayıt Defteri ekleyin (Editor > Project Settings aracılığıyla).

    • Ad: Cesium

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

    • Kapsamlar: com.cesium.unity

  3. Cesium for Unity paketini yükleyin.

  4. Google Photorealistic 3D Tiles API'sine bağlanın.

    1. Menüden Cesium > Cesium'u seçerek Cesium penceresini açın.

    2. Boş 3D Tiles Tileset'i tıklayın.

    3. Sol taraftaki panelde, Kaynak bölümündeki Döşeme Kümesi Kaynağı seçeneğinde URL'den'i (Cesium Ion'dan yerine) seçin.

    4. URL'yi Google 3D Tiles URL'sine ayarlayın.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. Atıfların düzgün şekilde gösterilmesi için Ekran Üzerinde Katkı Yapanları Göster'i etkinleştirin.
  5. Bu işlemle dünya yüklenir. LatLng'ye gitmek için Scene Hierarchy'de (Sahne Hiyerarşisi) CesiumGeoreference öğesini seçin ve ardından Inspector'da (İnceleyici) Origin Latitude/Longitude/Height'ı (Başlangıç Enlemi/Boylamı/Yüksekliği) düzenleyin.

deck.gl ile çalışma

WebGL tarafından desteklenen deck.gl, yüksek performanslı ve büyük ölçekli veri görselleştirmeleri için açık kaynaklı bir JavaScript çerçevesidir.

İlişkilendirme

copyright alanını tiles gltf asset dosyasından çıkarıp oluşturulan görünümde göstererek veri ilişkilendirmelerini düzgün şekilde görüntülediğinizden emin olun. Daha fazla bilgi için Görüntüleme verisi ilişkilendirmeleri konusuna bakın.

deck.gl oluşturucu örnekleri

Basit örnek

Aşağıdaki örnekte deck.gl oluşturucu başlatılır ve ardından 3D olarak bir yer yüklenir. Kodunuzda YOUR_API_KEY kısmını gerçek API anahtarınızla değiştirdiğinizden emin olun.

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

Google'ın gerçekçi fotoğraf 3B parçalarının üzerinde 2B katmanları görselleştirme

deck.gl TerrainExtension aksi takdirde 2 boyutlu olan verileri 3 boyutlu bir yüzeyde oluşturur. Örneğin, bir bina yerleşiminin GeoJSON'unu gerçekçi fotoğraf 3B parçalar geometrisinin üzerine yerleştirebilirsiniz.

Aşağıdaki örnekte, binalardan oluşan bir katman, fotogerçekçi 3D döşeme yüzeyine uyarlanmış poligonlarla görselleştirilmiştir.

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