# How to Configure MapLibre Layer Synchronization with deck.gl Raster Overlays in GeoLibre

> Learn how to configure MapLibre layer synchronization with deck.gl raster overlays in GeoLibre. Achieve seamless integration and consistent view states for your geospatial applications.

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

---

**GeoLibre uses a single shared interleaved deck.gl overlay to synchronize MapLibre GL JS base maps with deck.gl raster overlays, ensuring all layers coexist without overwriting each other while maintaining consistent view state.**

GeoLibre is an open-source geospatial framework that combines **MapLibre GL JS** for base map rendering with **deck.gl** for advanced 3-D and raster visualizations. Configuring MapLibre layer synchronization with deck.gl raster overlays requires a specific architectural pattern implemented in [`packages/plugins/src/plugins/shared-deck-overlay.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/shared-deck-overlay.ts) to prevent layer conflicts and ensure proper draw order.

## Understanding the Shared Overlay Architecture

The core challenge is that deck.gl only allows one interleaved `MapboxOverlay` per map instance (accessed via `map.__deck`). Adding a second overlay replaces the existing layer list, causing previously added layers to disappear. GeoLibre solves this by funneling every producer—raster controls, Google Photorealistic 3D Tiles, and vector visualizations—through a single shared overlay.

The system maintains a strict **source draw order** defined in the `SOURCE_DRAW_ORDER` constant (line 35 of [`shared-deck-overlay.ts`](https://github.com/opengeos/GeoLibre/blob/main/shared-deck-overlay.ts)). Layers render from bottom to top as follows: raster layers, Google 3D tiles, deck.gl visualizations, and finally route animations. This mirrors standard GIS stacking principles and guarantees raster backgrounds remain beneath 3-D structures.

## Core Implementation in shared-deck-overlay.ts

### Layer Aggregation and Ordering

The overlay aggregates layers from multiple sources using the `setSharedDeckLayers(source, layers)` function (lines 44-48). Each producer registers its layers under a specific `SharedDeckSource` key such as `"raster"`, `"google-3d-tiles"`, or `"deckviz"`. The `aggregatedLayers()` function (lines 73-79) merges these arrays according to `SOURCE_DRAW_ORDER` before passing them to the overlay.

### Lazy Mounting and Retry Logic

The overlay initializes lazily via `renderSharedDeckOverlay()` (lines 82-99). It only mounts when at least one layer exists, and includes a retry mechanism (`scheduleMountRetry`, lines 108-126) that attempts reconnection for approximately two seconds if the map isn't ready, preventing race conditions during startup.

### GPU Device Sharing

deck.gl's Luma GPU device is exposed to other producers through `onSharedDeckDevice` (lines 59-70). This callback immediately invokes listeners if the device exists and re-invokes on every map re-bind, enabling efficient texture allocation for raster controls without creating duplicate GPU contexts.

## Step-by-Step Implementation Guide

### Initialize the Shared Overlay

Before adding any raster layers, ensure the shared overlay exists. This function is idempotent and serializes concurrent calls via `ensureInFlight`.

```typescript
import { ensureSharedDeckOverlay } from "./shared-deck-overlay";

// app is the GeoLibreAppAPI passed to every plugin
await ensureSharedDeckOverlay(app);

```

### Register Raster Layers

Create your deck.gl raster layer and register it under the `"raster"` source key. This places it at the bottom of the layer stack.

```typescript
import type { Layer } from "@deck.gl/core";
import { setSharedDeckLayers, SharedDeckSource } from "./shared-deck-overlay";

const rasterLayer: Layer = {
  type: "TileLayer",
  data: "https://my.bucket/tiles/{z}/{x}/{y}.tif",
  // Additional configuration (opacity, bounds, etc.)
};

setSharedDeckLayers("raster" as SharedDeckSource, [rasterLayer]);

```

### Update or Remove Layers Dynamically

Replace existing raster layers or clear them completely without affecting other deck.gl content.

```typescript
// Update with new data
setSharedDeckLayers("raster" as SharedDeckSource, [newRasterLayer]);

// Remove all raster layers
setSharedDeckLayers("raster" as SharedDeckSource, []);

```

### Access the Shared GPU Device

For custom texture creation or advanced GPU operations, subscribe to the shared Luma device.

```typescript
import { onSharedDeckDevice } from "./shared-deck-overlay";

const unsubscribe = onSharedDeckDevice((device) => {
  // device is the LumaGPUDevice for texture allocation
  console.log("Shared deck device ready:", device);
});

// Cleanup when component unmounts
unsubscribe();

```

## Complete Plugin Example

Below is a minimal plugin structure that implements a raster overlay using the shared deck.gl system.

```typescript
import type { GeoLibrePlugin } from "../types";
import { ensureSharedDeckOverlay, setSharedDeckLayers } from "./shared-deck-overlay";

export const RASTER_PLUGIN_ID = "custom-raster-plugin";

export const customRasterPlugin: GeoLibrePlugin = {
  id: RASTER_PLUGIN_ID,
  name: "Custom Raster Overlay",
  version: "1.0.0",
  activeByDefault: false,
  
  activate: async (app) => {
    await ensureSharedDeckOverlay(app);
    
    const layer = {
      type: "TileLayer",
      data: "https://example.com/cog/{z}/{x}/{y}.tif",
      opacity: 0.8
    };
    
    setSharedDeckLayers("raster", [layer]);
  },
  
  deactivate: () => {
    // Clean up to prevent memory leaks
    setSharedDeckLayers("raster", []);
  },
};

```

## Summary

- **Single shared overlay**: GeoLibre uses one interleaved `MapboxOverlay` instance in [`shared-deck-overlay.ts`](https://github.com/opengeos/GeoLibre/blob/main/shared-deck-overlay.ts) to prevent layer overwriting and ensure all producers share the same GPU context.
- **Defined draw order**: The `SOURCE_DRAW_ORDER` constant guarantees raster layers render beneath 3-D tiles and vector visualizations.
- **Layer aggregation**: Use `setSharedDeckLayers()` with source keys (`"raster"`, `"deckviz"`, etc.) and let `aggregatedLayers()` handle the merge.
- **Lazy initialization**: The overlay mounts only when needed with automatic retry logic for map readiness.
- **GPU efficiency**: Access the shared Luma device via `onSharedDeckDevice()` for texture operations without context duplication.

## Frequently Asked Questions

### Why can't I create multiple deck.gl overlays on the same MapLibre map?

deck.gl's `MapboxOverlay` is designed to interleave with the base map using a single instance attached to `map.__deck`. Creating a second overlay replaces the first one's layer list, causing previously added layers to disappear. GeoLibre's shared overlay architecture prevents this by aggregating all layers through one central interface.

### How do I control the stacking order of raster overlays relative to 3-D tiles?

Register your raster layers with the `"raster"` source key. The `aggregatedLayers()` function in [`shared-deck-overlay.ts`](https://github.com/opengeos/GeoLibre/blob/main/shared-deck-overlay.ts) (lines 73-79) automatically orders layers according to the `SOURCE_DRAW_ORDER` array: raster at the bottom, followed by Google 3D tiles, then deck.gl visualizations. To change the order, modify the constant in the source file.

### What happens if the map isn't ready when I try to add layers?

The `renderSharedDeckOverlay()` function includes a retry mechanism (`scheduleMountRetry`, lines 108-126) that attempts to mount the overlay for approximately two seconds if `addMapControl` fails. This handles race conditions during map initialization without throwing errors.

### Can I access deck.gl's GPU device for custom texture operations?

Yes. Use the `onSharedDeckDevice()` callback (lines 59-70) to receive the Luma GPU device instance. The callback fires immediately if the device exists and re-fires on every map re-bind, ensuring your texture allocations stay synchronized with the active deck.gl context.