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

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 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). This file defines the core types used throughout the pipeline:

  • LngLat – simple longitude/latitude pair
  • ProfileElevationPoint – { lng, lat, ele } with elevation attached
// 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).

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:

// 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:

// 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/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) module serializes profiles:

// 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:

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

// 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) 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) 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) 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) 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) 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 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.

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 →