How to Query Files Within the GeoLibre Project: Zarr Raster API Guide
Use the queryZarrLayer function from @geolibre/plugins to read values from Zarr raster layers by passing a layer ID and GeoJSON geometry (Point for clicks, Polygon for regions).
GeoLibre is an open-source geospatial monorepo combining a React frontend, Tauri desktop shell, and Python FastAPI sidecar. When you need to query file contents—specifically values from cloud-optimized Zarr raster layers—the project exposes a unified API through its Zustand-based state management system. This guide walks through the complete query workflow as implemented in the opengeos/GeoLibre source code.
The queryZarrLayer API
All Zarr querying flows through the high-level queryZarrLayer function, exported from the plugins package.
Function Signature
As defined in packages/plugins/src/plugins/maplibre-components.ts (lines 33–45):
export async function queryZarrLayer(
layerId: string,
geometry: QueryGeometry,
selector?: Selector,
options?: QueryOptions,
): Promise<QueryResult | null>
Parameter Reference
| Parameter | Type | Description |
|---|---|---|
layerId |
string |
Identifier returned by addZarrRasterLayer when the layer was registered |
geometry |
GeoJSON.Point or Polygon/MultiPolygon |
Query region in WGS-84 coordinates [lng, lat] |
selector |
Selector (optional) |
Dimension overrides, e.g., { time: 12 } to read a non-current time slice |
options |
QueryOptions (optional) |
Configuration including AbortSignal and includeSpatialCoordinates flag |
Implementation Details
The function performs a lookup in the internal zarrControl map and delegates to the layer's queryData method. This design means:
- Plugins do not need to import the low-level
@carbonplan/zarr-layerreader directly - CRS reprojection is handled once by the renderer, avoiding duplicated coordinate transformations
- Missing layers or layers without
queryDataresolve tonullrather than throwing
Query Types and Code Examples
Single-Point Value Queries (Click-to-Value)
Fetch the raster value at a specific map coordinate:
import { queryZarrLayer } from "@geolibre/plugins";
async function getValueAtClick(layerId: string, lng: number, lat: number) {
const point = { type: "Point", coordinates: [lng, lat] as [number, number] };
const result = await queryZarrLayer(layerId, point);
console.log("Raster values at click:", result?.values);
}
The result.values array contains the raw cell value(s). For multi-band rasters, this contains one value per band.
Polygon Region Statistics
Aggregate values across an area of interest:
async function getStatsForPolygon(layerId: string, polygonGeoJSON: GeoJSON.Polygon) {
const result = await queryZarrLayer(layerId, polygonGeoJSON);
const values = result?.values ?? [];
// Compute your own statistics—the API returns raw pixel arrays
const mean = values.reduce((a, b) => a + b, 0) / values.length;
console.log(`Mean value across ${values.length} pixels:`, mean);
}
The renderer performs spatial masking and returns only valid data cells. Empty arrays indicate the polygon falls outside raster bounds or intersects only no-data cells.
Time-Dimension Override Queries
Read data from a specific time step without changing the visible layer state:
async function readTimeSlice(
layerId: string,
point: GeoJSON.Point,
timeStep: number
) {
const selector = { time: timeStep };
const result = await queryZarrLayer(layerId, point, selector);
console.log(`Values at time index ${timeStep}:`, result?.values);
}
This bypasses the Time Slider component's current position while maintaining the same spatial query geometry.
Cancellable Queries with AbortSignal
Prevent stale results during rapid user interactions:
async function queryWithAbort(layerId: string, geometry: QueryGeometry) {
const controller = new AbortController();
const resultPromise = queryZarrLayer(layerId, geometry, undefined, {
signal: controller.signal,
});
// Abort after timeout or user action
setTimeout(() => controller.abort(), 2000);
try {
const result = await resultPromise;
return result;
} catch (err) {
if (err.name === 'AbortError') {
console.log("Query cancelled by user");
}
throw err;
}
}
Cancelled queries reject with an AbortError. Always handle this case in UI components to prevent error state displays.
Architecture: How Queries Flow Through GeoLibre
Understanding the data flow helps debug failed queries and extend the system.
Step 1: Layer Registration
When you add a Zarr raster via addZarrRasterLayer, the function:
- Creates a
ZarrLayerinstance with the configured Zarr store URL - Registers it in the Zustand store (
@geolibre/core) - Adds it to the internal
zarrControlMap accessible to plugins
Step 2: Renderer Initialization
The ZarrLayer renderer:
- Loads initial data chunks asynchronously
- Builds internal coordinate transformation pipelines
- Exposes the
queryDatamethod used byqueryZarrLayer
Important: Queries during initial chunk loading return empty arrays rather than errors. Check result.values.length to distinguish "no data" from "not yet loaded."
Step 3: Temporal Adapter Synchronization
For time-enabled cubes, the registerZarrTemporalAdapter function (same source file) connects the Time Slider to the Zarr time axis. It reads the private dimensionValues property via readZarrDimensionValues, ensuring the UI slider matches available time steps without exposing Zarr internals to React components.
Error Handling and Edge Cases
GeoLibre's query implementation has specific behaviors for edge conditions:
- Out-of-bounds geometry: Returns
QueryResultwith emptyvaluesarray - Layer not found: Resolves to
null - Layer loading: Empty
valuesuntil first chunks arrive - Missing data cells: Omitted from
valuesarray (sparse representation) - Aborted request: Promise rejects with
AbortError
Always validate result is non-null before accessing properties, and handle empty values arrays appropriately in your UI.
Source File Reference
| File | Purpose |
|---|---|
packages/plugins/src/plugins/maplibre-components.ts |
queryZarrLayer implementation and Zarr utilities |
packages/plugins/src/types.ts |
TypeScript definitions for QueryResult, QueryGeometry, Selector |
apps/geolibre-desktop/src/hooks/usePlugins.ts |
React hook integrating plugin API into UI components |
tests/zarr-layer-api.test.ts |
Test coverage for query behavior and edge cases |
docs/architecture.md |
Monorepo structure and state management overview |
Summary
- Import
queryZarrLayerfrom@geolibre/pluginsfor all Zarr raster queries - Pass WGS-84 GeoJSON geometries—Points for single values, Polygons for regions
- Use the selector parameter to read non-current time slices without UI changes
- Include an AbortSignal in
optionsfor cancellable queries in responsive UIs - Check for
nullresults (missing layer) and emptyvaluesarrays (out of bounds or loading) - The renderer handles all CRS transformations—don't duplicate this work by accessing
@carbonplan/zarr-layerdirectly
Frequently Asked Questions
What coordinate system does queryZarrLayer expect?
All geometries must be supplied in WGS-84 (EPSG:4326) with longitude-latitude coordinate order: [lng, lat]. The renderer internally reprojects to the raster's native CRS. Passing projected coordinates produces incorrect query locations.
Why does my query return null instead of values?
queryZarrLayer returns null when the layerId does not exist in the internal zarrControl map. Verify the layer was successfully added via addZarrRasterLayer and that you're using the exact ID string returned by that function.
How do I query a specific time step without changing the visible layer?
Pass a selector object as the third argument: await queryZarrLayer(id, geometry, { time: 5 }). The time index is zero-based and corresponds to the Zarr array's time dimension. The visible layer state remains unchanged.
Can I query multiple rasters simultaneously?
The API handles one layer per call. For concurrent queries, use Promise.all with separate queryZarrLayer invocations. Each maintains its own AbortSignal for independent cancellation control.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →