# How the GeoLibre Elevation-Profile Plugin Calculates and Exports Elevation Data from Terrain Layers

> Learn how the GeoLibre elevation-profile plugin calculates and exports elevation data from terrain layers. It uses Open-Meteo API, renders D3 charts, and exports CSV/SVG.

- Repository: [Open Geospatial Solutions/GeoLibre](https://github.com/opengeos/GeoLibre)
- Tags: how-to-guide
- Published: 2026-08-04

---

**The GeoLibre elevation-profile plugin samples elevations along a user-drawn polyline by batching coordinates to the Open-Meteo API (100 points max per request), then renders a D3-style chart and exports the data as CSV or SVG.**

The **elevation-profile plugin** in [opengeos/GeoLibre](https://github.com/opengeos/GeoLibre) transforms any line drawn on a terrain layer into a detailed elevation chart with one-click export. This article breaks down exactly how the plugin calculates elevation data from terrain layers, handles API limits, and generates downloadable outputs—according to the actual source code implementation.

## Stage 1: Capture Line Geometry

When a user draws a line on the map, the `ElevationProfileControl` creates a temporary MapLibre source named `geolibre-elevation-profile-line` to store the polyline. The control listens for `draw`, `clear`, and `hover` events to update the geometry in real time.

The raw coordinates are stored as a `LngLat[]` array through the geometry module at [[`packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts). This file defines the core types used throughout the pipeline:

- `LngLat` – simple longitude/latitude pair
- `ProfileElevationPoint` – `{ lng, lat, ele }` with elevation attached

```typescript
// From geometry.ts - types used across the elevation pipeline
export interface LngLat {
  lng: number;
  lat: number;
}

export interface ProfileElevationPoint extends LngLat {
  ele: number;
}

```

The control extracts the coordinate list whenever the geometry changes, preparing it for elevation sampling.

## Stage 2: Batch-Fetch Elevations from Open-Meteo

The critical challenge: the **Open-Meteo Elevation API accepts ≤100 points per request**. The plugin solves this through intelligent chunking implemented in [[`packages/plugins/src/plugins/elevation-profile/elevation/client.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/client.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/client.ts).

### The Chunking Algorithm

The `fetchElevations` function splits any coordinate list into batches using the constant `MAX_POINTS_PER_REQUEST = 100`, then fires parallel `fetch` calls:

```typescript
// Simplified flow from client.ts
const MAX_POINTS_PER_REQUEST = 100;

export async function fetchElevations(coordinates: LngLat[]): Promise<number[]> {
  // Split into chunks of 100
  const chunks = chunkArray(coordinates, MAX_POINTS_PER_REQUEST);
  
  // Parallel requests to Open-Meteo
  const elevationChunks = await Promise.all(
    chunks.map(batch => fetchElevationBatch(batch))
  );
  
  // Concatenate results
  return elevationChunks.flat();
}

async function fetchElevationBatch(batch: LngLat[]): Promise<number[]> {
  const params = new URLSearchParams();
  batch.forEach(c => {
    params.append('latitude', c.lat.toString());
    params.append('longitude', c.lng.toString());
  });
  
  const url = `https://api.open-meteo.com/v1/elevation?${params}`;
  const response = await fetch(url);
  
  if (!response.ok) {
    throw new ElevationFetchError(`HTTP ${response.status}`);
  }
  
  const data = await response.json();
  return data.elevation;
}

```

### Merging Results

After all batches resolve, the elevation arrays are **zipped back** onto the original coordinates—preserving order—to produce the final `ProfileElevationPoint[]` array:

```typescript
// Result: [{lng: -122.42, lat: 37.77, ele: 15.2}, ...]
const profile = coordinates.map((coord, i) => ({
  lng: coord.lng,
  lat: coord.lat,
  ele: elevations[i]
}));

```

### Why Open-Meteo?

The plugin deliberately uses this **key-less, free API** because it provides global elevation data at sufficient resolution for GIS visualizations without authentication complexity. The chunking logic guarantees no HTTP 400 errors from oversized requests.

### Error Handling

Failed requests throw `ElevationFetchError`, which bubbles up to the control's status bar (`.elevation-profile-status`). The UI remains responsive because fetches use `Promise.all` with asynchronous error propagation.

## Stage 3: Render and Export the Elevation Profile

With elevation data attached to the geometry, the plugin renders a **D3-style chart** and provides two export formats.

### Chart Rendering

The [[`profileChart.ts`](https://github.com/opengeos/GeoLibre/blob/main/profileChart.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/chart/profileChart.ts) module builds scaled SVG paths:

- **Line path** – the elevation profile line
- **Area fill** – gradient beneath the line
- **Axis labels** – distance and elevation markers

The chart inserts into `.elevation-profile-chart` within the control's panel.

### CSV Export

The [[`packages/plugins/src/plugins/elevation-profile/export/csv.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/export/csv.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/export/csv.ts) module serializes profiles:

```typescript
// From csv.ts
export function profileToCsv(profile: ProfileElevationPoint[]): string {
  const header = 'longitude,latitude,elevation';
  const rows = profile.map(p => `${p.lng},${p.lat},${p.ele}`);
  return [header, ...rows].join('\n');
}

```

Output format example:

```csv
longitude,latitude,elevation
-122.4194,37.7749,15.2
-122.4180,37.7755,18.7
-122.4165,37.7762,22.1

```

### SVG Export

The SVG export clones the live chart DOM, **inlines all CSS styles** via an `inline()` helper, and saves as `elevation-profile.svg`. This produces a standalone file that renders correctly without external stylesheets.

Both exports trigger from `ElevationProfileControl._saveFile`, which creates a hidden `<a>` element for programmatic downloads.

### Programmatic Usage Examples

```typescript
// Initialize the control
import { ElevationProfileControl } from
  'packages/plugins/src/plugins/elevation-profile/core/ElevationProfileControl';

map.addControl(new ElevationProfileControl(), 'top-right');

// Export CSV programmatically
import { profileToCsv } from
  'packages/plugins/src/plugins/elevation-profile/export/csv';
import { getCurrentProfile } from
  'packages/plugins/src/plugins/elevation-profile/elevation/geometry';

const profile = getCurrentProfile();
const csv = profileToCsv(profile);
download(csv, 'my-elevation-profile.csv', 'text/csv');

// Export SVG
const svg = document.querySelector('.elevation-profile-svg')!.outerHTML;
download(svg, 'my-elevation-profile.svg', 'image/svg+xml');

```

## Key Implementation Files

| File | Purpose |
|------|---------|
| [[`packages/plugins/src/plugins/elevation-profile/elevation/client.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/client.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/client.ts) | Batched Open-Meteo API requests (`fetchElevations`, `MAX_POINTS_PER_REQUEST`) |
| [[`packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/elevation/geometry.ts) | Types (`LngLat`, `ProfileElevationPoint`) and coordinate helpers |
| [[`packages/plugins/src/plugins/elevation-profile/export/csv.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/export/csv.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/export/csv.ts) | CSV serialization (`profileToCsv`) |
| [[`packages/plugins/src/plugins/elevation-profile/core/ElevationProfileControl.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/core/ElevationProfileControl.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/core/ElevationProfileControl.ts) | Main UI control, map sources, export wiring (`_saveFile`) |
| [[`packages/plugins/src/plugins/elevation-profile/chart/profileChart.ts`](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/chart/profileChart.ts)](https://github.com/opengeos/GeoLibre/blob/main/packages/plugins/src/plugins/elevation-profile/chart/profileChart.ts) | SVG chart generation (line, area, axes) |

## Summary

The GeoLibre elevation-profile plugin implements a **five-stage pipeline** for calculating and exporting elevation data from terrain layers:

- **Geometry capture** – stores user-drawn lines as `LngLat[]` arrays
- **Intelligent chunking** – splits requests to respect Open-Meteo's 100-point API limit
- **Parallel fetching** – uses `Promise.all` for efficient batch retrieval
- **D3 chart rendering** – visualizes elevation profiles with line, area, and axes
- **Dual export** – generates standalone CSV data or styled SVG graphics

The implementation prioritizes **reliability** (error handling via `ElevationFetchError`), **performance** (parallel chunked requests), and **portability** (inlined SVG styles, simple CSV format).

## Frequently Asked Questions

### What elevation data source does the GeoLibre elevation-profile plugin use?

The plugin uses the **free, key-less Open-Meteo Elevation API** at `https://api.open-meteo.com/v1/elevation`. This provides global elevation coverage without requiring API keys or authentication, making it ideal for open-source deployment.

### Why does the plugin split API requests into batches of 100 points?

The Open-Meteo Elevation API enforces a **hard limit of 100 coordinates per request**. Exceeding this returns HTTP 400 errors. The `MAX_POINTS_PER_REQUEST` constant in [`client.ts`](https://github.com/opengeos/GeoLibre/blob/main/client.ts) ensures compliance by chunking longer polylines into parallel batches that are concatenated after all responses arrive.

### How accurate is the exported CSV elevation data?

The CSV contains **exact values returned by Open-Meteo** for each sampled point, with no interpolation or smoothing applied. Accuracy depends on the underlying terrain model (typically SRTM or similar). Each row preserves the original longitude and latitude alongside the fetched elevation.

### Can I export the elevation profile without using the UI buttons?

Yes. The plugin exposes `profileToCsv()` for programmatic CSV generation and the chart SVG can be queried via `.elevation-profile-svg` and serialized. The `ElevationProfileControl._saveFile` method demonstrates the download trigger pattern using a hidden anchor element.