How to Integrate External Data Sources with GeoLibre: Overture Maps, Planetary Computer, and Earth Engine

Integrate any external raster service into GeoLibre by implementing the TimelapseProvider interface, which converts remote tile URLs into map layers through the Zustand store and MapLibre sync layer.

GeoLibre is architected as a plugin‑driven, store‑driven application where all map data flows through GeoLibreLayer records in the central Zustand store. This design makes it straightforward to bring in external imagery from sources like Google Earth Engine, Microsoft Planetary Computer, or Overture Maps. The integration pattern is consistent across providers: generate tile‑URL templates, wrap them in TimelapseFrame objects, and register a provider that the Timelapse control consumes automatically.

Understanding the Layer Architecture

Every raster or vector layer that appears on the map originates as a GeoLibreLayer in the store. The packages/map/src/MapController.ts file handles synchronizing these store records to actual MapLibre sources through its syncLayers method. For external raster services, you don't manipulate the store directly—instead, you implement a provider that generates frame objects containing tile URL templates.

The TimelapseProvider interface in packages/plugins/src/plugins/timelapse-providers.ts defines the contract:

  • id – unique provider identifier
  • name – human‑readable label shown in the UI picker
  • attribution – credit string rendered on the map
  • listFrames() – async method returning TimelapseFrame[] with tile URLs, zoom limits, and metadata

When a user selects a frame, the Timelapse control instantiates a raster source using the tileUrlTemplate and adds it to the map through the standard store → MapController → MapLibre pipeline.

Integrating Google Earth Engine

Earth Engine exposes raster data through getMapId() (or getTileUrl()), which returns a tile template string containing {z}/{x}/{y} placeholders. Your provider wraps these templates into frames.

import { TimelapseProvider, TimelapseFrame } from "./timelapse-providers";
import { registerTimelapseProvider } from "./timelapse-providers";

/** Fetches a tile URL template for a specific year from EE. */
async function eeTileUrl(year: number): Promise<string> {
  const collection = ee.ImageCollection("MODIS/061/MOD13A2")
    .filter(ee.Filter.calendarRange(year, year, "year"));
  const image = collection.first();
  const { tileUrl } = image.getMap({ format: "png" });
  // Returns: "https://earthengine.googleapis.com/map/{mapid}/{z}/{x}/{y}?token=..."
  return tileUrl;
}

const earthEngineProvider: TimelapseProvider = {
  id: "ee-modis-ndvi",
  name: "Google Earth Engine NDVI",
  attribution: "© Google Earth Engine",
  async listFrames() {
    const years = [2018, 2019, 2020, 2021];
    const frames: TimelapseFrame[] = await Promise.all(
      years.map(async (y) => ({
        id: `ee-modis-${y}`,
        label: `${y}`,
        year: y,
        tileUrlTemplate: await eeTileUrl(y),
        attribution: `NDVI ${y} via Google Earth Engine`,
        minzoom: 0,
        maxzoom: 12,
      }))
    );
    return frames;
  },
};

registerTimelapseProvider(earthEngineProvider);

The earth-engine-auth.ts file in the same directory handles OAuth authentication and reports availability to the UI, so your provider can assume an authenticated EE client is present when registered.

Integrating Microsoft Planetary Computer

Planetary Computer serves imagery through its STAC API. You query for collections, extract asset URLs, and construct tile templates. The signed URLs returned by Planetary Computer typically include the {z}/{x}/{y} structure directly.

import { TimelapseProvider, TimelapseFrame } from "./timelapse-providers";
import { registerTimelapseProvider } from "./timelapse-providers";

const PC_SEARCH_URL = "https://planetarycomputer.microsoft.com/api/stac/v1/search";

async function fetchPcMosaic(year: number): Promise<string> {
  const body = {
    collections: ["sentinel-2-l2a"],
    datetime: `${year}-01-01/${year}-12-31`,
    limit: 1,
    query: { "eo:cloud_cover": { lt: 0.1 } },
  };
  const resp = await fetch(PC_SEARCH_URL, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });
  const json = await resp.json();
  const asset = json.features[0].assets["visual"];
  // Signed href contains z/x/y placeholders ready for MapLibre
  return asset.href;
}

const planetaryComputerProvider: TimelapseProvider = {
  id: "pc-sentinel2",
  name: "Planetary Computer Sentinel-2",
  attribution: "© Microsoft Planetary Computer",
  async listFrames() {
    const years = [2020, 2021, 2022];
    const frames: TimelapseFrame[] = await Promise.all(
      years.map(async (y) => ({
        id: `pc-s2-${y}`,
        label: `${y}`,
        year: y,
        tileUrlTemplate: await fetchPcMosaic(y),
        attribution: `Sentinel-2 ${y} via Planetary Computer`,
        minzoom: 0,
        maxzoom: 14,
      }))
    );
    return frames;
  },
};

registerTimelapseProvider(planetaryComputerProvider);

Because Planetary Computer returns signed URLs with embedded tokens, each frame carries a complete, authenticated tile template that requires no additional request headers.

Integrating Overture Maps and Other Tile Sources

Overture Maps distributes vector tiles through AWS S3 or CDN endpoints. For raster‑style integration, you can:

  • Create a vector tile provider – Implement a variant of TimelapseProvider that returns vector type sources instead of raster, pointing to Overture's {z}/{x}/{y}.pbf endpoints
  • Use a raster proxy – Pre‑render Overture data into raster tiles, then follow the standard provider pattern above

The source-coop-api.ts file demonstrates how to structure catalog‑based providers that query external APIs before generating frames. Adapt this pattern for Overture's partition‑based data layout:

  1. Query Overture's release manifest for available themes (buildings, places, transportation)
  2. Generate one TimelapseFrame per theme or version
  3. Point tileUrlTemplate to the appropriate tile endpoint (e.g., https://overturemaps-tiles.s3.amazonaws.com/2024-04-16/buildings/{z}/{x}/{y}.pbf)

Registering and Activating Your Provider

The provider lifecycle follows three stages:

  1. Import and register – Your provider file imports registerTimelapseProvider from timelapse-providers.ts and calls it at module initialization
  2. UI discovery – TimelapseControl.tsx reads the timelapseProviders registry and populates the provider picker dropdown
  3. Frame activation – On user selection, listFrames() executes, and the returned frames populate the year slider. Selecting a frame triggers MapController.ts → syncLayers to create the MapLibre raster source

Ensure your provider module is imported early—typically in packages/plugins/src/index.ts or equivalent—to guarantee registration before the Timelapse UI mounts.

Handling Custom Formats and Size Limits

Remote data integration must respect GeoLibre's format classification system. The remote-file-formats.ts file centralizes:

  • Format detection – Mapping file extensions to processing strategies
  • Size limits – Enforcing DuckDB and memory constraints for downloaded data

If your external source uses a non‑standard extension or URL pattern, extend the RemoteFileFormat union type and add detection logic in remote-file-formats.ts. This ensures consistent behavior for size‑checking and streaming limits across all remote sources.

Summary

  • Implement TimelapseProvider – Define id, name, attribution, and listFrames() to generate tile‑URL templates for any external service
  • Wrap Earth Engine – Use getMap() or getTileUrl() to obtain authenticated tile templates, then return them as TimelapseFrame objects
  • Query Planetary Computer – Hit the STAC API, extract signed asset URLs, and convert to frames with appropriate zoom limits
  • Register early – Call registerTimelapseProvider() at module load so TimelapseControl.tsx discovers your source
  • Extend formats if needed – Add new URL patterns to remote-file-formats.ts to maintain consistent size and streaming behavior

Frequently Asked Questions

What file should I modify to add a new external data provider?

Create a new TypeScript file in packages/plugins/src/plugins/ that implements TimelapseProvider, then import and register it in the package index. The timelapse-providers.ts file defines the interface and registry, while source-coop-api.ts provides a working template for API‑based providers.

Does GeoLibre support vector tiles from Overture Maps?

Yes. The provider pattern accepts any MapLibre‑compatible source. For vector tiles, return a TimelapseFrame with a URL template pointing to .pbf endpoints and ensure your consuming code sets the source type to vector. You may need to extend MapController.ts logic if you require custom layer styling beyond the default raster handling.

How does authentication work for Earth Engine?

The earth-engine-auth.ts plugin manages OAuth flow and exposes an authenticated ee client globally. Your provider can assume this client is initialized when listFrames() is called. For production deployments, ensure the token refresh logic in earth-engine-auth.ts remains active throughout the session.

Can I combine multiple external sources in one provider?

Yes. A single TimelapseProvider can return frames from heterogeneous sources—mix Earth Engine, Planetary Computer, and custom endpoints. Each TimelapseFrame carries its own tileUrlTemplate and attribution, so the map renders them uniformly through the same syncLayers pipeline.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →