Skip to content
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
5db40e0
RD-1396 migrate routing and animation helpers
Dec 18, 2025
133298d
RD-1396 Update and use correct imports
Dec 18, 2025
cd7d9c8
RD-1396 fix exports and failing exports unit test
Dec 18, 2025
f087a09
RD-1396 Add logic to only use a single geojson feature, update tests
Dec 19, 2025
0535f44
RD-1396 Update-readme
Dec 19, 2025
afed85a
RD-1396 Update changelog
Dec 19, 2025
b860d04
RD-1396 Appease linter, fix types, add 3d as devDep
Dec 19, 2025
73dbbff
RD-1396 run prettier
Dec 19, 2025
f1edfdb
RD-1396 Fix failing CI tests
Dec 19, 2025
eb1ccad
RD-1396 increase timeout on test for CI
Dec 19, 2025
4f13774
RD-1396 add timeout to correct test
Dec 19, 2025
de91f4a
RD-1396 Increase timeout in e2e test
Dec 19, 2025
266d312
[BOT] Update snapshots (Triggered by @lesbaa in PR RD-1396-move-routi…
Dec 19, 2025
3336516
RD-1396 Increase timeout to avoid flakeyness
Dec 19, 2025
13b488d
[BOT] Update snapshots (Triggered by @lesbaa in PR RD-1396-move-routi…
Dec 19, 2025
e7bb797
RD-1396 Update readme
Dec 22, 2025
095ca64
RD-1396 Fixes, typos and update readme from PR requests
Dec 22, 2025
a4d893e
RD-1396 fix type failure
Dec 29, 2025
0098f24
RD-1396 Fix PR comments
Dec 29, 2025
d8cb244
RD-1396 fix MapTilerAnimation example
Dec 29, 2025
52f2883
RD-1396: Fix default options logic for path smoothing
Jan 5, 2026
32df581
Merge branch 'next' into RD-1396-move-routing-to-main-repo
lesbaa Jan 13, 2026
8837c1f
RD-1396 Fix failing e2e tests and inject correct vite defined variables
lesbaa Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@

## NEXT

### ✨ Features and improvements
- Adds the new `MapTilerAnimation` module and associated helpers for creating and managing animations, lerping between values and 'smoothing' arrays.
- Adds the new `AnimatedRouteLayer` module and associated helpers for animating camera movement along GeoJSON features paths.

### 🐛 Bug Fixes
- None
### ⚙️ Others
- None

## 3.10.0

### ✨ Features and improvements
- MapLibre GL dependency was updated to `5.14`
- Types that were removed in a minor version of MapLibre GL, breaking semver, were moved into MapTiler SDK to not break compatibility for MapTiler SDK users
- Overpanning and underzooming patch for `ImageViewer` was updated to use a new standard MapLibre GL approach instead of monkey-patching it

## 3.9.1

### 🐛 Bug Fixes
- Reworks `setStyle` logic for halo and space

Expand Down
152 changes: 152 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,158 @@ When defining a new *ramp*, the colors can be an RGB array (`[number, number, nu

Many methods are available on color ramps, such as getting the `<canvas>` element of it, rescaling it, flipping it or [resampling it in a non-linear way](colorramp.md). Read more on [our reference page](https://docs.maptiler.com/sdk-js/api/color-ramp/) and have a look at our [examples](https://docs.maptiler.com/sdk-js/examples/?q=colorramp) to see how they work.

# Camera routes and animations

The SDK comes with several classes to help with animations, particularly route animations.

See `demos/11-animated-routes.html` for examples.
See `demos/12-maptiler-animation.html` for examples.

#### 🧩 `MaptilerAnimation`

MaptilerAnimation is a utility class for smoothly animating between keyframes using custom easing and playback control. It supports event-based hooks for frame updates and completion, and works well within rendering loops or UI transitions.

##### 🚀 Usage

```ts
// linearly animated between values
const animation = new MaptilerAnimation({
keyframes: [
// `props` are interpolated across the duration
{ delta: 0, props: { lon: -7.445, } },
// `userData` can hold any type of custom data to pass with the keyframe
{ delta: 0.5, userData: { mydata: "whoa!" } },
{ delta: 1, props: { lon: -7.473 } }
],
duration: 1000, // 1 second
iterations: Infinity // loop forever
});

const marker = new Marker().setLngLat(
new LngLat(
-7.449346225791231,
39.399728941536836,
)
).addTo(map);

// TimeUpdate is fired every frame
animation.addEventListener(AnimationEventTypes.TimeUpdate, (e) => {
marker.setLngLat(
new LngLat(
e.props.lon,
39.399728941536836,
)
)
})
// fired when the keyframe changes
animation.addEventListener(AnimationEventTypes.Keyframe, ({ userData }) => {
console.log(userData.mydata) // "whoa!"
});

animation.play();
```
![](images/animate-linear-trimmed.gif)

```ts
// eased between values
const animation = new MaptilerAnimation({
keyframes: [
// `props` are interpolated across the duration
{ delta: 0, easing: EasingFunctionName.ElasticInOut, props: { lon: -7.445, } },
{ delta: 1, props: { lon: -7.455 } }
],
duration: 1000, // 1 second
iterations: Infinity // loop forever
});
```
![](images/animate-elastic-trimmed.gif)

####  🗺️ `AnimatedRouteLayer`

`AnimatedRouteLayer` is custom layer that animates a path or route on the map based on keyframes or GeoJSON data. It supports animated line styling and camera following, making it ideal for visualizing routes, playback tracks, or timeline-based geographic events.

Note: At present, to avoid problems arising from the camera being manipulated by two animations at any one time, there can only ever be one instance of `AnimatedRouteLayer` on the map at any time. This API may change in the future, but at present you must remove each instance of `AnimatedRouteLayer` from the map before adding another.

##### ✨ Features
- Animate a path using keyframes or GeoJSON data
- Optional animated stroke styles to indicate progress
- Camera movement smoothing, following along the route
- Configurable duration, easing, delay, and iterations via geojson properties
- Event-based lifecycle hooks for adaptability.
- Optional manual frame advancement (e.g., for scrubbing or syncing with map events, scroll etc etc)

##### 🚀 Basic Usage
```ts
const myGeoJSONSource = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[-74.0060, 40.7128],
[-73.9352, 40.7306],
[-73.9851, 40.7580]
]
},
"properties": {
"@duration": 5000, // animation params are prepended with '@'
"@iterations": 3,
"@delay": 1000,
"@autoplay": true,
"bearing": [
40,
30,
10,
10,
20,
40,
]
}
}
]
}

map.addSource("my-geojson-source", {
type: "geojson",
data: myGeoJSONSource,
});

const animatedRoute = new AnimatedRouteLayer({
source: {
// assumes that the source is already added to the map with the given layer ID
id: "my-geojson-source", // the name of the source
layerID: "route-layer", // the name of the layer
},
// OR
keyframes: [], // an array of keyframes

duration: 5000,
pathStrokeAnimation: {
// will only be applied to LineString GeoJSON types
activeColor: [0, 128, 0, 1], // color of the line that has already been traversed
inactiveColor: [128, 128, 128, 0.5],
},
cameraAnimation: {
follow: true, // should the camera follow the route?
pathSmoothing: {
resolution: 20, // the resolution of the smoothness
epsilon: 10, // how much the path is simplified before smoothing
},
},
autoplay: true,
});

// Add to map
map.addLayer(animatedRoute);

// Playback controls
animatedRoute.play();
animatedRoute.pause();
```

For a full example of how to use this, look at [the example](./demos/11-animated-routes.html)

# Vector Layer Helpers
**Let's make vector layers easy!** Originally, you'd have to add a source and then proceed to the styling of your layer, which can be tricky because there are a lot of `paint` and `layout` options and they vary a lot from one type of layer to another. **But we have helpers for this!** 🖋️
Expand Down
79 changes: 79 additions & 0 deletions demos/public/11-animated-routes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Animations and Animated Routes</title>
<style>
html {
height: 100%;
font-family: sans-serif;
font-size: 13px;
}

body {
height: 100%;
margin: 0;
display: grid;
/* grid-template: 1fr 1fr / 1fr 1fr; */
/* gap: 16px; */
box-sizing: border-box;
}

.map {
display: flex;
justify-content: center;
align-items: center;
}

.controls-container {
position: absolute;
top: 16px;
left: 16px;
gap: 8px;
z-index: 1;
}

.controls {
display: flex;
}

.padded, button {
display: inline-flex;
align-items: center;
border-radius: 8px;
background-color: white;
padding: 8px;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 4px;
}

button {
cursor: pointer;
}
</style>
</head>
<body>
<div class="controls-container">
<div class="controls">
<button id="play">
Play
</button>
<button id="pause">
Pause
</button>
<button id="faster">
Faster
</button>
<button id="slower">
Slower
</button>
<div id="playbackRate" class="padded">1.0x</div>
</div>
<div id="keyframeName" class="padded" style="display: none">
</div>
</div>
<script type="module" src="./src/11-animated-routes.ts"></script>
</body>
</html>
83 changes: 83 additions & 0 deletions demos/public/12-maptiler-animation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>MaptilerAnimation with MapTiler 3D Module</title>
<style>
html {
height: 100%;
font-family: sans-serif;
font-size: 13px;
}

body {
height: 100%;
margin: 0;
display: grid;
/* grid-template: 1fr 1fr / 1fr 1fr; */
/* gap: 16px; */
box-sizing: border-box;
}

.map {
display: flex;
justify-content: center;
align-items: center;
}

.controls-container {
position: absolute;
top: 16px;
left: 16px;
gap: 8px;
z-index: 1;
}

.controls {
display: flex;
}

.padded, button {
display: inline-flex;
align-items: center;
border-radius: 8px;
background-color: white;
padding: 8px;
border: 1px solid #ccc;
font-size: 16px;
margin-right: 4px;
}

button {
cursor: pointer;
}
</style>
<link rel="stylesheet" href="../build/maptiler-sdk.css">
</head>
<body>
<div class="controls-container">
<div class="controls">
<button id="play">
Play
</button>
<button id="pause">
Pause
</button>
<button id="faster">
Faster
</button>
<button id="slower">
Slower
</button>
<button id="frame-advance">
Frame Advance
</button>
<div id="playbackRate" class="padded">1.0x</div>
</div>
<div id="keyframeName" class="padded" style="display: none">
</div>
</div>
<script type="module" src="./src/12-maptiler-animation.ts"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions demos/public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
<a href="07-spacebox.html">SpaceBox →</a>
<a href="09-custom-controls-declarative.html">Custom Controls: Declarative →</a>
<a href="10-custom-controls-programmatic.html">Custom Controls: Programmatic →</a>
<a href="11-animated-routes.html">Animated Routes →</a>
<a href="12-maptiler-animation.html">Basic Animations →</a>
</div>
</div>
</body>
Expand Down
11 changes: 11 additions & 0 deletions demos/public/models/burj_khalifa/license.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Model Information:
* title: Burj Khalifa
* source: https://sketchfab.com/3d-models/burj-khalifa-59e6dd74e5f647158de568b5a7f9cab7
* author: ManySince910 (https://sketchfab.com/noears6)

Model License:
* license type: CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
* requirements: Author must be credited. Commercial use is allowed.

If you use this 3D model in your project be sure to copy paste this credit wherever you share it:
This work is based on "Burj Khalifa" (https://sketchfab.com/3d-models/burj-khalifa-59e6dd74e5f647158de568b5a7f9cab7) by ManySince910 (https://sketchfab.com/noears6) licensed under CC-BY-4.0 (http://creativecommons.org/licenses/by/4.0/)
Binary file added demos/public/models/burj_khalifa/scene.bin
Binary file not shown.
Loading
Loading