How to Debug Issues in GeoLibre: A Step-by-Step Guide for the Open Source Geospatial Library
Use the browser console to inspect the Zustand store via useAppStore.getState() and the MapLibre-GL instance via window.mapController.getMap() to quickly isolate state versus rendering bugs in GeoLibre.
GeoLibre is an open-source geospatial visualization library built as an npm workspaces monorepo. Effective debugging requires understanding two core pillars: the Zustand state store that manages all UI and project state, and the MapController that wraps MapLibre-GL for map rendering. This guide walks you through proven techniques to debug issues in GeoLibre based on its actual source code architecture.
Understanding GeoLibre's Debug Architecture
Before diving into specific techniques, recognize that GeoLibre splits responsibilities cleanly between state management and map rendering. Most bugs fall into one of three categories:
- Store-related: UI state, layer visibility, project metadata, or undo/redo history
- Map-related: Basemap loading, layer syncing, story-map playback, or control visibility
- Integration bugs: State and map becoming out of sync
The following sections target each area with concrete commands and file references.
Debugging the Zustand Store (useAppStore)
The central store lives in @geolibre/core/src/store.ts and holds all application state through the useAppStore hook. This is your first stop for most debugging scenarios.
Inspect Store State in the Console
Dump the complete state to verify data integrity:
// Browser console command
useAppStore.getState()
For filtered inspection, target specific slices:
// Check only layers with visibility = false
const state = useAppStore.getState();
const hiddenLayers = state.layers.filter(l => !l.visible);
console.table(hiddenLayers.map(l => ({ id: l.id, name: l.name })));
Verify Undo/Redo History
GeoLibre uses zundo for temporal state management. Check the history stack when bugs appear after multiple edits:
const past = useAppStore.getState().temporal.getState().pastStates;
console.table(past.map(s => s.projectName));
The temporal wrapper is defined at lines 5-11 in packages/core/src/store.ts, with pruneHistoryBySize available to prevent memory bloat.
Test Actions Directly
Isolate whether a bug is store-related by calling actions manually:
// Toggle basemap visibility
useAppStore.getState().setBasemapVisible(false);
// Update collaboration presence
useAppStore.getState().updateCollaborationPresence({ cursor: { x: 100, y: 200 }});
If the action works in console but fails in UI, the bug likely involves the component layer, not the store itself.
Debugging the MapController
The MapController in @geolibre/map/src/map-controller.ts manages the MapLibre-GL instance and synchronizes it with store state. Access it globally via window.mapController in development builds.
Access the MapLibre Instance
const map = window.mapController?.getMap();
console.log(map.getStyle().layers.map(l => l.id));
This reveals all active layers, sources, and paint properties currently rendered.
Force Layer Synchronization
When layers exist in the store but don't appear on the map, manually trigger a sync:
window.mapController?.syncLayers(useAppStore.getState().layers);
The syncLayers method (lines 120-130 in map-controller.ts) handles the complex logic of diffing store layers against MapLibre layers.
Debug Basemap Issues
Planetary and regional basemaps use sentinel URLs that resolveMapStyle expands. Test a planetary switch manually:
import { useAppStore } from '@geolibre/core';
const moonSentinel = 'geolibre://basemap/planet/Moon';
useAppStore.getState().setBasemapStyleUrl(moonSentinel);
window.mapController?.setStyle(moonSentinel);
If this fails, check console warnings—unrecognized sentinels fall back to DEFAULT_BASEMAP with a warning (lines 81-89).
Test Story-Map Playback
Verify story-map features independently of the UI:
// Set layer opacity for a story chapter
window.mapController?.setStoryLayerOpacity('layer-id', 0.2);
// Apply camera from a story chapter
window.mapController?.applyStoryChapterCamera(chapterConfig);
These methods at lines 740-770 let you isolate whether story-map bugs are in the playback logic or the chapter data itself.
Common Debugging Workflow
Follow this systematic approach to debug issues in GeoLibre efficiently:
-
Start the dev server
npm run dev # http://localhost:5173 -
Reproduce and pause
Trigger the bug, then open DevTools without reloading.
-
Inspect store state
Run
useAppStore.getState()and verify the relevant data exists and is correctly structured. -
Check MapLibre state
Run
window.mapController?.getMap().getStyle()and confirm layers/sources match the store. -
Trace the execution
Add
console.trace()to store actions or use debugger breakpoints in:packages/core/src/store.tsfor state mutationspackages/map/src/map-controller.tsfor map operations
-
Run targeted tests
# Frontend unit tests npm run test:frontend # Python processing tests npm run test:backend # Full UI regression npm run test:e2e
Symptom-Based Debugging Reference
| Symptom | Likely Location | Key File/Function |
|---|---|---|
| Layer missing after grid resize | Store grid logic | store.ts → setMapGrid |
| Basemap won't switch to planet | Sentinel resolution | map-controller.ts → applyPlanetaryBasemap |
| Undo/redo consumes too much memory | History pruning | store.ts → pruneHistoryBySize |
| Story opacity not resetting | Style restoration | map-controller.ts → restoreLayerStyles |
| Plugin raster missing in export | Source detection | map-controller.ts → getLayerRasterSource |
| Control (compass, terrain) not appearing | DOM management | map-controller.ts → setBuiltInControlVisible |
Advanced Techniques
Override Network Requests
Capture failing Mapbox style fetches for debugging:
const originalFetch = window.fetch;
window.fetch = async (input, init) => {
if (String(input).includes('mapbox')) {
console.log('Mapbox request:', input);
}
return originalFetch(input, init);
};
Enable React DevTools Inspection
The useAppStore hook appears in React DevTools under Hooks → useAppStore, providing a visual tree of state that updates in real time as you interact with the application.
Run Single Test with Coverage
For rapid iteration on specific logic:
node --import tsx --test tests/layer-control-order.test.ts
The coverage floor enforced by test:frontend:coverage ensures your debugging changes remain tested.
Summary
- Start with the store:
useAppStore.getState()reveals all UI and project state; actions can be called directly to isolate bugs - Verify MapLibre sync:
window.mapController.getMap()exposes the rendered map state;syncLayersforces reconciliation - Use sentinel debugging: Planetary basemap failures log warnings and fall back to defaults—check
resolveMapStyle - Leverage the test suite:
npm run test:frontend,test:backend, andtest:e2eprovide regression safety - Reference exact line numbers: The source files
packages/core/src/store.tsandpackages/map/src/map-controller.tscontain the definitive implementation
Frequently Asked Questions
How do I access the GeoLibre store outside of React components?
The useAppStore export from @geolibre/core exposes a getState() method callable from anywhere, including the browser console. Import the store in your component or access it globally in development to inspect or manipulate state directly without React re-renders.
Why isn't my layer appearing on the map even though it's in the store?
The store and MapLibre can drift out of sync. Call window.mapController?.syncLayers(useAppStore.getState().layers) to force synchronization. If this fixes the issue, the bug is in the reactive subscription chain—likely in the component that should trigger sync on layer changes.
How do I debug GeoLibre's undo/redo functionality?
Access the temporal state via useAppStore.getState().temporal.getState(). The pastStates and futureStates arrays contain full state snapshots. Use console.table(pastStates.map(s => s.projectName)) to inspect history, and check pruneHistoryBySize in store.ts if memory usage grows unexpectedly.
Where should I add logging when debugging GeoLibre basemap errors?
Add console.log or console.warn in packages/map/src/map-controller.ts within resolveMapStyle (lines 63-92) and applyPlanetaryBasemap. Unknown sentinel URLs trigger warnings at lines 81-89, making this the ideal interception point for basemap debugging.
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 →