186 lines
6.9 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<title>heightmap demo</title>
</head>
<body>
<canvas id="canvas" style="width: 100%; height: 100%;"></canvas>
<div id="mapWrapper" style="
position: absolute;
z-index: 100;
width: 520px; height: 320px;
top: 360px;
color: white;
background: #00000080;">
<div id="msg"></div>
<p><a style="color: white;" href="https://github.com/w3reality/three-geo/tree/master/examples/heightmaps">Source Code</a></p>
</div>
<script src="../deps/three/build/three.min.js"></script>
<script src="../deps/three/examples/js/controls/OrbitControls.js"></script>
<script src="../deps/three/examples/js/libs/stats.min.js"></script>
<script src="../deps/threelet.min.js"></script>
<script src="../../dist/three-geo.min.js"></script>
<script type="module">
(async () => {
const threelet = new Threelet({
canvas: document.getElementById("canvas"),
});
threelet.setup('mod-controls', THREE.OrbitControls);
threelet.setup('mod-stats', Stats, {panelType: 1});
const { scene, render } = threelet;
render(); // first time
//
const group = new THREE.Group();
group.rotation.x = - Math.PI/2;
scene.add(group);
const ioToken = 'pk.eyJ1IjoiamRldmVsIiwiYSI6ImNqemFwaGJoZjAyc3MzbXA1OGNuODBxa2EifQ.7M__SgfWZGJuEiSqbBXdoQ';
const tgeo = new ThreeGeo({
tokenMapbox: ioToken, // <---- set your Mapbox API token here
});
if (!window.location.origin.startsWith('https://w3reality.github.io') && tgeo.tokenMapbox === ioToken) {
const warning = 'Please set your Mapbox API token in ThreeGeo constructor.';
alert(warning);
throw warning;
}
//
const createTextSprite = (text, color) => Threelet.Utils.createCanvasSprite(
Threelet.Utils.createCanvasFromText(text, 256, 64, {
tfg: color,
fontSize: '36px',
fontFamily: 'Times',
}));
const demToObjects = (demUri, demTile, proj) => {
const { obj, offset, size } = ThreeGeo.Utils.bboxToWireframe(
ThreeGeo.Utils.tileToBbox(demTile), proj, {
offsetZ: - 0.1,
color: 0xcc00cc,
});
// console.log('offset, size:', offset, size);
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(size[0], size[1]),
new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(demUri),
// side: THREE.DoubleSide,
}));
plane.position.set(...offset);
const sp = createTextSprite(`${demTile.join('-')}`, '#f0f');
sp.position.set(offset[0], offset[1], offset[2] + 0.1);
return {
wireframe: obj,
plane: plane,
sprite: sp,
};
};
const msg = document.getElementById('msg');
const appendText = (el, text) => {
const div = document.createElement('div');
div.appendChild(document.createTextNode(text));
el.appendChild(div);
};
// params: [lat, lng], terrain's radius (km), zoom resolution, callbacks
// Beware the value of radius; radius > 5.0 (km) could trigger huge number of tile API calls!!
const origin = [46.5763, 7.9904];
const radius = 5.0;
const { proj, bbox, unitsPerMeter } = tgeo.getProjection(origin, radius);
// console.log('proj:', proj);
// console.log('unitsPerMeter:', unitsPerMeter);
const srcDemUris = {};
appendText(msg, `---- ROI ----`);
appendText(msg, `lat lng: (${origin[0]}, ${origin[1]})`);
appendText(msg, `radius: ${radius} [km]`);
appendText(msg, `units per km: ${unitsPerMeter * 1000}`);
appendText(msg, `bbox (w, s, e, n): (${bbox.map(q => q.toFixed(4)).join(', ')})`);
appendText(msg, `---- Terrain Composition ----`);
const appendCompositionInfo = (el, tile, srcDemTile) => {
const div = document.createElement('div');
el.appendChild(div);
let span = document.createElement('span');
span.style.color = '#00ffffff';
span.appendChild(document.createTextNode(`tile ${tile.join('-')}`));
div.appendChild(span);
div.appendChild(document.createTextNode(' using '));
span = document.createElement('span');
span.style.color = '#ff00ffff';
span.appendChild(document.createTextNode(`DEM ${srcDemTile.join('-')}`));
div.appendChild(span);
};
//
const _zoom = (new URLSearchParams(document.location.search)).get('zoom');
const zoom = _zoom ? Number(_zoom) : 12;
if (zoom !== 11 && zoom !== 12 && zoom !== 13) {
const warning = 'Sorry, `zoom` is limited to 11, 12, or 13 in this example.';
alert(warning);
throw warning;
}
const terrain = await tgeo.getTerrainRgb(origin, radius, zoom);
group.add(terrain);
terrain.children.forEach(mesh => {
mesh.material.wireframe = true;
console.log('rgb DEM mesh:', mesh);
console.log('userData:', mesh.userData);
// !! how to access the post-processed heightmap
const array = mesh.geometry.attributes.position.array;
console.log('array.length:', array.length); // 3x128x128 (+ deltaSeams)
// !! how to visualize constituent tiles of the terrain
const tile = mesh.userData.threeGeo.tile;
const { obj, offset } = ThreeGeo.Utils.bboxToWireframe(
ThreeGeo.Utils.tileToBbox(tile), proj, {offsetZ: - 0.05});
const sp = createTextSprite(`${tile.join('-')}`, '#0ff');
sp.position.set(offset[0], offset[1], offset[2] + 0.05);
group.add(obj, sp);
// !! how to access src DEM being used (grand-parental tile)
// https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
const { tile: srcDemTile, uri: srcDemUri } = mesh.userData.threeGeo.srcDem;
appendCompositionInfo(msg, tile, srcDemTile);
if (! srcDemUris[srcDemUri]) {
srcDemUris[srcDemUri] = true;
let demUri = srcDemUri;
if (!tgeo.apiRgb.startsWith('mapbox-')) { // debug mode
const [tx, ty, tz] = srcDemTile;
demUri = `../cache/eiger/custom-terrain-rgb-${tz}-${tx}-${ty}.png`;
}
const { wireframe, plane, sprite } = demToObjects(demUri, srcDemTile, proj);
group.add(wireframe, plane, sprite);
}
});
render();
})();
</script>
</body>
</html>