3D Model
Show a 3D model on the map using three.js. The model is Trimble-owned content from the SketchUp team. Configuration of the custom layer for a 3D model implements theCustomLayerInterface
.
Requires Trimble Maps v3.0.0 or later.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.21.0.css" /> <script src="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.21.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>
3D Model
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8" /> <link rel="stylesheet" href="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.21.0.css" /> <script src="https://maps-sdk.trimblemaps.com/v3/trimblemaps-3.21.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>