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:

  1. Start the dev server

    npm run dev  # http://localhost:5173
    
  2. Reproduce and pause

    Trigger the bug, then open DevTools without reloading.

  3. Inspect store state

    Run useAppStore.getState() and verify the relevant data exists and is correctly structured.

  4. Check MapLibre state

    Run window.mapController?.getMap().getStyle() and confirm layers/sources match the store.

  5. Trace the execution

    Add console.trace() to store actions or use debugger breakpoints in:

  6. 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; syncLayers forces 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, and test:e2e provide regression safety
  • Reference exact line numbers: The source files packages/core/src/store.ts and packages/map/src/map-controller.ts contain 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:

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 →