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 pairProfileElevationPoint–{ 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
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.allfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →