# How the .geolibre.json Project Format Stores Layer and View State in GeoLibre

> Discover how the .geolibre.json format stores GeoLibre project state, including map view and layer definitions, within a single JSON document.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: internals
- Published: 2026-08-01

---

**The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) format captures all workspace state—including map camera position and every layer definition—in a single JSON document with top-level `mapView` and `layers` fields.**

GeoLibre's native project format provides complete workspace serialization, enabling users to save and reopen complex mapping projects exactly as configured. This article examines how the [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) format structures **view state** (camera position and multi-pane layouts) and **layer state** (data sources, visibility, and styling) based on the opengeos/GeoLibre source code.

## Map View State: Primary and Secondary Panes

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) format distinguishes between a primary map view and optional secondary panes for split-screen or multi-view layouts.

### Primary Map View (`mapView`)

The top-level `mapView` field stores the main camera state as a **MapViewState** object. According to [`docs/project-format.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/project-format.md), this includes:

- `center`: [longitude, latitude] array
- `zoom`: zoom level number
- `bearing`: rotation in degrees (0 = north up)
- `pitch`: tilt angle in degrees (0 = top-down view)
- `bbox`: optional bounding box override

The TypeScript definition in **[`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts)** (lines 1005-1020) formalizes this structure:

```typescript
// MapViewState definition in packages/core/src/types.ts
interface MapViewState {
  center: [number, number];
  zoom: number;
  bearing?: number;
  pitch?: number;
  bbox?: [number, number, number, number]; // [minX, minY, maxX, maxY]
}

```

### Secondary Map Views (`secondaryMapViews`)

For multi-pane layouts, the `secondaryMapViews` array contains **SecondaryMapView** objects. Each secondary pane maintains independent camera state plus layout-specific overrides:

- `id`: unique pane identifier
- `view`: nested `MapViewState` (same shape as primary)
- `label`: optional display name for the pane
- `viewKind`: renderer type (`"maplibre"` or `"cesium"` for 2-D/3-D)
- `layerVisibility`: per-layer visibility overrides keyed by layer ID

The **SecondaryMapView** interface appears in **[`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts)** (lines 1036-1053):

```typescript
// SecondaryMapView definition in packages/core/src/types.ts
interface SecondaryMapView {
  id: string;
  view: MapViewState;
  label?: string;
  viewKind?: 'maplibre' | 'cesium';
  layerVisibility?: Record<string, boolean>;
}

```

The Zustand store in **[`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts)** (lines 340-365) manages runtime synchronization between these views and their JSON serialization.

## Layer State: The `layers` Array

Every data layer in a GeoLibre project is represented as an object in the top-level `layers` array. The schema documented in **[`docs/project-format.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/project-format.md)** (lines 45-74) specifies:

| Property | Purpose |
|----------|---------|
| `id` | Unique layer identifier (referenced by `layerVisibility` overrides) |
| `name` | Human-readable display name |
| `type` | Source type: `geojson`, `xyz`, `wms`, `vector`, `raster`, etc. |
| `source` | Type-specific source configuration |
| `visible` | Boolean visibility flag |
| `opacity` | Alpha value 0-1 |
| `style` | Renderer-specific symbology (colors, widths, labels) |
| `geojson` | Inline GeoJSON data (for `geojson` type) |
| `sourcePath` | External file reference |
| `refreshInterval` | Optional auto-refresh timing in seconds |
| `joins` | Attribute join configurations |

Each layer's complete definition—including inline data, external references, and styling—preserves enough information to reconstruct the visualization without additional context.

## Complete Project Examples

### Single-Pane Project with GeoJSON Layer

```json
{
  "version": "0.1.0",
  "name": "Sample Project",
  "mapView": {
    "center": [-122.4194, 37.7749],
    "zoom": 11,
    "bearing": 0,
    "pitch": 45
  },
  "basemapStyleUrl": "",
  "basemapVisible": true,
  "basemapOpacity": 1,
  "layers": [
    {
      "id": "layer-1",
      "name": "San Francisco Roads",
      "type": "geojson",
      "source": { "type": "geojson" },
      "visible": true,
      "opacity": 1,
      "style": {
        "fillColor": "#3b82f6",
        "strokeColor": "#1e40af",
        "strokeWidth": 2,
        "fillOpacity": 0.6
      },
      "geojson": { "type": "FeatureCollection", "features": [] },
      "sourcePath": "/data/sf-roads.geojson"
    }
  ],
  "styles": {},
  "plugins": {}
}

```

### Split-Screen Layout with Per-Pane Visibility Overrides

```json
{
  "version": "0.1.0",
  "name": "Split-Screen Demo",
  "mapView": { "center": [0, 0], "zoom": 2, "bearing": 0, "pitch": 0 },
  "basemapStyleUrl": "",
  "basemapVisible": true,
  "layers": [
    {
      "id": "layer-1",
      "name": "Global Roads",
      "type": "geojson",
      "source": { "type": "geojson" },
      "visible": true,
      "opacity": 1,
      "style": { "strokeColor": "#ef4444", "strokeWidth": 1 }
    }
  ],
  "secondaryMapViews": [
    {
      "id": "pane-2",
      "view": { "center": [0, 0], "zoom": 5, "bearing": 0, "pitch": 0 },
      "label": "Zoomed-In",
      "viewKind": "maplibre",
      "layerVisibility": { "layer-1": false }
    }
  ],
  "styles": {},
  "plugins": {}
}

```

In this configuration, the secondary pane (`pane-2`) displays a zoomed-in view with the road layer hidden, demonstrating how `layerVisibility` overrides global layer settings per pane.

## Key Implementation Files

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) persistence layer spans several packages in the opengeos/GeoLibre repository:

- **[`docs/project-format.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/project-format.md)**: Authoritative schema documentation for all project fields
- **[`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts)**: TypeScript contracts for `MapViewState`, `SecondaryMapView`, and related interfaces
- **[`packages/core/src/store.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/store.ts)**: Zustand store handling runtime state and JSON serialization (lines 340-365 for secondary views)
- **[`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts)**: MapLibre GL JS integration for applying `MapViewState`
- **[`packages/map/src/cesium-camera.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-camera.ts)**: Parallel Cesium implementation for 3-D globe views

## Summary

The [`.geolibre.json`](https://github.com/opengeos/GeoLibre/blob/main/.geolibre.json) project format achieves complete workspace serialization through:

- **Primary view state** in the top-level `mapView` field with center, zoom, bearing, pitch, and optional bbox
- **Secondary view states** in the `secondaryMapViews` array, supporting multi-pane layouts with independent cameras and per-pane layer visibility
- **Layer definitions** in the `layers` array, capturing source configuration, visibility, opacity, styling, and data references
- **Renderer flexibility** through `viewKind` selection between MapLibre 2-D and Cesium 3-D on a per-pane basis

This structure enables GeoLibre to restore exact visual state across sessions while remaining human-readable and version-controllable.

## Frequently Asked Questions

### What is the maximum number of secondary map views supported in a .geolibre.json file?

The `secondaryMapViews` field is defined as an array with no hardcoded limit in the TypeScript types. Practical constraints depend on browser performance and available screen real estate for rendering multiple map instances simultaneously.

### How does GeoLibre handle layer visibility when a secondary pane has no visibility override specified?

Per the `SecondaryMapView` interface definition, `layerVisibility` is optional. When omitted or when a layer ID is not present in the record, the secondary pane inherits the global layer visibility state from the main `layers` array.

### Can different secondary panes use different rendering engines in the same project?

Yes. The `viewKind` property on each `SecondaryMapView` accepts `"maplibre"` or `"cesium"` independently. This allows mixed 2-D and 3-D visualization within a single project file, with **[`packages/map/src/map-controller.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/map-controller.ts)** and **[`packages/map/src/cesium-camera.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/map/src/cesium-camera.ts)** handling their respective renderers.

### Where is the ground truth for the .geolibre.json schema specification?

The authoritative documentation resides in **[`docs/project-format.md`](https://github.com/opengeos/GeoLibre/blob/main/docs/project-format.md)** at the repository root. This file defines all valid fields, their types, and their semantic meaning, while **[`packages/core/src/types.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/core/src/types.ts)** provides the runtime TypeScript implementation that enforces these contracts.