עבודה עם רינדור אריחים בתלת-ממד

משבצות תלת-ממדיות פוטוריאליסטיות פורמט glTF סטנדרטי של OGC, כלומר, אפשר להשתמש בכל כלי לעיבוד שתומך במפרט של משבצות תלת-ממד מסוג OGC כדי ליצור בתצוגות התלת-ממדיות שלכם. לדוגמה, צסיום היא ספריית בסיסית של קוד פתוח לעיבוד תצוגות חזותיות בתלת ממד.

עבודה עם SesiumJS

SesiumJS היא ספריית JavaScript בקוד פתוח להמחשת תלת ממד באינטרנט. למידע נוסף על השימוש ב-CesiumJS, ראו מידע על SesiumJS.

פקדי משתמש

למעבד המשבצות CisiumJS יש קבוצה סטנדרטית של בקרות משתמש.

פעולה תיאור
הזזה לחיצה שמאלית & לגרור
שינוי מרחק התצוגה לחיצה ימנית & לגרור או לגלול את גלגל העכבר
סיבוב התצוגה Ctrl + לחיצה על שמאל/ימין ו- גרירה, או לחיצה אמצעית & לגרור

שיטות מומלצות

יש כמה גישות להפחתת הטעינה של תלת-ממד של CisiumJS פעמים. לדוגמה:

  • כדי להפעיל בקשות בו-זמנית, צריך להוסיף את ההצהרה הבאה ל-HTML של העיבוד:

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

    ככל שהמדד REQUEST_COUNT גבוה יותר, כך המשבצות נטענות. אבל כשטוענים את הקובץ בדפדפן Chrome עם REQUEST_COUNT גדול מ-10 והמטמון מושבת, ייתכן שתיתקלו בעיה ב-Chrome. ברוב תרחישי השימוש, אנחנו ממליצים על REQUEST_COUNT של 18. או של ביצועים.

  • הפעלת דילוג על רמות פירוט. מידע נוסף זמין במאמר הבא: בעיה בצסיום.

כדי לוודא ששיוך הנתונים מוצג כראוי, צריך להפעיל showCreditsOnScreen: true מידע נוסף זמין במאמר הבא: מדיניות.

מדדי רינדור

כדי למצוא את קצב הפריימים, בודקים כמה פעמים בשנייה requestAnimationFrame נקראת.

כדי לראות איך מחושב זמן האחזור של רינדור, אפשר לעיין PerformanceDisplay בכיתה.

דוגמאות לרינדור CisiumJS

אפשר להשתמש במעבד של CesiumJS עם משבצות תלת-ממד של Map Tiles API פשוט לספק את כתובת ה-URL של קבוצת המשבצות הבסיסית.

דוגמה פשוטה

הדוגמה הבאה מפעילה את רינדור CisiumJS ולאחר מכן טוענת את הרמה הבסיסית (root). קבוצת משבצות.

<!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 זמין במאמר הפעלת מצב עיבוד בקשה.

דף ה-HTML מעובד כמו שמוצג כאן.

שילוב של Places API

אפשר להשתמש ב-CesiumJS עם Places API כדי לאחזר מידע נוסף. תוכלו להשתמש בווידג'ט של ההשלמה האוטומטית כדי לעבור אזור התצוגה של 'מקומות'. בדוגמה הזו נעשה שימוש בממשק ה-API של ההשלמה האוטומטית של Places, שמופעל על ידי לפי ההוראות האלה, וגם את JavaScript API של מפות Google, שמופעל על ידי לפי ההוראות האלה.

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

תצוגת רחפן מסתובב

ניתן לשלוט במצלמה כדי להוסיף אנימציה באמצעות ערכת המשבצות. בשילוב עם של Places API ושל Layerion API, האנימציה הזו מדמה פעולה אינטראקטיבית באמצעות רחפן בכל נקודת עניין.

דוגמת הקוד הזו תנווט אותך סביב המקום שבחרת ווידג'ט להשלמה אוטומטית.

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

ציירו קווים פוליגוניים ותוויות

דוגמת הקוד הזו מדגימה איך להוסיף קווים פוליגוניים ותוויות למפה. אפשר להוסיף קווים פוליגוניים למפה כדי להציג מסלולי נסיעה או הליכה, או כדי להציג אותם תחומי הנכסים, או כדי לחשב את משך הנסיעה והנהיגה. אפשר גם מקבלים מאפיינים מבלי לעבד את הסצנה בפועל.

תוכל לקחת משתמשים לסיור מיוחד בשכונה, או להראות לנכסים סמוכים שנמצאים כרגע במבצע, ואז אפשר להוסיף תלת ממד כמו שלטי חוצות לסצנה.

אפשר לסכם נסיעה, לפרט את המאפיינים שצפיתם בהם, להציג הפרטים האלה באובייקטים וירטואליים.

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

מסלול המצלמה

בצסיום, ניתן להקיף את המצלמה מסביב לנקודת עניין, וכך להימנע התנגשויות עם בניינים. לחלופין, אפשר להפוך את המבנים לשקיפות כשהמצלמה נעה דרכם.

קודם כול, נועלים את המצלמה על נקודה מסוימת, ואז אפשר ליצור מסלול מצלמה כדי להציג את הנכס שלכם. אפשר לעשות זאת בעזרת lookAtTransform מותאמת ל-event listener, כפי שמוצג בדוגמת הקוד הזו.

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

למידע נוסף על שליטה במצלמה: שליטה במצלמה

עבודה עם Cisium for Unreal

כדי להשתמש ב-Ccesium for Unreal Plugin עם 3D Tiles API, צריך לפעול לפי השלבים. שלמטה.

  1. מתקינים את הפלאגין Cesium for Unreal.

  2. יוצרים פרויקט חדש ב-Unreal.

  3. התחברות לממשק ה-API של משבצות תלת-ממדיות ב-Google Photoreal.

    1. כדי לפתוח את חלון צסיום, בוחרים באפשרות צסיום > צסיום בתפריט.

    2. בוחרים באפשרות ערכת משבצות ריקה של משבצות תלת-ממדיות.

    3. ב-World Outliner, פותחים את החלונית Details על ידי בחירה באפשרות הזו. Cesium3DTileset.

    4. משנים את המקור מ-From Cisium Ion ל-מכתובת אתר.

    5. הגדר את כתובת האתר ככתובת האתר של אריחי Google 3D.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. מפעילים את האפשרות הצגת קרדיטים במסך כדי להציג ייחוס כראוי.
  4. כל העולם טעון. כדי להעביר לנקודת LatLng, בוחרים את לפריט CesiumGeoreference בחלונית Outliner, ואז לערוך את קו הרוחב/קו האורך/גובה של נקודת המוצא בחלונית פרטים.

עבודה עם Cisium ל-Unity

כדי להשתמש באריחים פוטוריאליסטיים עם צסיום ל-Unity, צריך לבצע את השלבים הבאים.

  1. יצירת פרויקט Unity חדש.

  2. מוסיפים Scoped Registry חדש בקטע Package Manager (דרך Editor > Project Settings)

    • שם: צסיום

    • כתובת URL: https://unity.pkg.cesium.com

    • היקפים: com.cesium.unity

  3. התקנה של חבילת Cesium ל-Unity.

  4. התחברות לממשק ה-API של משבצות תלת-ממדיות ב-Google Photoreal.

    1. כדי לפתוח את חלון צסיום, בוחרים באפשרות צסיום > צסיום בתפריט.

    2. לוחצים על קבוצת משבצות של משבצות תלת-ממדיות ריקות.

    3. בחלונית שבצד שמאל, באפשרות Tileset Source שבקטע Source, בוחרים באפשרות מכתובת אתר (במקום באפשרות From Cisium Ion).

    4. מגדירים את כתובת ה-URL ככתובת האתר של משבצות תלת-ממד של Google.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. מפעילים את האפשרות הצגת קרדיטים במסך כדי להציג ייחוס כראוי.
  5. כל העולם טעון. כדי להעביר לכל סוג של LatLng, בוחרים את פריט CesiumGeoreference ב-Scene Hierarchy, ואז עורכים את קו הרוחב/קו האורך/גובה המוצא במפקח.

עבודה עם deck.gl

deck.gl, מופעלת על ידי WebGL, מסגרת JavaScript בקוד פתוח שמספקת ביצועים גבוהים, תצוגות חזותיות של נתונים בקנה מידה גדול.

שיוך (Attribution)

כדי לוודא שייחוסי הנתונים מוצגים כראוי, מחלצים את השדה copyright ממשבצות gltf asset, ואז יוצג בתצוגה המעובדת. עבור מידע נוסף: שיוך של נתונים לרשת המדיה

דוגמאות לכלי לרינדור deck.gl

דוגמה פשוטה

הדוגמה הבאה מאתחלת את רינדור deck.gl, ולאחר מכן טוענת מקום בתלת-ממד. בקוד שלך, חשוב להחליף את YOUR_API_KEY ב את מפתח ה-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>

הצגה חזותית של שכבות דו-ממדיות על גבי משבצות תלת-ממדיות של Google פוטוריאליסטיות

מרפסת הדק.gl TerrainExtension מציג נתונים דו-ממדיים אחרים על משטח תלת-ממדי. לדוגמה, אפשר להציג את GeoJSON של טביעת הרגל של בניין מעל הגיאומטריה הפוטוריאליסטית של אריחים תלת-ממדיים.

בדוגמה הבאה, מוצגת שכבה של בניינים עם הפוליגונים. הותאם לפני השטח הפוטוריאליסטי של משבצות תלת-ממדיות.

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