# How the Annotation Engine Converts Voice to GeoJSON in God's Eye View

> Learn how the annotation engine converts voice to GeoJSON. Discover the process of turning spoken commands into runtime objects and then serializing them into GeoJSON geometries for God's Eye View.

- Repository: [Bilawal Sidhu/gods-eye-view](https://github.com/bilawalsidhu/gods-eye-view)
- Tags: how-to-guide
- Published: 2026-09-12

---

**The annotation engine converts spoken commands into GeoJSON by resolving voice input into runtime annotation objects, then serializing them through a pure conversion module that maps internal types to GeoJSON geometries.**

The `bilawalsidhu/gods-eye-view` repository implements a sophisticated pipeline that bridges natural language interactions with geospatial data standards. This article examines the exact mechanism by which voice commands traverse through the system and ultimately materialize as standards-compliant GeoJSON FeatureCollections.

## The Voice-to-GeoJSON Pipeline

The conversion process operates across three distinct architectural layers, beginning with voice capture and ending with structured geospatial output.

### Voice Command Initialization

The voice subsystem initializes the annotation engine through `initGevVoiceCommands` in [`src/voice/gevRealtime.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/voice/gevRealtime.js). This function registers the engine with the voice action runner, establishing the connection between speech recognition and annotation creation.

```javascript
// src/voice/gevRealtime.js
export function initGevVoiceCommands({ viewer, styleManager, dataManager, sceneDirector = null, annotations = null }) {
  const runner = createGevActionRunner({ viewer, styleManager, dataManager, sceneDirector, annotations });
  // ... runner configuration
}

```

When the LLM issues an `annotate_map` action, the system routes the request through [`src/voice/gevActions.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/voice/gevActions.js). The `annotateMap` function at approximately line 290 forwards the structured voice command to the engine's core processing method.

```javascript
// src/voice/gevActions.js (≈ line 290)
async function annotateMap(annotations, args = {}) {
  // ... argument processing
  const result = await annotations.annotate(requests, { ... });
}

```

### Resolution and Runtime Object Creation

Inside [`src/annotations/annotationEngine.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationEngine.js), the `annotate` method coordinates the transformation from abstract command to concrete geospatial data. The engine maintains a live annotation store using a JavaScript Map and processes each request through the **annotationResolver**, which handles geocoding, Overpass OSM queries, and Places API integration.

```javascript
// src/annotations/annotationEngine.js (≈ line 96-104)
const annotations = new Map(); // live annotation store
// ...

async function annotate(requests, opts = {}) {
  // ...
  const settled = await Promise.allSettled(
    list.map(spec => resolveSpec(spec, controller.signal))
  );
}

```

The resolver returns a normalized *anchor* (longitude, latitude, height) and an optional deferred footprint outline. The engine then constructs a **runtime annotation object** via `buildAnnotation` (lines 88-95), capturing only semantic fields required for rendering: type, label, color, and geometry.

```javascript
// src/annotations/annotationEngine.js (≈ line 88-95)
function buildAnnotation(spec, resolved, persist) {
  const type = normalizeType(spec?.type);
  const id = `anno-${++_seq}`;
  // ...
  return { ...base, anchor, ... }; // e.g., pin, area, route, arrow
}

```

## Serializing Runtime Annotations to GeoJSON

The repository provides a Cesium-agnostic conversion module at [`src/annotations/annotationGeoJson.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationGeoJson.js) that handles bidirectional translation between runtime objects and GeoJSON standards.

### From Annotation to Feature

The `annotationToFeature` function inspects the annotation's type property and constructs the appropriate GeoJSON geometry. Pins and labels generate **Point** geometries, routes become **LineString** objects, and areas with sufficient vertices convert to **Polygon** features.

```javascript
// src/annotations/annotationGeoJson.js (≈ line 51-103)
export function annotationToFeature(anno) {
  if (!anno || typeof anno !== 'object' || !VALID_TYPES.has(anno.type)) return null;
  // ...
  if (type === 'route') { 
    geometry = { type: 'LineString', coordinates: ... } 
  }
  else if (type === 'area' && anno.ring?.length >= 3) { 
    geometry = { type: 'Polygon', ... } 
  }
  else { 
    geometry = { type: 'Point', ... } 
  }
  return { type: 'Feature', geometry, properties };
}

```

Engine-specific metadata persists under the `gev:` namespace within the GeoJSON properties, ensuring that styling information and annotation semantics survive the serialization process without polluting standard geospatial fields.

### From Feature to Annotation

Reverse conversion occurs through `featureToAnnotation`, which reconstructs runtime objects from GeoJSON Features while intentionally discarding transient render state such as alpha values and animation timers. The function validates the input structure and extracts the `gev:type` property to determine the appropriate reconstruction logic.

```javascript
// src/annotations/annotationGeoJson.js (≈ line 112-176)
export function featureToAnnotation(feature) {
  if (!feature || feature.type !== 'Feature' ...) return null;
  const type = p['gev:type'];
  // ...
  if (type === 'route') { 
    return { ..., path, anchor: path[0] } 
  }
  else if (g.type === 'Polygon') { ... }
  else { ... } // Point geometry → pin/highlight/label
}

```

### Batch Conversion

For persistence and transmission operations, the module exposes batch helpers that wrap single-item conversions into FeatureCollections:

```javascript
// src/annotations/annotationGeoJson.js (≈ line 191-202)
export function annotationsToFeatureCollection(annotations) {
  const features = (Array.isArray(annotations) ? annotations : [])
    .map(annotationToFeature).filter(Boolean);
  return { type: 'FeatureCollection', features };
}

export function featureCollectionToAnnotations(collection) {
  if (!collection || collection.type !== 'FeatureCollection') return [];
  return collection.features.map(featureToAnnotation).filter(Boolean);
}

```

## Practical Implementation Examples

The following patterns demonstrate the complete workflow from voice capture to GeoJSON export and restoration.

### Exporting Voice-Captured Annotations

This example captures spoken place references and converts the resulting annotations to a downloadable GeoJSON file:

```javascript
// 1️⃣ Capture voice-driven annotations
const voiceResult = await annotations.annotate([
  { type: 'pin', target: 'Eiffel Tower, Paris', label: 'Eiffel', color: 'red' },
  { type: 'area', target: 'Central Park, NY', footprint: true }
]);

// 2️⃣ Serialize the live annotations to GeoJSON
import { annotationsToFeatureCollection } from './annotations/annotationGeoJson.js';
const geojson = annotationsToFeatureCollection(annotations.list());

// 3️⃣ Save / transmit the GeoJSON (e.g., download as file)
const blob = new Blob([JSON.stringify(geojson, null, 2)], {type: 'application/json'});
saveAs(blob, 'my-annotations.geojson');

```

### Restoring Annotations from GeoJSON

To load previously saved annotations back into the engine:

```javascript
import { featureCollectionToAnnotations } from './annotations/annotationGeoJson.js';

// Assume `fileContent` holds the JSON string of a previously exported file
const collection = JSON.parse(fileContent);
const restoredAnnotations = featureCollectionToAnnotations(collection);

// Re-inject them into the live engine (here we simply add them)
await annotations.annotate(restoredAnnotations, { persist: true });

```

## Summary

- **Voice integration** begins in [`src/voice/gevRealtime.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/voice/gevRealtime.js), where `initGevVoiceCommands` registers the annotation engine with the voice action runner.
- **Action routing** occurs in [`src/voice/gevActions.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/voice/gevActions.js), specifically the `annotateMap` function that handles `annotate_map` LLM actions.
- **Runtime resolution** happens in [`src/annotations/annotationEngine.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationEngine.js), where the resolver geocodes targets and `buildAnnotation` constructs normalized objects with anchors and geometry.
- **GeoJSON serialization** is handled by [`src/annotations/annotationGeoJson.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationGeoJson.js), which provides type-aware conversion through `annotationToFeature` and `featureToAnnotation`.
- **Persistence** is achieved via `annotationsToFeatureCollection` and `featureCollectionToAnnotations`, enabling full round-trip data integrity.

## Frequently Asked Questions

### How does the engine handle different annotation types when converting to GeoJSON?

The `annotationToFeature` function in [`src/annotations/annotationGeoJson.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationGeoJson.js) uses a type-specific branching strategy. Routes generate LineString geometries, areas become Polygons (when the ring has three or more points), and all other types default to Point geometries. Each type stores its specific metadata under the `gev:` namespace in the GeoJSON properties.

### What geocoding services does the annotation resolver use?

According to the source code analysis, the **annotationResolver** (located in [`src/annotations/annotationResolver.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/src/annotations/annotationResolver.js)) integrates multiple geospatial services including Overpass OSM for footprint data, Places API for point-of-interest resolution, and standard geocoding endpoints. These services supply the longitude, latitude, and height coordinates that anchor each annotation in 3D space.

### Can the GeoJSON output be used outside of God's Eye View?

Yes. The [`annotationGeoJson.js`](https://github.com/bilawalsidhu/gods-eye-view/blob/main/annotationGeoJson.js) module produces standards-compliant GeoJSON FeatureCollections that conform to RFC 7946. While the module stores engine-specific metadata under the `gev:` property namespace, the core geometry types (Point, LineString, Polygon) and coordinate systems remain compatible with standard geospatial tools like QGIS, Mapbox, and PostGIS.

### How does the system preserve transient render states during serialization?

The conversion process intentionally strips transient states. According to the implementation in `featureToAnnotation`, the system discards runtime-specific properties such as alpha transparency values and animation timers during GeoJSON export. Only persistent semantic data—including type, label, color, and geometry—survives the round-trip conversion.