DEM (Digital Elevation Model) — источник цифровой модели рельефа, содержащий информацию о высотах поверхности. В MapLibre GL JS такие источники используются для построения трёхмерного рельефа карты, расчёта затенения местности (hillshade), визуализации горных районов, долин, плато и других особенностей ландшафта.
В отличие от обычных растровых или векторных источников, DEM-источник не отображается напрямую. Он служит набором данных, на основе которого движок карты вычисляет геометрию поверхности.
Основные сценарии использования:
DEM-источник создаётся через тип raster-dem.
Базовая структура:
map.addSource('terrain-source', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
],
tileSize: 256
});
Параметры:
| Параметр | Описание |
|---|---|
type |
Всегда raster-dem |
tiles |
URL-шаблоны тайлов DEM |
tileSize |
Размер тайла |
bounds |
Географические границы данных |
minzoom |
Минимальный уровень масштабирования |
maxzoom |
Максимальный уровень масштабирования |
attribution |
Информация об источнике данных |
encoding |
Формат кодирования высот |
Наиболее распространённый вариант:
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://terrain.example.com/{z}/{x}/{y}.png'
],
tileSize: 512
});
После регистрации источник становится доступным для других компонентов карты.
Проверка существования:
const source = map.getSource('dem');
if (source) {
console.log('Источник найден');
}
Удаление:
map.removeSource('dem');
Перед удалением необходимо отключить все слои и механизмы, использующие данный источник.
DEM-тайлы обычно представляют собой изображения PNG, в которых значение каждого пикселя кодирует высоту.
Наиболее популярные схемы кодирования:
Высота вычисляется по формуле:
height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)
где:
Пример:
R = 1
G = 150
B = 200
Высота:
-10000 + ((1×65536 + 150×256 + 200) × 0.1)
Такой подход позволяет хранить значения высоты с высокой точностью.
Параметр encoding определяет способ декодирования
высотных данных.
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
],
encoding: 'mapbox'
});
Это наиболее распространённый вариант.
Формат Terrarium используется рядом открытых картографических сервисов.
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/terrarium/{z}/{x}/{y}.png'
],
encoding: 'terrarium'
});
Формула декодирования отличается от Terrain RGB.
После регистрации источника можно активировать рельеф карты.
map.setTerrain({
source: 'dem'
});
После выполнения карта начинает использовать высотные данные для формирования поверхности.
Полный пример:
map.on('load', () => {
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://terrain.example.com/{z}/{x}/{y}.png'
],
tileSize: 512
});
map.setTerrain({
source: 'dem'
});
});
Рельеф может усиливаться для более наглядного отображения.
Параметр exaggeration задаёт масштаб высоты.
map.setTerrain({
source: 'dem',
exaggeration: 1.5
});
Возможные значения:
0.5
Уменьшенный рельеф.
1
Реальные высоты.
2
Удвоенная высота.
5
Сильно выраженный рельеф.
Пример:
map.setTerrain({
source: 'dem',
exaggeration: 3
});
Горы и возвышенности становятся значительно заметнее.
Значение преувеличения можно менять во время работы приложения.
let exaggeration = 1;
function increaseTerrain() {
exaggeration += 0.5;
map.setTerrain({
source: 'dem',
exaggeration
});
}
Уменьшение:
function decreaseTerrain() {
exaggeration = Math.max(
0,
exaggeration - 0.5
);
map.setTerrain({
source: 'dem',
exaggeration
});
}
Такой подход часто используется совместно со слайдерами интерфейса.
Для возврата к плоской карте:
map.setTerrain(null);
Источник данных при этом остаётся зарегистрированным.
Если требуется полное удаление:
map.setTerrain(null);
map.removeSource('dem');
DEM-данные могут использоваться для генерации слоя теней рельефа.
Сначала создаётся источник:
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
]
});
Затем слой:
map.addLayer({
id: 'hillshade',
type: 'hillshade',
source: 'dem'
});
Результатом становится псевдообъёмное освещение поверхности.
paint: {
'hillshade-shadow-color': '#2c3e50'
}
paint: {
'hillshade-highlight-color': '#ffffff'
}
paint: {
'hillshade-accent-color': '#888888'
}
Полный пример:
map.addLayer({
id: 'hillshade',
type: 'hillshade',
source: 'dem',
paint: {
'hillshade-shadow-color': '#1e293b',
'hillshade-highlight-color': '#ffffff',
'hillshade-accent-color': '#94a3b8'
}
});
Обе технологии могут использоваться одновременно.
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
]
});
map.setTerrain({
source: 'dem',
exaggeration: 1.5
});
map.addLayer({
id: 'hillshade',
type: 'hillshade',
source: 'dem'
});
Преимущества такого подхода:
При наличии покрытия только для определённого региона рекомендуется
использовать bounds.
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
],
bounds: [
30,
40,
45,
55
]
});
Порядок координат:
[minLng, minLat, maxLng, maxLat]
Это позволяет избежать лишних запросов за пределами области покрытия.
Для DEM обычно указываются границы масштабирования.
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
],
minzoom: 0,
maxzoom: 14
});
После достижения maxzoom используются наиболее детальные
доступные данные.
Наиболее распространены два размера.
tileSize: 256
Преимущества:
Недостатки:
tileSize: 512
Преимущества:
Недостатки:
Для современных серверов и клиентов обычно предпочтителен вариант:
tileSize: 512
Иногда требуется получить зарегистрированный объект.
const demSource = map.getSource('dem');
console.log(demSource);
Это удобно при диагностике состояния карты.
Большинство операций выполняется после события загрузки.
map.on('load', () => {
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://example.com/dem/{z}/{x}/{y}.png'
]
});
map.setTerrain({
source: 'dem'
});
});
Добавление DEM до полной инициализации карты может привести к ошибкам.
Некоторые стили содержат DEM-источник заранее.
Пример получения источника:
console.log(
map.getStyle().sources
);
Результат может содержать:
{
terrain: {
type: 'raster-dem'
}
}
После этого достаточно включить рельеф:
map.setTerrain({
source: 'terrain'
});
Высотные данные требуют дополнительных вычислений на стороне клиента.
Основные факторы нагрузки:
Для больших проектов обычно применяются следующие практики:
maxzoom: 13
ограничение чрезмерной детализации;
exaggeration: 1.2
умеренное усиление рельефа;
tileSize: 512
уменьшение количества сетевых запросов.
Ошибка:
type: 'raster'
Для DEM обязательно:
type: 'raster-dem'
Ошибка:
encoding: 'mapbox'
при использовании данных Terrarium.
Исправление:
encoding: 'terrarium'
Неверное кодирование приводит к искажённым высотам.
Ошибка:
map.setTerrain({
source: 'dem'
});
до выполнения:
map.addSource('dem', ...);
Источник должен быть зарегистрирован заранее.
Ошибка:
map.removeSource('dem');
при включённом terrain.
Правильный порядок:
map.setTerrain(null);
map.removeSource('dem');
Ошибка конфигурации:
tiles: [
'/terrain'
]
Корректный шаблон:
tiles: [
'/terrain/{z}/{x}/{y}.png'
]
MapLibre должен иметь возможность вычислять путь к каждому отдельному тайлу.
const map = new maplibregl.Map({
container: 'map',
style: 'style.json',
center: [7.5, 46.5],
zoom: 10,
pitch: 70,
bearing: 30
});
map.on('load', () => {
map.addSource('dem', {
type: 'raster-dem',
tiles: [
'https://terrain.example.com/{z}/{x}/{y}.png'
],
tileSize: 512,
encoding: 'mapbox',
maxzoom: 14
});
map.setTerrain({
source: 'dem',
exaggeration: 1.8
});
map.addLayer({
id: 'hillshade',
type: 'hillshade',
source: 'dem',
paint: {
'hillshade-shadow-color': '#2d3748',
'hillshade-highlight-color': '#ffffff',
'hillshade-accent-color': '#a0aec0'
}
});
});
Такая конфигурация создаёт полноценную трёхмерную поверхность местности, использующую цифровую модель высот для построения геометрии рельефа и дополнительной светотеневой визуализации.