Elevate symbols above the terrain
Use the symbol-height-offset property to float icons and text above the ground in 3D.
import * as maplibregl from 'https://unpkg.com/maplibre-gl@6.6.0/dist/maplibre-gl.mjs';
const map = new maplibregl.Map({
container: 'map',
zoom: 13,
center: [11.39085, 47.27574],
pitch: 75,
hash: true,
style: {
version: 8,
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19
},
terrainSource: {
type: 'raster-dem',
url: 'https://tiles.mapterhorn.com/tilejson.json'
},
// A few points, each carrying the height (in meters) it should float at.
balloons: {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{type: 'Feature', properties: {label: '0 m', height: 0}, geometry: {type: 'Point', coordinates: [11.385, 47.276]}},
{type: 'Feature', properties: {label: '500 m', height: 500}, geometry: {type: 'Point', coordinates: [11.390, 47.276]}},
{type: 'Feature', properties: {label: '1500 m', height: 1500}, geometry: {type: 'Point', coordinates: [11.395, 47.276]}}
]
}
}
},
layers: [
{id: 'osm', type: 'raster', source: 'osm'},
{
id: 'balloons',
type: 'symbol',
source: 'balloons',
layout: {
'symbol-placement': 'point',
// Data-driven: float each label at the height stored on the feature.
'symbol-height-offset': ['get', 'height'],
'text-field': ['get', 'label'],
'text-size': 16,
'text-allow-overlap': true
},
paint: {
'text-color': '#1b6',
'text-halo-color': '#fff',
'text-halo-width': 2
}
}
],
terrain: {source: 'terrainSource', exaggeration: 1},
sky: {}
},
maxZoom: 18,
maxPitch: 85
});
map.addControl(new maplibregl.NavigationControl({visualizePitch: true}));
<!DOCTYPE html>
<html lang="en">
<head>
<title>Elevate symbols above the terrain</title>
<meta property="og:description" content="Use the symbol-height-offset property to float icons and text above the ground in 3D." />
<meta property="og:category" content="Icons & Symbols" />
<meta charset='utf-8'>
<meta property="og:created" content="2026-06-25" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel='stylesheet' href='https://unpkg.com/maplibre-gl@6.6.0/dist/maplibre-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>
<body>
<div id="map"></div>
<script type="module">
import * as maplibregl from 'https://unpkg.com/maplibre-gl@6.6.0/dist/maplibre-gl.mjs';
const map = new maplibregl.Map({
container: 'map',
zoom: 13,
center: [11.39085, 47.27574],
pitch: 75,
hash: true,
style: {
version: 8,
glyphs: 'https://demotiles.maplibre.org/font/{fontstack}/{range}.pbf',
sources: {
osm: {
type: 'raster',
tiles: ['https://a.tile.openstreetmap.org/{z}/{x}/{y}.png'],
tileSize: 256,
attribution: '© OpenStreetMap Contributors',
maxzoom: 19
},
terrainSource: {
type: 'raster-dem',
url: 'https://tiles.mapterhorn.com/tilejson.json'
},
// A few points, each carrying the height (in meters) it should float at.
balloons: {
type: 'geojson',
data: {
type: 'FeatureCollection',
features: [
{type: 'Feature', properties: {label: '0 m', height: 0}, geometry: {type: 'Point', coordinates: [11.385, 47.276]}},
{type: 'Feature', properties: {label: '500 m', height: 500}, geometry: {type: 'Point', coordinates: [11.390, 47.276]}},
{type: 'Feature', properties: {label: '1500 m', height: 1500}, geometry: {type: 'Point', coordinates: [11.395, 47.276]}}
]
}
}
},
layers: [
{id: 'osm', type: 'raster', source: 'osm'},
{
id: 'balloons',
type: 'symbol',
source: 'balloons',
layout: {
'symbol-placement': 'point',
// Data-driven: float each label at the height stored on the feature.
'symbol-height-offset': ['get', 'height'],
'text-field': ['get', 'label'],
'text-size': 16,
'text-allow-overlap': true
},
paint: {
'text-color': '#1b6',
'text-halo-color': '#fff',
'text-halo-width': 2
}
}
],
terrain: {source: 'terrainSource', exaggeration: 1},
sky: {}
},
maxZoom: 18,
maxPitch: 85
});
map.addControl(new maplibregl.NavigationControl({visualizePitch: true}));
</script>
</body>
</html>