maplibre-gl-time-slider
v0.3.0
Published
A MapLibre GL JS plugin for visualizing time series vector and raster data with an interactive time slider control
Maintainers
Readme
maplibre-gl-time-slider
A MapLibre GL JS plugin for visualizing time series vector and raster data with an interactive time slider control.
Features
- Interactive time slider with play/pause controls
- Support for both vector and raster time series data
- Built-in TiTiler integration for Cloud Optimized GeoTIFFs (COGs)
- Add Layer button for persisting time periods as permanent layers for comparison
- Customizable playback speed and loop settings
- Control layer ordering with
beforeIdparameter - React component and hooks support
- TypeScript support with full type definitions
- Lightweight and easy to integrate
Installation
npm install maplibre-gl-time-sliderQuick Start
Basic Usage (Vanilla JavaScript/TypeScript)
import maplibregl from 'maplibre-gl';
import { TimeSliderControl } from 'maplibre-gl-time-slider';
import 'maplibre-gl-time-slider/style.css';
const map = new maplibregl.Map({
container: 'map',
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
});
map.on('load', () => {
const timeSlider = new TimeSliderControl({
title: 'Time Slider',
labels: ['2024-01', '2024-02', '2024-03', '2024-04'],
speed: 1000,
loop: true,
onChange: (index, label) => {
console.log(`Current: ${label} (index: ${index})`);
// Update your map layers here
},
});
map.addControl(timeSlider, 'top-right');
});React Usage
import { useState, useEffect, useRef } from 'react';
import maplibregl from 'maplibre-gl';
import { TimeSliderControlReact } from 'maplibre-gl-time-slider/react';
import 'maplibre-gl-time-slider/style.css';
function MyMap() {
const mapContainer = useRef<HTMLDivElement>(null);
const [map, setMap] = useState<maplibregl.Map | null>(null);
useEffect(() => {
if (!mapContainer.current) return;
const mapInstance = new maplibregl.Map({
container: mapContainer.current,
style: 'https://demotiles.maplibre.org/style.json',
center: [0, 0],
zoom: 2,
});
mapInstance.on('load', () => setMap(mapInstance));
return () => mapInstance.remove();
}, []);
return (
<>
<div ref={mapContainer} style={{ width: '100%', height: '100vh' }} />
{map && (
<TimeSliderControlReact
map={map}
title="Time Slider"
labels={['2024-01', '2024-02', '2024-03']}
onChange={(index, label) => {
// Update map layers
}}
/>
)}
</>
);
}Examples
Vector Data (Filtering by Time)
Filter vector features by a time property:
const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
const timeSlider = new TimeSliderControl({
labels: months,
onChange: (index, label) => {
// Filter vector layer by month
map.setFilter('my-layer', ['==', ['get', 'month'], index]);
},
});Raster Data (TiTiler Integration)
Display time series raster data using TiTiler:
import { TimeSliderControl, buildTiTilerTileUrl } from 'maplibre-gl-time-slider';
const rasterData = {
'2024-01': 'https://example.com/cog_2024_01.tif',
'2024-02': 'https://example.com/cog_2024_02.tif',
'2024-03': 'https://example.com/cog_2024_03.tif',
};
const labels = Object.keys(rasterData);
const urls = Object.values(rasterData);
// Add initial raster source
map.addSource('raster-data', {
type: 'raster',
tiles: [
buildTiTilerTileUrl({
url: urls[0],
colormap: 'viridis',
}),
],
tileSize: 256,
});
map.addLayer({
id: 'raster-layer',
type: 'raster',
source: 'raster-data',
});
// Create time slider
const timeSlider = new TimeSliderControl({
labels: labels,
onChange: (index, label) => {
const source = map.getSource('raster-data');
source.setTiles([
buildTiTilerTileUrl({
url: urls[index],
colormap: 'viridis',
}),
]);
},
});Adding Persistent Layers for Comparison
Enable the "Add Layer" button to allow users to persist specific time periods as permanent layers:
let layerCounter = 0;
const timeSlider = new TimeSliderControl({
labels: ['2020', '2021', '2022', '2023'],
onChange: (index, label) => {
// Update the main time slider layer
const source = map.getSource('main-raster');
source.setTiles([getTileUrlForYear(label)]);
},
onAddLayer: (index, label, beforeId) => {
// Create a persistent layer for the selected time period
layerCounter++;
const sourceId = `persistent-source-${label}`;
const layerId = `Persistent Layer ${label}`;
// Add source
map.addSource(sourceId, {
type: 'raster',
tiles: [getTileUrlForYear(label)],
tileSize: 256,
});
// Add layer before the main time slider layer
map.addLayer({
id: layerId,
type: 'raster',
source: sourceId,
paint: {
'raster-opacity': 0.7,
},
}, beforeId);
console.log(`Added persistent layer for ${label}`);
},
beforeId: 'main-layer-id', // Insert persistent layers before this layer
});This feature is useful for:
- Comparing data across multiple time periods
- Creating side-by-side visualizations
- Highlighting specific time periods for analysis
- Building temporal comparisons in the layer control
API Reference
TimeSliderControl
Main control class that implements MapLibre's IControl interface.
Constructor Options
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| labels | string[] | [] | Array of labels to display (required) |
| title | string | 'Time Slider' | Title displayed in the panel header |
| collapsed | boolean | true | Whether to start with panel collapsed |
| position | string | 'top-right' | Control position on the map |
| panelWidth | number | 300 | Width of the panel in pixels |
| initialIndex | number | 0 | Initial index to start at |
| speed | number | 1000 | Playback speed in milliseconds |
| loop | boolean | true | Whether to loop playback |
| className | string | '' | Custom CSS class name |
| onChange | function | - | Callback when index changes: (index: number, label: string) => void |
| onAddLayer | function | - | Callback when "Add Layer" button is clicked: (index: number, label: string, beforeId?: string) => void |
| beforeId | string | - | Layer ID to insert new persistent layers before (ensures proper layer ordering) |
Methods
| Method | Description |
|--------|-------------|
| play() | Start playback |
| pause() | Pause playback |
| togglePlayback() | Toggle play/pause state |
| next() | Move to next label |
| prev() | Move to previous label |
| goTo(index) | Navigate to specific index |
| setSpeed(ms) | Set playback speed |
| setLoop(enabled) | Set loop state |
| setLabels(labels, resetIndex?) | Update labels |
| getCurrentIndex() | Get current index |
| getCurrentLabel() | Get current label |
| getLabels() | Get all labels |
| getState() | Get full state object |
| toggle() | Toggle panel collapsed state |
| expand() | Expand panel |
| collapse() | Collapse panel |
| on(event, handler) | Register event listener |
| off(event, handler) | Remove event listener |
Events
| Event | Description |
|-------|-------------|
| change | Fired when the current index changes |
| play | Fired when playback starts |
| pause | Fired when playback pauses |
| collapse | Fired when panel collapses |
| expand | Fired when panel expands |
| statechange | Fired on any state change |
TiTiler Utilities
buildTiTilerTileUrl(options)
Builds a TiTiler XYZ tile URL for MapLibre raster sources.
const tileUrl = buildTiTilerTileUrl({
url: 'https://example.com/my-cog.tif',
endpoint: 'https://giswqs-titiler-endpoint.hf.space', // optional, default
colormap: 'viridis', // optional, default
rescale: [-10, 10], // optional
bidx: [1], // optional, band indexes
});getTiTilerBounds(url, endpoint?)
Fetches the bounds of a COG file.
const bounds = await getTiTilerBounds('https://example.com/my-cog.tif');
map.fitBounds(bounds);getTiTilerInfo(url, endpoint?)
Fetches metadata about a COG file.
getTiTilerStatistics(url, endpoint?)
Fetches statistics (min, max, mean, std) for each band.
Development
# Install dependencies
npm install
# Start development server
npm run dev
# Build library
npm run build
# Build examples
npm run build:examples
# Run tests
npm test
# Lint code
npm run lint
# Format code
npm run formatDocker
The examples can be run using Docker. The image is automatically built and published to GitHub Container Registry.
Pull and Run
# Pull the latest image
docker pull ghcr.io/opengeos/maplibre-gl-time-slider:latest
# Run the container
docker run -p 8080:80 ghcr.io/opengeos/maplibre-gl-time-slider:latestThen open http://localhost:8080/maplibre-gl-time-slider/ in your browser to view the examples.
Build Locally
# Build the image
docker build -t maplibre-gl-time-slider .
# Run the container
docker run -p 8080:80 maplibre-gl-time-sliderAvailable Tags
| Tag | Description |
|-----|-------------|
| latest | Latest release |
| x.y.z | Specific version (e.g., 1.0.0) |
| x.y | Minor version (e.g., 1.0) |
License
MIT License - see LICENSE for details.
