العمل مع عارض ثلاثي الأبعاد للمربّعات

المطوّرون في المنطقة الاقتصادية الأوروبية

تتوفّر المربّعات الثلاثية الأبعاد لصور واقعية بتنسيق glTF المتوافق مع معيار OGC، ما يعني أنّه يمكنك استخدام أي عارض متوافق مع مواصفات المربّعات الثلاثية الأبعاد لصور واقعية من OGC لإنشاء تصوّراتك الثلاثية الأبعاد. على سبيل المثال، Cesium هي مكتبة أساسية مفتوحة المصدر لعرض المرئيات الثلاثية الأبعاد.

العمل باستخدام CesiumJS

‫CesiumJS هي مكتبة Javascript مفتوحة المصدر لإنشاء عروض ثلاثية الأبعاد على الويب. لمزيد من المعلومات حول استخدام CesiumJS، يُرجى الاطّلاع على التعرّف على CesiumJS.

عناصر تحكم المستخدم

يحتوي عارض مربّعات CesiumJS على مجموعة عادية من عناصر التحكّم الخاصة بالمستخدم.

الإجراء الوصف
عرض شامل النقر بزر الماوس الأيسر والسحب
العرض بوضع التكبير أو التصغير انقر بزر الماوس الأيمن واسحب، أو مرِّر عجلة الماوس
تدوير العرض ‫Ctrl + النقر بزر الماوس الأيسر/الأيمن مع السحب، أو النقر بزر الماوس الأوسط مع السحب

أفضل الممارسات

هناك عدة طرق يمكنك اتّباعها لتقليل أوقات تحميل CesiumJS 3D. على سبيل المثال:

  • فعِّل الطلبات المتزامنة من خلال إضافة العبارة التالية إلى رمز HTML للعرض:

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

    كلما زادت قيمة REQUEST_COUNT، زادت سرعة تحميل المربّعات. ومع ذلك، عند التحميل في متصفّح Chrome باستخدام REQUEST_COUNT أكبر من 10 مع إيقاف ذاكرة التخزين المؤقت، قد تواجه مشكلة معروفة في Chrome. في معظم حالات الاستخدام، ننصح باستخدام REQUEST_COUNT بقيمة 18 للحصول على الأداء الأمثل.

  • تفعيل تخطّي مستويات التفاصيل لمزيد من المعلومات، يُرجى الاطّلاع على مشكلة Cesium هذه.

تأكَّد من عرض نِسب البيانات بشكل صحيح من خلال تفعيل showCreditsOnScreen: true. لمزيد من المعلومات، يُرجى الاطّلاع على السياسات.

مقاييس العرض

للعثور على معدّل عرض الإطارات، اطّلِع على عدد المرات التي يتم فيها استدعاء طريقة requestAnimationFrame في الثانية.

لمعرفة كيفية احتساب وقت استجابة اللقطة، يمكنك الاطّلاع على فئة PerformanceDisplay.

أمثلة على عارض CesiumJS

يمكنك استخدام أداة عرض CesiumJS مع مربّعات الخرائط الثلاثية الأبعاد في Map Tiles API من خلال تقديم عنوان URL لمجموعة المربّعات الجذرية فقط.

مثال بسيط

يؤدي المثال التالي إلى إعداد أداة عرض CesiumJS، ثم تحميل مجموعة المربّعات الجذرية.

<!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 (الإصدار الجديد) لاسترداد المزيد من المعلومات. يمكنك استخدام تطبيق "الإكمال التلقائي" المصغّر للانتقال إلى إطار العرض في Places. يستخدم هذا المثال واجهة برمجة التطبيقات Places Autocomplete API، التي يتم تفعيلها من خلال اتّباع هذه التعليمات، وواجهة برمجة التطبيقات Maps JavaScript API، التي يتم تفعيلها من خلال اتّباع هذه التعليمات.

<!DOCTYPE html>
<head>
 <meta charset="utf-8" />
 <title>CesiumJS 3D Tiles Places API (New) 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 (الجديدة) وElevation 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>

مدار الكاميرا

في Cesium، يمكنك تدوير الكاميرا حول نقطة اهتمام، مع تجنُّب الاصطدام بالمباني. يمكنك بدلاً من ذلك جعل المباني شفافة عندما تتحرك الكاميرا من خلالها.

أولاً، ثبِّت الكاميرا على نقطة معيّنة، ثم أنشئ مدارًا للكاميرا لعرض مادة العرض. ويمكنك إجراء ذلك باستخدام دالة lookAtTransform في الكاميرا مع متتبِّع الأحداث، كما هو موضّح في عينة التعليمات البرمجية هذه.

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

لمزيد من المعلومات حول التحكّم في الكاميرا، يُرجى الاطّلاع على مقالة التحكّم في الكاميرا.

العمل مع Cesium for Unreal

لاستخدام المكوّن الإضافي Cesium for Unreal مع واجهة 3D Tiles API، اتّبِع الخطوات أدناه.

  1. ثبِّت المكوّن الإضافي Cesium for Unreal.

  2. أنشئ مشروعًا جديدًا في Unreal.

  3. الربط بواجهة Google Photorealistic 3D Tiles API

    1. افتح نافذة Cesium من خلال اختيار Cesium > Cesium من القائمة.

    2. اختَر مجموعة مربّعات ثلاثية الأبعاد فارغة.

    3. في World Outliner، افتح لوحة التفاصيل من خلال اختيار Cesium3DTileset.

    4. غيِّر المصدر من من Cesium Ion إلى من عنوان URL.

    5. اضبط عنوان URL ليكون عنوان URL الخاص بـ Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. فعِّل خيار عرض أسماء المساهمين على الشاشة لعرض معلومات المساهمين بشكل صحيح.
  4. سيؤدي ذلك إلى تحميل العالم. للانتقال إلى أي إحداثيات LatLng، اختَر العنصر CesiumGeoreference في لوحة أداة التخطيط، ثم عدِّل خط العرض/خط الطول/الارتفاع الأصلي في لوحة التفاصيل.

العمل باستخدام Cesium for Unity

لاستخدام مربّعات الصور الواقعية مع Cesium for Unity، اتّبِع الخطوات التالية:

  1. أنشِئ مشروع Unity جديدًا.

  2. أضِف سجلّاً جديدًا محدد النطاق في قسم "إدارة الحِزم" (Package Manager) (من خلال المحرِّر > إعدادات المشروع).

    • الاسم: Cesium

    • عنوان URL‏: https://unity.pkg.cesium.com

    • النطاقات: com.cesium.unity

  3. ثبِّت حزمة Cesium for Unity.

  4. الربط بواجهة Google Photorealistic 3D Tiles API

    1. افتح نافذة Cesium من خلال اختيار Cesium > Cesium من القائمة.

    2. انقر على مجموعة مربّعات ثلاثية الأبعاد فارغة.

    3. في اللوحة الجانبية اليمنى، في خيار Tileset Source (مصدر مجموعة المربّعات) ضمن Source (المصدر)، اختَر From URL (من عنوان URL) (بدلاً من From Cesium Ion (من Cesium Ion)).

    4. اضبط عنوان URL على عنوان URL الخاص بـ Google 3D Tiles.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. فعِّل خيار عرض أسماء المساهمين على الشاشة لعرض معلومات المساهمين بشكل صحيح.
  5. سيؤدي ذلك إلى تحميل العالم. للانتقال إلى أي إحداثيات LatLng، اختَر العنصر CesiumGeoreference في Scene Hierarchy، ثم عدِّل Origin Latitude/Longitude/Height في Inspector.

العمل باستخدام deck.gl

‫deck.gl هو إطار عمل JavaScript مفتوح المصدر يستند إلى WebGL، وهو مخصّص لإنشاء تصورات بيانات عالية الأداء وواسعة النطاق.

تحديد المصدر

تأكَّد من عرض نِسب البيانات بشكل صحيح من خلال استخراج الحقل copyright من ملفات gltf الخاصة بالمربّعات asset، ثم عرضه في العرض المعروض. لمزيد من المعلومات، يُرجى الاطّلاع على عرض نِسب البيانات.

أمثلة على عرض deck.gl

مثال بسيط

يعمل المثال التالي على إعداد أداة عرض deck.gl، ثم تحميل مكان بثلاثة أبعاد. في الرمز البرمجي، احرص على استبدال YOUR_API_KEY بمفتاح واجهة برمجة التطبيقات الفعلي.

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

تعرض إضافة TerrainExtension في deck.gl البيانات الثنائية الأبعاد على سطح ثلاثي الأبعاد. على سبيل المثال، يمكنك تغطية 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>