<html>
<head>
<meta charset='utf-8' />
<title>Vector-Tiles Sagrada Familia 3d ICGC</title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/98/three.min.js"></script>
<script src="vendors/js/GLTFLoader.js"></script>
<link href='https://cdn.skypack.dev/maplibre-gl/dist/mapbox-gl.css' rel='stylesheet' />
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
height: 100%
}
#menu {
position: absolute;
z-index: 1000;
background: #fff;
padding: 10px;
font-family: 'Open Sans', sans-serif;
top: 5px;
left: 5px;
border-radius: 7px;
-webkit-box-shadow: 5px 5px 5px -5px rgba(0, 0, 0, 0.75);
-moz-box-shadow: 5px 5px 5px -5px rgba(0, 0, 0, 0.75);
box-shadow: 5px 5px 5px -5px rgba(0, 0, 0, 0.75);
}
#div_pitch {
position: absolute;
right: 10px;
top: 142px;
width: 30px;
z-index: 100;
}
.sliderClass {
position: absolute;
left: 10px;
top: 79px;
width: 130px;
z-index: 100;
display: none;
background-color: white;
padding: 10px;
border-radius: 4px;
font-size: 0.85em;
opacity: 0.9;
}
</style>
</head>
<body>
<div id='menu'>
<input id='icgc_mapa_estandard' type='radio' name='rtoggle' value='icgc_mapa_estandard' checked='checked'>
<label for='icgc_mapa_estandard'>Mapa està ndard</label><br>
<input id='icgc_orto_hibrida' type='radio' name='rtoggle' value='icgc_orto_hibrida'>
<label for='icgc_orto_hibrida'>Orto hÃbrida</label>
</div>
<div id='div_pitch' class="mapboxgl-ctrl-group mapboxgl-ctrl">
<button id="bt_pitch" title="Perspectiva" class="mapboxgl-ctrl-icon glyphicon glyphicon-road"></button>
</div>
<div id="map"></div>
<script>
import {Map, NavigationControl,GeolocateControl,AttributionControl} from 'https://cdn.skypack.dev/maplibre-gl';
const THREE = window.THREE;
const loader = new THREE.GLTFLoader();
let _gltf;
let currentTO;
let isRotating = false;
const map = new Map({
container: 'map',
style: 'https://geoserveis.icgc.cat/contextmaps/icgc_mapa_estandard.json',
center: [2.174689, 41.403806],
zoom: 15.93,
maxZoom: 18,
hash: true,
bearing: -131.2,
pitch: 45
});
map.on('load', function () {
map.addControl(new NavigationControl());
map.addControl(new GeolocateControl({
positionOptions: {
enableHighAccuracy: true,
watchPosition: true
}
}));
loader.load(
// resource URL
'models/sf_0.01625.glb',
// called when the resource is loaded
function (gltf) {
_gltf = gltf;
add3DModel();
currentTO = setTimeout(startRotation, 2000);
},
// called while loading is progressing
function (xhr) {
console.log((xhr.loaded / xhr.total * 100) + '% loaded');
},
// called when loading has errors
function (error) {
console.log('An error happened', error);
}
);
map.on('styledata', function () {
setTimeout(function () {
if (map.isStyleLoaded()) {
add3DModel();
}
}, 2000);
});
map.on('zoomend', function (e) {
if (map.getZoom() >= 13) {
map.easeTo({
'pitch': 50
});
} else {
map.easeTo({
'pitch': 20
});
}
});
});
class ThreeICGCMODEL {
constructor() {
this.id = 'sagradaFamilia';
this.type = 'custom';
this.renderingMode = '3d';
this.translate = fromLL(2.174546, 41.403564);
this.scale = 0.000000037;
this.camera = new THREE.Camera();
this.scene = _gltf.scene;
this.scene.remove(this.scene.getObjectByName("Ambient"));
const directionalLight = new THREE.AmbientLight(0xffffff, 1.5);
directionalLight.name = "Ambient";
this.scene.add(directionalLight);
}
onAdd(map, gl) {
this.map = map;
this.renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl
});
this.renderer.autoClear = false;
}
render(gl, matrix) {
const m = new THREE.Matrix4().fromArray(matrix);
const l = new THREE.Matrix4()
.makeTranslation(this.translate[0], this.translate[1], this.translate[2])
.scale(new THREE.Vector3(this.scale, -this.scale, this.scale));
this.camera.projectionMatrix.elements = matrix;
this.camera.projectionMatrix = m.multiply(l);
this.renderer.state.reset();
// this.renderer.context.disable(this.renderer.context.DEPTH_TEST);
this.renderer.render(this.scene, this.camera);
this.map.triggerRepaint();
}
}
//Thank you!!!
//https://bl.ocks.org/andrewharvey/7b61e9bdb4165e8832b7495c2a4f17f7
const fromLL = function (lon, lat) {
// derived from https://gist.github.com/springmeyer/871897
const extent = 20037508.34;
const x = lon * extent / 180;
let y = Math.log(Math.tan((90 + lat) * Math.PI / 360)) / (Math.PI / 180);
y = y * extent / 180;
return [(x + extent) / (2 * extent), 1 - ((y + extent) / (2 * extent)), 0.000001];
}
const layerList = document.getElementById('menu');
const inputs = layerList.getElementsByTagName('input');
const button = document.getElementById('bt_pitch');
function switchLayer(layer) {
const layerId = layer.target.id;
map.setStyle('https://geoserveis.icgc.cat/contextmaps/' + layerId + '.json');
}
function canviaPerspectiva() {
const pitch = parseInt(map.getPitch());
pitch == 60 ? pitch = 0 : pitch = pitch + 30;
map.easeTo({
'pitch': pitch
})
};
function add3DModel() {
if (!map.getLayer('sagradaFamilia')) {
map.addLayer(new ThreeICGCMODEL());
}
}
function startRotation() {
isRotating = true;
window.requestAnimationFrame(rotate);
}
map.on('mousemove', function () {
isRotating = false;
clearTimeout(currentTO);
currentTO = setTimeout(startRotation, 2000);
});
function rotate() {
if (isRotating) {
map.setBearing(map.getBearing() + 0.1);
}
window.requestAnimationFrame(rotate);
}
for (let i = 0; i < inputs.length; i++) {
inputs[i].onclick = switchLayer;
}
button.onclick = canviaPerspectiva;
</script>
</body>
</html>