<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.9.0.css" />
<script src="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.9.0.js"></script>
<script src="https://unpkg.com/three@0.128.0/build/three.min.js"></script>
<script src="https://unpkg.com/three@0.128.0/examples/js/loaders/ColladaLoader.js"></script>
<style>
body { margin: 0; padding: 0; }
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
.note {
position: absolute;
top: 20px;
left: 20px;
background-color: rgba(255, 255, 255, 0.8);
font-family: sans-serif;
font-weight: bold;
padding: 4px 8px;
color: maroon;
}
</style>
</head>
<body>
<div id="map"></div>
<div class="note">Right-click drag to rotate</div>
<script>
TrimbleMaps.APIKey = 'YOUR_API_KEY_HERE';
const myMap = new TrimbleMaps.Map({
container: 'map',
style: TrimbleMaps.Common.Style.TRANSPORTATION,
zoom: 16,
center: [-122.34929, 47.6216],
pitch: 60,
antialias: true, // create the gl context with MSAA antialiasing, so custom layers are antialiased
});
// configuration of the custom layer for a 3D model per the CustomLayerInterface
const customLayer = {
id: '3d-model',
type: 'custom',
renderingMode: '3d',
onAdd: function (map, gl) {
const scene = new THREE.Scene();
// use the three.js Collada loader to add the 3D model to the three.js scene
const loader = new THREE.ColladaLoader();
// './data/space_needle.dae'
loader.load(`${window.location.origin}/maps-sdk/data/space_needle.dae`, function (dae) {
scene.add(dae.scene);
}.bind(this));
// create three.js lights to illuminate the model
const directionalLight = new THREE.DirectionalLight(0x999999);
directionalLight.position.set(0, -70, 100).normalize();
scene.add(directionalLight);
const directionalLight2 = new THREE.DirectionalLight(0x999999);
directionalLight2.position.set(0, 70, 100).normalize();
scene.add(directionalLight2);
const light = new THREE.AmbientLight(0x666666); // soft white light
scene.add(light);
this.scene = scene;
// use the GL JS map canvas for three.js
const renderer = new THREE.WebGLRenderer({
canvas: map.getCanvas(),
context: gl,
antialias: true,
});
renderer.autoClear = false;
this.renderer = renderer;
},
render: function (gl, matrix) {
// parameters to ensure the model is georeferenced correctly on the map
const modelAltitude = 0;
const modelOrigin = [-122.34929, 47.6204];
const modelRotate = [Math.PI / 2, 0, 0];
const modelAsMercatorCoordinate = TrimbleMaps.MercatorCoordinate.fromLngLat(
modelOrigin,
modelAltitude
);
// transformation parameters to position, rotate and scale the 3D model onto the map
const modelTransform = {
translateX: modelAsMercatorCoordinate.x,
translateY: modelAsMercatorCoordinate.y,
translateZ: modelAsMercatorCoordinate.z,
rotateX: modelRotate[0],
rotateY: modelRotate[1],
rotateZ: modelRotate[2],
/* Since our 3D model is in real world meters, a scale transform needs to be
* applied since the CustomLayerInterface expects units in MercatorCoordinates.
*/
scale: modelAsMercatorCoordinate.meterInMercatorCoordinateUnits(),
};
const rotationX = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(1, 0, 0),
modelTransform.rotateX
);
const rotationY = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 1, 0),
modelTransform.rotateY
);
const rotationZ = new THREE.Matrix4().makeRotationAxis(
new THREE.Vector3(0, 0, 1),
modelTransform.rotateZ
);
const m = new THREE.Matrix4().fromArray(matrix);
const l = new THREE.Matrix4()
.makeTranslation(modelTransform.translateX, modelTransform.translateY, modelTransform.translateZ)
.scale(new THREE.Vector3(modelTransform.scale, -modelTransform.scale, modelTransform.scale))
.multiply(rotationX)
.multiply(rotationY)
.multiply(rotationZ);
const camera = new THREE.Camera();
camera.projectionMatrix = m.multiply(l);
this.renderer.resetState();
this.renderer.render(this.scene, camera);
},
};
myMap.on('style.load', function () {
if(myMap.getLayer(customLayer.id) == null) {
myMap.addLayer(customLayer);
}
});
</script>
</body>
</html>