# How the Time Slider Plugin in GeoLibre Animates Time Series Raster and Vector Data

> Discover how GeoLibre's Time Slider plugin animates time series raster and vector data. It synchronizes with Zustand to control timelines for tiles, filters, and overlays.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-03

---

**The Time Slider plugin in GeoLibre wraps the `maplibre-gl-time-slider` library and synchronizes it with a Zustand store to drive raster tiles, vector filters, data-cube slices, and overlay visibility from a unified timeline.**

The Time Slider plugin is a core component of the open-source GeoLibre geospatial platform maintained by OpenGeo. It enables users to explore temporal datasets—Cloud Optimized GeoTIFFs (COGs), mosaics, vector features, and data cubes—through an interactive dock interface. This article explains the complete animation architecture, from plugin activation to pixel-level time series queries.

---

## Plugin Architecture and Activation

The plugin lives in [`packages/plugins/src/plugins/maplibre-time-slider.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/maplibre-time-slider.ts) and follows a standard lifecycle: **activate**, **sync**, and **deactivate**.

When a user opens the Time Slider dock, `maplibreTimeSliderPlugin.activate` instantiates or restores a `TimeSliderControl` and stores it in a module-level variable:

```typescript
// maplibre-time-slider.ts (lines 58-66)
timeSliderControl = new TimeSliderControl(savedConfig);
map.addControl(timeSliderControl, 'bottom-left');

```

The `TimeSliderControl` is the third-party widget that renders the timeline, playback buttons, and date display. The plugin wraps this control to integrate it with GeoLibre's reactive store system.

---

## Store-Control Synchronization

The `attachStoreSync` function establishes bidirectional communication between the control and the application's Zustand store:

```typescript
// maplibre-time-slider.ts (lines 76-88)
function attachStoreSync(control: TimeSliderControl) {
  control.on('sourceadd', syncStoreLayers);
  control.on('sourceremove', syncStoreLayers);
  startThemeObserver(control);  // Sync light/dark mode
}

```

Two critical events flow through this system:

- **`sourceadd`/`sourceremove`** – Fires when raster sources are added to or removed from the timeline
- **`statechange`** – Fires on every timeline tick, date selection, or playback step

---

## Raster Source Mirroring and Animation

### Creating Mirrored Store Layers

For each source managed by the control, `syncStoreLayers` creates a **mirrored GeoLibre layer** via `createStoreLayer` (lines 101-119). This mirror captures:

- Source ID and extent
- Opacity and visibility state
- Flags: `identifiable`, `clientRenderedRaster`

These mirrored layers enable the Layers panel and Style panel to control raster appearance without directly manipulating the `TimeSliderControl`.

### Tile Animation Mechanics

Raster animation happens automatically within the control. When the timeline advances, the control computes the date-templated URL for each source and requests the appropriate tiles. The plugin's role is minimal: it keeps the store's opacity/visibility in sync so the UI reflects the current state.

For **COG sources**, the plugin can also read pixel values. The `resolveUrl` helper expands date templates (e.g., `/{year}/{month}/{day}`), and `resolvePixelReadUrl` locates the exact image region for a given coordinate.

---

## Vector Data Binding

Vector layers participate in animation through **time binding metadata**. When a user selects "Bind to Time Slider" in the UI, the layer gains a `timeBinding` entry with these properties:

```typescript
interface TimeBinding {
  property: string;      // Feature property containing the timestamp
  min: string;          // ISO date string (timeline start)
  max: string;          // ISO date string (timeline end)
  granularity: TimeGranularity;  // 'day', 'month', 'year', etc.
}

```

### Building Time Filters

The `buildTimeFilter` function in [`time-slider-binding.ts`](https://github.com/opengeos/GeoLibre/blob/main/time-slider-binding.ts) constructs a MapLibre filter expression:

```typescript
// time-slider-binding.ts
function buildTimeFilter(property: string, date: Date): FilterExpression {
  const start = date.toISOString();
  const end = getNextDate(date, granularity).toISOString();
  return [
    "all",
    [">=", ["get", property], start],
    ["<", ["get", property], end]
  ];
}

```

The `reconcileBoundLayers` function (called on every `statechange`) applies this filter to bound layers via `store.updateLayer`, but only when the filter actually changes—preventing unnecessary re-renders.

---

## Data-Cube (Selector) Binding

Some vector layers are backed by **temporal data cubes** (Zarr stores or similar). These use a `SelectorTimeBinding` rather than a property filter.

The [`temporal-layers.ts`](https://github.com/opengeos/GeoLibre/blob/main/temporal-layers.ts) module manages this pathway:

1. **`getSelectorLayers`** – Discovers layers with temporal adapter registrations
2. **`scheduleSelectorTimes`** – Throttles calls to `adapter.setTime` to the most recent timeline position
3. **Adapter rendering** – The temporal adapter fetches the appropriate cube slice and renders via Deck.gl or the WASM raster pipeline

This approach bypasses MapLibre filters entirely: the data cube's native time indexing determines what appears on screen.

---

## Overlay Frame Animation

KML and image overlays with `<TimeSpan>` or `<TimeStamp>` elements receive special handling. The `getTimeOverlayFrames` function collects all time-enabled overlays, and `applyTimeOverlayVisibility` (lines 1025-1034) toggles their visibility flag to match the current date:

```typescript
// maplibre-time-slider.ts (simplified)
function applyTimeOverlayVisibility(date: Date) {
  const frames = getTimeOverlayFrames();
  for (const frame of frames) {
    frame.visible = isDateInRange(date, frame.timeSpan);
  }
}

```

This enables synchronized playback of image sequences—such as satellite imagery composites—alongside other data types.

---

## Pixel Time Series Queries

The chart button in the dock triggers `queryPixelTimeSeries` from [`time-slider-pixel-series.ts`](https://github.com/opengeos/GeoLibre/blob/main/time-slider-pixel-series.ts). This function reads underlying raster values across the entire timeline:

### Step-by-Step Query Process

| Step | Action | Key Function |
|------|--------|--------------|
| Gather sources | Collect all pixel-identifiable raster sources | `getTimeSliderPixelSources` |
| Determine steps | Build timeline positions (ordinal or continuous) | Timeline config |
| Downsample | Optionally reduce steps for performance | `downsampleSteps` |
| Resolve URLs | Expand date templates and mosaic manifests | `resolveUrl`, `usesMosaicManifest` |
| Read pixels | Load GeoTIFFs and extract band values | `loadGeoTIFF`, `readPixelValues` |
| Limit concurrency | Cap parallel reads to 6 | `READ_CONCURRENCY` |

```typescript
// time-slider-pixel-series.ts (lines 24-37)
export async function queryPixelTimeSeries(
  lngLat: [number, number],
  options: { maxSteps?: number } = {}
): Promise<PixelTimeSeriesResult> {
  const sources = getTimeSliderPixelSources();
  const steps = downsampleSteps(getTimelineSteps(), options.maxSteps);
  
  const series = await Promise.all(
    sources.map(s => querySourceSeries(s, steps, lngLat))
  );
  
  return { series, bands: extractBandOptions(sources) };
}

```

The result is a `PixelTimeSeriesResult` containing labeled series for each source and band, ready for charting or export to GeoJSON/CSV.

---

## Cleanup and State Persistence

When the dock closes, `deactivate` performs orderly shutdown:

```typescript
// maplibre-time-slider.ts (lines 77-84)
function deactivate() {
  const config = timeSliderControl.getConfig();
  savedState.set(TIME_SLIDER_KEY, config);  // Persist for next session
  
  map.removeControl(timeSliderControl);
  removeAllTimeSliderStoreLayers();
  clearMemoization();
}

```

This preserves user preferences while ensuring no orphaned layers or callbacks remain.

---

## Summary

- **Raster animation** is handled internally by `TimeSliderControl`; the plugin mirrors sources to the store for UI consistency
- **Vector animation** uses `timeBinding` metadata and `buildTimeFilter` to construct MapLibre filter expressions
- **Data-cube animation** delegates to registered `TemporalLayerAdapter` instances via throttled `setTime` calls
- **Overlay animation** toggles visibility of time-enabled KML/image frames
- **Pixel queries** resolve date-templated URLs, read COG bands with controlled concurrency, and return structured time series
- All pathways synchronize through `reconcileBoundLayers` on every timeline state change

---

## Frequently Asked Questions

### How do I bind a vector layer to the Time Slider?

Add a `timeBinding` object to the layer's metadata with the timestamp property, date range, and granularity. The plugin automatically constructs appropriate filters when the timeline moves.

### Can the Time Slider animate both raster and vector data simultaneously?

Yes. The same `statechange` event drives tile URL updates for rasters, filter updates for vectors, adapter time slices for data cubes, and visibility toggles for overlays—all coordinated through `reconcileBoundLayers`.

### What is the concurrency limit for pixel time series queries?

The plugin limits parallel GeoTIFF reads to **6 concurrent requests** (`READ_CONCURRENCY`) to prevent browser connection pool exhaustion while maintaining reasonable query performance.

### Where is the Time Slider plugin configuration persisted?

The `deactivate` handler saves the control's configuration to a `savedState` store keyed by `TIME_SLIDER_KEY`. This restores the timeline position, playback settings, and source list on next activation.