3D Tiles with implicit tiling¶
Loading a 3D Tiles 1.1 tileset with implicit tiling, whose tiles are given by subtree files instead of being listed in tileset.json.
display_3dtiles_implicit.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 implicit tiling</title>
<meta property="og:description" content="Loading a 3D Tiles 1.1 tileset with implicit tiling, whose tiles are given by subtree files instead of being listed in tileset.json." />
<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: 320px;
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;
}
.legend span {
display: inline-block;
width: 10px;
height: 10px;
margin-right: 5px;
}
</style>
</head>
<body>
<div id="mapray-container"></div>
<div class="map-overlay top">
<div class="map-overlay-inner">
<fieldset>
<label>Implicit tiling</label>
tileset.json gives only the root tile and the rule to divide it. Which tiles exist is read from subtree files as the camera comes closer.
</fieldset>
<fieldset>
<label>Camera</label>
<button id="far_button" type="button">View from far</button>
<button id="near_button" type="button">Come closer to the centre</button>
</fieldset>
<fieldset>
<label>Tile level (color of the boxes)</label>
<div class="legend">
<span style="background-color: #ebebeb"></span>0
<span style="background-color: #fad940"></span>1
<span style="background-color: #fa8c26"></span>2
<span style="background-color: #e03326"></span>3
<span style="background-color: #8c33bf"></span>4
</div>
</fieldset>
<fieldset>
<label>Fetched so far</label>
<span id="fetch_info">-</span>
</fieldset>
</div>
</div>
</body>
</html>
<script>
// Generated by browser-test/scripts/generate-implicit-3dtiles-example.mjs: a sparse quadtree of 5 levels over Yumenoshima, Tokyo.
const TILESET_URL = new URL( "../data/3dtiles-implicit/tileset.json", location.href ).href;
const CENTER = { longitude: 139.8270, latitude: 35.6527 };
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()
} );
function lookAtCenter( distance, height ) {
const offset = distance / 111000;
uiviewer.setCameraPosition( { longitude: CENTER.longitude - offset, latitude: CENTER.latitude - offset, height } );
uiviewer.setLookAtPosition( { longitude: CENTER.longitude, latitude: CENTER.latitude, height: 0 } );
}
lookAtCenter( 2500, 2000 );
// The scene requests tiles itself; nothing in the application changes for implicit tiling.
const scene = uiviewer.viewer.b3d_collection.createTiles3DScene( new mapray.StandardTiles3DProvider( { url: TILESET_URL } ) );
// Count what the browser fetched, to show that only the needed subtrees and tiles are read.
const fetched = { subtrees: new Set(), contents: new Set(), deepest: -1 };
function countEntries( entries ) {
for ( const entry of entries ) {
const subtree = entry.name.match( /\/subtrees\/(\d+)\.\d+\.\d+\.subtree/ );
const content = entry.name.match( /\/content\/(\d+)\/\d+\/\d+\.glb/ );
if ( subtree ) {
fetched.subtrees.add( entry.name );
}
if ( content ) {
fetched.contents.add( entry.name );
fetched.deepest = Math.max( fetched.deepest, Number( content[1] ) );
}
}
}
countEntries( performance.getEntriesByType( "resource" ) );
new PerformanceObserver( list => countEntries( list.getEntries() ) ).observe( { type: "resource", buffered: true } );
let tileset_bytes = "-";
let tile_positions = "-";
fetch( TILESET_URL ).then( response => response.text() ).then( text => {
const implicit = JSON.parse( text ).root.implicitTiling;
tileset_bytes = text.length;
// A quadtree of n levels has 1 + 4 + ... + 4^(n-1) tile positions.
tile_positions = ( Math.pow( 4, implicit.availableLevels ) - 1 ) / 3;
} );
setInterval( () => {
document.getElementById( 'fetch_info' ).textContent =
"tileset.json " + tileset_bytes + " bytes for up to " + tile_positions + " tile positions; " +
fetched.subtrees.size + " subtree files and " + fetched.contents.size + " tiles fetched, deepest level " +
( fetched.deepest < 0 ? "-" : fetched.deepest );
}, 500 );
document.getElementById( 'far_button' ).addEventListener( 'click', () => lookAtCenter( 2500, 2000 ) );
document.getElementById( 'near_button' ).addEventListener( 'click', () => lookAtCenter( 700, 450 ) );
</script>
Info
このサンプルコードは、<YOUR_MAPRAY_ACCESS_TOKEN>を、あなたのMapray CloudのアカウントのOrganization Tokenに置き換えるまで、期待通りに動作しません。