3D 타일 렌더기 사용

포토리얼리스틱 3D 타일은 OGC 표준 glTF 형식 OGC 3D 타일 사양을 지원하는 모든 렌더기를 사용하여 3D 시각화입니다. 예를 들어 세슘 3D 시각화 렌더링을 위한 기본적인 오픈소스 라이브러리입니다.

CesiumJS 사용

CesiumJS는 웹에서 3D 시각화를 위한 오픈소스 JavaScript 라이브러리입니다. CesiumJS 사용에 대한 자세한 내용은 다음을 참조하세요. CesiumJS 알아보기

사용자 제어

CesiumJS 타일 렌더기에는 표준 사용자 컨트롤 세트가 있습니다.

작업 설명
화면 이동 왼쪽 클릭 및 드래그
확대/축소 뷰 마우스 오른쪽 버튼으로 클릭 및 마우스 휠을 드래그하거나 스크롤하세요.
보기 회전 Ctrl + 왼쪽/오른쪽 클릭 및 드래그하거나 가운데를 클릭하고 드래그

권장사항

CesiumJS 3D 로드를 줄이기 위해 취할 수 있는 몇 가지 접근 방식이 있습니다. 표시됩니다. 예를 들면 다음과 같습니다.

  • 렌더링 HTML에 다음 문을 추가하여 동시 요청을 사용 설정합니다.

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

    REQUEST_COUNT 값이 높을수록 타일이 로드됩니다. 하지만 REQUEST_COUNT로 Chrome 브라우저에서 로드할 때는 캐시가 사용 중지되어 있다면 알려진 문제가 발생할 수 Chrome 문제 대부분의 사용 사례에서 최적의 성능을 위해 REQUEST_COUNT를 18로 설정하는 것이 좋습니다. 확인할 수 있습니다

  • 세부정보 수준 건너뛰기를 사용 설정합니다. 자세한 내용은 Cesium 문제.

사용 설정하여 데이터 저작자 표시가 올바르게 표시되는지 확인하세요. showCreditsOnScreen: true 자세한 내용은 정책.

렌더링 측정항목

프레임 속도를 알아보려면 초당 몇 번을 보면 requestAnimationFrame 메서드가 호출됩니다.

프레임 지연 시간이 계산되는 방식을 확인하려면 PerformanceDisplay 클래스에 대해 자세히 알아보세요.

CesiumJS 렌더기 예시

다음과 같은 간단한 방법으로 Map Tiles API의 3D 타일과 함께 CesiumJS 렌더기를 사용할 수 있습니다. 루트 타일셋 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 Autocomplete API를 사용합니다. 이는 이 안내에 따라 Maps JavaScript API를 사용하면 이 안내에 따라 변경할 수 있습니다.

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

다중선 및 라벨 그리기

이 코드 샘플은 지도에 다중선과 라벨을 추가하는 방법을 보여줍니다. 다음과 같은 작업을 할 수 있습니다. 지도에 다중선을 추가하여 운전경로와 도보경로를 표시하거나 부동산 경계를 정의하거나 운전 및 도보 시간을 계산할 수 있습니다. 그 외에 속성을 가져올 수 없습니다.

사용자에게 주변 지역을 선별해서 둘러보도록 하거나 3D 이미지를 추가하여 현재 매매 중인 빌보드 등의 물체를 장면에 배치할 수 있습니다.

이동을 요약하여 조회했던 숙박 시설을 나열하고, 이러한 세부 정보를 가상 객체에 저장할 수 있습니다

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

카메라 제어에 관한 자세한 내용은 다음을 참고하세요. 카메라 제어

Unreal용 Cesium 사용

3D Tiles API에서 Cesium for Unreal 플러그인을 사용하려면 다음 단계를 따르세요. 참조하세요.

  1. Unreal용 Cesium 플러그인을 설치합니다.

  2. 새 Unreal 프로젝트를 만듭니다.

  3. Google 포토리얼리스틱 3D 타일 API에 연결합니다.

    1. Cesium >을 선택하여 Cesium 창을 엽니다. Cesium을 선택합니다.

    2. 빈 3D 타일 타일 세트를 선택합니다.

    3. 월드 아웃라이너에서 다음을 선택하여 세부정보 패널을 엽니다. Cesium3DTileset에서 사용할 수 있습니다.

    4. 소스세슘 이온에서에서 URL로로 변경합니다.

    5. URL을 Google 3D Tiles URL로 설정합니다.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. 저작자를 올바르게 표시하려면 화면에 크레딧 표시를 사용 설정합니다.
  4. 이는 세계를 로드해 나갑니다. LatLng로 이동하려면 아웃라이너 패널에서 CesiumGeoreference 항목을 선택한 다음 세부정보 패널의 출발지 위도/경도/높이.

Unity용 Cesium 사용

Unity용 Cesium으로 실사 타일을 사용하려면 다음 단계를 따르세요.

  1. 새 Unity 프로젝트를 만듭니다.

  2. 패키지 관리자 섹션에 편집기 > 프로젝트 설정을 통해 새로운 범위 지정 레지스트리를 추가합니다.

    • 이름: Cesium

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

    • 범위: com.cesium.unity

  3. Unity용 Cesium 패키지를 설치합니다.

  4. Google Photorealistic 3D Tiles API에 연결합니다.

    1. Cesium >을 선택하여 Cesium 창을 엽니다. Cesium을 선택합니다.

    2. 빈 3D 타일 타일 세트를 클릭합니다.

    3. 왼쪽 패널의 소스 아래 타일 세트 소스 옵션에서, Cesium Ion 대신 From URL을 선택합니다.

    4. URL을 Google 3D Tiles URL로 설정합니다.

    https://tile.googleapis.com/v1/3dtiles/root.json?key=YOUR_API_KEY
    
    1. 저작자를 올바르게 표시하려면 화면에 크레딧 표시를 사용 설정합니다.
  5. 이는 세계를 로드해 나갑니다. LatLng로 이동하려면 Scene Hierarchy에서 CesiumGeoreference 항목을 가져온 다음 Inspector(검사기)의 출발지 위도/경도/높이

deck.gl 사용

deck.gl WebGL 기반의 고성능 오픈소스 자바스크립트 프레임워크 대규모 데이터 시각화를 제공합니다

기여 분석

copyright를 추출하여 데이터 속성이 올바르게 표시되는지 확인합니다. 타일 gltf asset에서 가져온 후 렌더링된 뷰에 표시합니다. 대상 자세한 내용은 데이터 저작자 표시 표시.

deck.gl 렌더기 예시

간단한 예시

다음 예에서는 deck.gl 렌더기를 초기화한 후 장소를 로드합니다. 3D로 구현됩니다. 코드에서 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 포토리얼리스틱 3D 타일 위에 2D 레이어 시각화

deck.gl TerrainExtension 3D 표면에 2D 데이터를 렌더링합니다. 예를 들어, 실사 3D 타일 도형 위에 표시된 건물 접지면의 GeoJSON입니다.

다음 예에서는 건물 레이어가 다각형으로 시각화됩니다. 실사 3D 타일 표면에 맞게 조정됩니다.

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