コンテンツにスキップ

3D Tiles with the sun, the atmosphere and the terrain

Fitting OGC 3D Tiles (PLATEAU Sapporo) to the terrain with vertical_offset, and changing the sun and the atmosphere lighting mode.

display_3dtiles_with_environment.html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>3D Tiles with the sun, the atmosphere and the terrain</title>
  <meta property="og:description" content="Fitting OGC 3D Tiles (PLATEAU Sapporo) to the terrain with vertical_offset, and changing the sun and the atmosphere lighting mode." />
  <script src="./v0.10.7/mapray.min.js"></script>
  <script src="./v0.10.7/maprayui.min.js"></script>
  <link rel="stylesheet" href="./v0.10.7/mapray.css">
  <style>
    body {margin: 0; padding: 0;}
    html, body, div#mapray-container { height: 100%; }
    .map-overlay {
        font: 12px/20px "Robot", sans-serif;
        position: absolute;
        width: 300px;
        top: 0;
        left: 0;
        padding: 10px;
    }
    .map-overlay .map-overlay-inner {
        background-color: #fff;
        color: #000;
        box-shadow: 0 1px 2px rgba(0,0,0,0.1);
        border-radius: 3px;
        padding: 10px;
        margin-bottom: 10px;
    }
    .map-overlay-inner fieldset {
        border: none;
        padding: 0;
        margin: 0 0 10px;
    }
    .map-overlay-inner fieldset:last-child {
        margin: 0;
    }
    .map-overlay-inner label {
        display: block;
        font-weight: bold;
        margin: 0 0 5px;
    }
    .map-overlay-inner input[type=range] {
        width: 100%;
    }
  </style>
</head>
<body>
<div id="mapray-container"></div>
<div class="map-overlay top">
    <div class="map-overlay-inner">
        <fieldset>
            <label>Solar time: <span id="time_value"></span></label>
            <input id="time_slider" type="range" min="6" max="18" step="0.25" value="10">
        </fieldset>
        <fieldset>
            <label>Atmosphere lighting mode</label>
            <select id="mode_select">
                <option value="INHERIT" selected>INHERIT (follow the atmosphere)</option>
                <option value="PHYSICAL">PHYSICAL</option>
                <option value="SIMPLE">SIMPLE</option>
                <option value="OFF">OFF (independent of the time)</option>
            </select>
        </fieldset>
        <fieldset>
            <label>Vertical offset: <span id="offset_value"></span></label>
            <input id="offset_slider" type="range" min="-60" max="20" step="1" value="-32">
            The geoid height around Sapporo is about 32 m.
        </fieldset>
        <fieldset>
            <label><input id="visible_check" type="checkbox" checked> Show the 3D Tiles</label>
        </fieldset>
    </div>
</div>
</body>
</html>
<script>
  // Sapporo TV Tower
  const CENTER = { longitude: 141.3564, latitude: 43.0611 };

  const cloudApi = new mapray.cloud.CloudApiV2({
        tokenType: mapray.cloud.CloudApi.TokenType.ACCESS_TOKEN,
        token: "<YOUR_MAPRAY_ACCESS_TOKEN>"
    });
    // The StandardUIView in the ui package includes mouse-based camera manipulation.
    const uiviewer = new maprayui.StandardUIViewer( "mapray-container", {
        dem_provider: new mapray.StandardDemProvider( cloudApi.getDefaultDemAsResource() ),
        atmosphere: new mapray.Atmosphere(),
        sun_visualizer: new mapray.SunVisualizer( 32 )
    } );
  // South-west of Odori Park in Sapporo JP
  uiviewer.setCameraPosition({
    longitude: 141.3480,
    latitude: 43.0560,
    height: 350
  });
  uiviewer.setLookAtPosition({
    longitude: CENTER.longitude,
    latitude: CENTER.latitude,
    height: 30
  });
  // PLATEAU Sapporo-shi Chuo-ku buildings, LOD2 with textures (OGC 3D Tiles 1.0: b3dm content)
  const provider = new mapray.StandardTiles3DProvider({
    url: "https://assets.cms.plateau.reearth.io/assets/16/06e56d-a2d4-4fe4-ac91-b2762b96e033/01100_sapporo-shi_city_2020_citygml_7_op_bldg_3dtiles_01101_chuo-ku_lod2/tileset.json"
  }, {
    // PLATEAU tiles carry no copyright string, so credit the dataset as the PLATEAU terms of use require (Public Data License 1.0, compatible with CC BY 4.0).
    attributions: [ "出典:「3D都市モデル(Project PLATEAU)札幌市(2020年度)」(国土交通省)" ]
  });
  const scene = uiviewer.viewer.b3d_collection.createTiles3DScene(provider, {
    show_attributions: true,
    // PLATEAU heights are ellipsoidal while the terrain is above the geoid, so the buildings are lowered by the geoid height.
    vertical_offset: -32
  });

  // Sun direction at an equinox (declination 0), from the solar time at the center of the view.
  function sunDirection( solar_hour ) {
      const hour_angle = ( solar_hour - 12 ) * 15 * Math.PI / 180;
      const latitude = CENTER.latitude * Math.PI / 180;
      // East, north and up at the center
      const east  = -Math.sin( hour_angle );
      const north = -Math.cos( hour_angle ) * Math.sin( latitude );
      const up    =  Math.cos( hour_angle ) * Math.cos( latitude );
      // MLOCS of a GeoPoint has its x axis to the east, y to the north and z up.
      const mlocs_to_gocs = new mapray.GeoPoint( CENTER.longitude, CENTER.latitude, 0 ).getMlocsToGocsMatrix( mapray.GeoMath.createMatrix() );
      const m = mlocs_to_gocs;
      return [ m[0] * east + m[4] * north + m[8]  * up,
               m[1] * east + m[5] * north + m[9]  * up,
               m[2] * east + m[6] * north + m[10] * up ];
  }

  const time_slider = document.getElementById( 'time_slider' );
  const offset_slider = document.getElementById( 'offset_slider' );

  function applyTime() {
      const hour = Number( time_slider.value );
      uiviewer.viewer.sun.setSunDirection( sunDirection( hour ) );
      const minutes = Math.round( ( hour % 1 ) * 60 );
      document.getElementById( 'time_value' ).textContent = Math.floor( hour ) + ":" + String( minutes ).padStart( 2, "0" );
  }

  function applyOffset() {
      scene.setVerticalOffset( Number( offset_slider.value ) );
      document.getElementById( 'offset_value' ).textContent = offset_slider.value + " m";
  }

  time_slider.addEventListener( 'input', applyTime );
  offset_slider.addEventListener( 'input', applyOffset );
  document.getElementById( 'mode_select' ).addEventListener( 'change', event => {
      scene.setAtmosphereLightingMode( mapray.AtmosphereLightingMode[event.target.value] );
  } );
  document.getElementById( 'visible_check' ).addEventListener( 'change', event => {
      // Visibility by category hides every 3D Tiles scene of the viewer at once.
      uiviewer.viewer.setVisibility( mapray.Viewer.Category.TILES_3D, event.target.checked );
  } );

  applyTime();
  applyOffset();
</script>

Info

このサンプルコードは、<YOUR_MAPRAY_ACCESS_TOKEN>を、あなたのMapray CloudのアカウントのOrganization Tokenに置き換えるまで、期待通りに動作しません。