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

تتوفّر المربّعات الثلاثية الأبعاد ذات الصور الواقعية في تنسيق glTF العادي في OGC مما يعني أنه يمكنك استخدام أي عارض يتوافق مع مواصفات OGC 3D Tiles لإنشاء والتصورات ثلاثية الأبعاد الخاصة بك. على سبيل المثال: السيوم هي مكتبة أساسية مفتوحة المصدر لعرض التصورات ثلاثية الأبعاد.

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

CesiumJS هي مكتبة JavaScript مفتوحة المصدر لعرض التصور ثلاثي الأبعاد على الويب. لمزيد من المعلومات عن استخدام CelsiumJS، راجع تعرَّف على CelsiumJS.

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

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

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

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

هناك العديد من المناهج التي يمكنك اتباعها لخفض تحميل CelsiumJS ثلاثي الأبعاد أوقات. على سبيل المثال:

  • يمكنك تفعيل الطلبات المتزامنة عن طريق إضافة العبارة التالية إلى محتوى HTML للعرض:

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

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

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

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

مقاييس العرض

للعثور على معدل عرض الإطارات، انظر إلى عدد المرات في الثانية التي requestAnimationFrame المريض.

للتعرف على كيفية حساب وقت استجابة الإطار، يمكنك إلقاء نظرة على PerformanceDisplay الصف.

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

ويمكنك استخدام عارض CelsiumJS مع المربعات ثلاثية الأبعاد في Map Tiles API توفير عنوان URL لمجموعة الشرائح الجذر.

مثال بسيط

يقوم المثال التالي بتهيئة عارض CelsiumJS، ثم تحميل الجذر مجموعة الفئات.

<!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 على النحو الموضّح هنا.

دمج واجهة برمجة تطبيقات الأماكن

يمكنك استخدام CelsiumJS مع Places API لاسترداد المزيد من المعلومات. يمكنك استخدام أداة الإكمال التلقائي للانتقال إلى إطار عرض الأماكن. يستخدم هذا المثال واجهة برمجة تطبيقات الإكمال التلقائي للأماكن، التي تم تفعيلها من خلال باتّباع هذه التعليمات وواجهة برمجة تطبيقات JavaScript للخرائط، التي تم تفعيلها بواسطة باتّباع هذه التعليمات.

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

عرض طائرة تصوير دوّارة

يمكنك التحكّم في الكاميرا لتحريكها من خلال مجموعة المربّعات. عند الدمج مع واجهة برمجة تطبيقات الأماكن وواجهة برمجة تطبيقات الارتفاع، فإن هذه الرسوم المتحركة تحاكي طائرة بدون طيار فوق أي نقطة اهتمام.

ينقلك نموذج الرمز هذا إلى المكان الذي حددته في أداة الإكمال التلقائي

<!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 مع أداة معالجة الأحداث، كما هو موضح في عينة التعليمات البرمجية هذه.

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

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

استعِن بأداة السيزيوم لأداء لعبة Unreal

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

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

  2. أنشِئ مشروع Unreal جديد.

  3. الربط بواجهة برمجة تطبيقات الصور الواقعية ثلاثية الأبعاد من Google

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

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

    3. في مخطط العالم، افتح لوحة التفاصيل من خلال اختيار Cesium3DTileset:

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

    5. عيِّن عنوان URL ليكون عنوان URL لشاشات Google الثلاثية الأبعاد.

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

تعاوَن مع Celsium لتحقيق الانسجام

لاستخدام مربّعات الصور الواقعية مع Celsium for Unity، يجب اتّباع الخطوات التالية.

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

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

    • الاسم: السيزيوم

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

    • النطاق(النطاقات): com.ceium.unity

  3. تثبيت حزمة Celsium for Unity.

  4. الربط بواجهة برمجة التطبيقات الثلاثية الأبعاد في "صور Google الواقعية"

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

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

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

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

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

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

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

تحديد المصدر

تأكَّد من عرض عمليات تحديد المصدر للبيانات بشكل صحيح من خلال استخراج 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".

The deck.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>