
In Part 1, we explored how to integrate MapLibre GL with Vue.js to render a 3D globe and prepare the foundation for WebGL-based custom layers using Three.js. In this follow-up post, we’ll take things further by animating a 3D helicopter model along a predefined route — combining geospatial precision with 3D animation inside a modern Vue application.
To get full source code for this tutorial, click here.
To check the live demo for this tutorial, click here.
🛫 Objective
We aim to:
- Load a 3D helicopter model using
GLTFLoader - Render it on a custom MapLibre GL 3D layer using Three.js
- Animate its movement along a
LineStringusing Turf.js - Sync camera and bearing updates for immersive flight simulation
🧱 Setting Up the Scene
We’ll build on the existing Vue component and define a GeoJSON LineString that represents the helicopter’s flight path.
|
1 2 3 4 |
const route: GeoJSON.Feature<GeoJSON.LineString> = turf.lineString([ [-81.4004, 28.5396], [-81.4055, 28.541425] ]); |
Using Turf.js, we calculate the total distance and interpolate positions along the path based on elapsed time and animation progress.
🌀 Adding the Helicopter Model
We use a .glb format helicopter model loaded with GLTFLoader. Once the model is loaded, it’s scaled, rotated, and added to a THREE.Group() for easier transformations during animation.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
loader.load( that.helicopterConfig.url, (gltf) => { // Apply scaling and rotation gltf.scene.scale.set(2, 2, 2); gltf.scene.rotation.y = 0; // Add to scene self.helicopterGroup = new THREE.Group(); self.helicopter = gltf.scene; self.helicopterGroup.add(self.helicopter); self.scene.add(self.helicopterGroup); // Play animations if available self.mixer = new THREE.AnimationMixer(gltf.scene); gltf.animations.forEach((clip) => { self.mixer.clipAction(clip).play(); }); that.flightStartTime = performance.now(); } ); |
🛰️ Mapping 3D Coordinates
Using MercatorCoordinate.fromLngLat, we convert geographic coordinates to WebGL-compatible world space. We also apply transformation matrices to handle real-time positioning and orientation:
|
1 2 |
modelTransform = that.modelTransform(modelAsMercatorCoordinate, rotateX, yaw, rotateZ); self.camera.projectionMatrix = that.modelTransformation(modelTransform, args.defaultProjectionData.mainMatrix); |
These matrices allow seamless updates to the model’s position and rotation as it flies.
🎯 Controlling the Animation
A simple playFlight() method starts the animation, which is rendered on every frame via the render() function inside the CustomLayerInterface.
We calculate the helicopter’s current position on the route using turf.along() and derive the bearing using turf.bearing(), updating both the map’s center and bearing in sync:
|
1 2 3 |
const pointOnLine = turf.along(self.route, distance, { units: 'meters' }); const pointCoord = pointOnLine.geometry.coordinates; const bearing = turf.bearing(pointOnLine, nextPoint); |
The helicopter also gently changes altitude mid-flight for a more dynamic feel.
🧪 Demo Controls
We’ve added a basic control panel to trigger the animation:
|
1 2 3 |
<div id="controls" class="controls"> <button @click="playFlight">Play</button> </div> |
You can extend this to add Pause, Restart, or Speed controls later.
📽️ Final Result
The result is an interactive Vue + MapLibre + Three.js experience where a helicopter smoothly flies along a path, adjusting bearing and altitude — with real-time updates and full control over rendering.
This combination opens up rich possibilities for simulating aircraft, vehicles, or even storytelling elements in a geospatial context.
I hope this tutorial will create a good foundation for you. If you want tutorials on another GIS topic or you have any queries, please send an mail at contact@spatial-dev.guru.
