DEM источники

DEM (Digital Elevation Model) — источник цифровой модели рельефа, содержащий информацию о высотах поверхности. В MapLibre GL JS такие источники используются для построения трёхмерного рельефа карты, расчёта затенения местности (hillshade), визуализации горных районов, долин, плато и других особенностей ландшафта.

В отличие от обычных растровых или векторных источников, DEM-источник не отображается напрямую. Он служит набором данных, на основе которого движок карты вычисляет геометрию поверхности.

Основные сценарии использования:

  • отображение рельефа местности;
  • создание объёмной поверхности карты;
  • генерация теней рельефа;
  • повышение реалистичности 3D-сцен;
  • анализ высотных данных.

Структура 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 Формат кодирования высот

Добавление DEM-источника

Наиболее распространённый вариант:

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

DEM-тайлы обычно представляют собой изображения PNG, в которых значение каждого пикселя кодирует высоту.

Наиболее популярные схемы кодирования:

Mapbox Terrain RGB

Высота вычисляется по формуле:

height = -10000 + ((R * 256 * 256 + G * 256 + B) * 0.1)

где:

  • R — красный канал;
  • G — зелёный канал;
  • B — синий канал.

Пример:

R = 1
G = 150
B = 200

Высота:

-10000 + ((1×65536 + 150×256 + 200) × 0.1)

Такой подход позволяет хранить значения высоты с высокой точностью.


Параметр encoding

Параметр encoding определяет способ декодирования высотных данных.

Mapbox

map.addSource('dem', {
    type: 'raster-dem',
    tiles: [
        'https://example.com/dem/{z}/{x}/{y}.png'
    ],
    encoding: 'mapbox'
});

Это наиболее распространённый вариант.

Terrarium

Формат Terrarium используется рядом открытых картографических сервисов.

map.addSource('dem', {
    type: 'raster-dem',
    tiles: [
        'https://example.com/terrarium/{z}/{x}/{y}.png'
    ],
    encoding: 'terrarium'
});

Формула декодирования отличается от Terrain RGB.


Использование DEM для построения рельефа

После регистрации источника можно активировать рельеф карты.

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');

Использование hillshade

DEM-данные могут использоваться для генерации слоя теней рельефа.

Сначала создаётся источник:

map.addSource('dem', {
    type: 'raster-dem',
    tiles: [
        'https://example.com/dem/{z}/{x}/{y}.png'
    ]
});

Затем слой:

map.addLayer({
    id: 'hillshade',
    type: 'hillshade',
    source: 'dem'
});

Результатом становится псевдообъёмное освещение поверхности.


Настройка hillshade

Цвет теней

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'
    }
});

Комбинирование рельефа и hillshade

Обе технологии могут использоваться одновременно.

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 используются наиболее детальные доступные данные.


Размер тайлов

Наиболее распространены два размера.

Тайлы 256×256

tileSize: 256

Преимущества:

  • меньший размер файлов;
  • меньшее потребление памяти.

Недостатки:

  • больше запросов к серверу.

Тайлы 512×512

tileSize: 512

Преимущества:

  • меньше сетевых запросов;
  • более эффективная работа на высоких масштабах.

Недостатки:

  • более тяжёлые файлы.

Для современных серверов и клиентов обычно предпочтителен вариант:

tileSize: 512

Получение конфигурации источника

Иногда требуется получить зарегистрированный объект.

const demSource = map.getSource('dem');

console.log(demSource);

Это удобно при диагностике состояния карты.


Проверка загрузки карты перед подключением DEM

Большинство операций выполняется после события загрузки.

map.on('load', () => {

    map.addSource('dem', {
        type: 'raster-dem',
        tiles: [
            'https://example.com/dem/{z}/{x}/{y}.png'
        ]
    });

    map.setTerrain({
        source: 'dem'
    });

});

Добавление DEM до полной инициализации карты может привести к ошибкам.


Использование стиля с уже встроенным DEM

Некоторые стили содержат DEM-источник заранее.

Пример получения источника:

console.log(
    map.getStyle().sources
);

Результат может содержать:

{
    terrain: {
        type: 'raster-dem'
    }
}

После этого достаточно включить рельеф:

map.setTerrain({
    source: 'terrain'
});

Производительность DEM

Высотные данные требуют дополнительных вычислений на стороне клиента.

Основные факторы нагрузки:

  • размер DEM-тайлов;
  • уровень детализации данных;
  • коэффициент преувеличения;
  • количество одновременно загружаемых тайлов;
  • использование hillshade;
  • наклон карты;
  • аппаратное ускорение браузера.

Для больших проектов обычно применяются следующие практики:

maxzoom: 13

ограничение чрезмерной детализации;

exaggeration: 1.2

умеренное усиление рельефа;

tileSize: 512

уменьшение количества сетевых запросов.


Типичные ошибки

Неверный тип источника

Ошибка:

type: 'raster'

Для DEM обязательно:

type: 'raster-dem'

Несоответствие encoding

Ошибка:

encoding: 'mapbox'

при использовании данных Terrarium.

Исправление:

encoding: 'terrarium'

Неверное кодирование приводит к искажённым высотам.


Отсутствие источника при вызове setTerrain

Ошибка:

map.setTerrain({
    source: 'dem'
});

до выполнения:

map.addSource('dem', ...);

Источник должен быть зарегистрирован заранее.


Удаление источника при активном рельефе

Ошибка:

map.removeSource('dem');

при включённом terrain.

Правильный порядок:

map.setTerrain(null);

map.removeSource('dem');

Неверный URL тайлов

Ошибка конфигурации:

tiles: [
    '/terrain'
]

Корректный шаблон:

tiles: [
    '/terrain/{z}/{x}/{y}.png'
]

MapLibre должен иметь возможность вычислять путь к каждому отдельному тайлу.


Полный пример подключения DEM и рельефа

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'
        }
    });

});

Такая конфигурация создаёт полноценную трёхмерную поверхность местности, использующую цифровую модель высот для построения геометрии рельефа и дополнительной светотеневой визуализации.