MapLibre GL JS поддерживает трёхмерную визуализацию зданий через слой типа fill-extrusion, который преобразует двумерные полигоны в объёмные геометрические формы на основе атрибутов высоты и базовой отметки. Этот механизм используется для отображения городской застройки, анализа плотности, визуального моделирования инфраструктуры и создания картографических сцен с глубиной.
Слой fill-extrusion строится поверх векторных тайлов и
интерпретирует полигональные геометрии как объёмные объекты. Каждому
полигону назначается:
Геометрия экструзии формируется как вертикальное «вытягивание»
полигона между значениями base и height.
Ключевое требование — наличие атрибутов высоты в векторных данных. Чаще всего используются OpenStreetMap-совместимые схемы:
height — фактическая высота зданияbuilding:levels — количество этажейmin_height — подиум или нависающая структураroof:height — дополнительная высота крышиПри отсутствии height часто применяется оценка:
height ≈ building:levels × 3
(где 3 метра — усреднённая высота этажа)
Структура слоя в стиле MapLibre GL JS описывается через JSON-стиль:
{
"id": "3d-buildings",
"type": "fill-extrusion",
"source": "composite",
"source-layer": "building",
"minzoom": 15,
"filter": ["==", "extrude", "true"],
"paint": {
"fill-extrusion-color": "#aaa",
"fill-extrusion-height": ["get", "height"],
"fill-extrusion-base": ["get", "min_height"],
"fill-extrusion-opacity": 0.8
}
}
Основная конфигурация карты включает источник тайлов и добавление слоя после загрузки стиля:
import maplibregl from "maplibre-gl";
const map = new maplibregl.Map({
container: "map",
style: "https://demotiles.maplibre.org/style.json",
center: [37.6173, 55.7558],
zoom: 15,
pitch: 60,
bearing: -20
});
map.on("load", () => {
map.addLayer({
id: "3d-buildings",
source: "openmaptiles",
"source-layer": "building",
type: "fill-extrusion",
minzoom: 15,
filter: ["==", "extrude", "true"],
paint: {
"fill-extrusion-color": "#b8b8b8",
"fill-extrusion-height": ["get", "height"],
"fill-extrusion-base": ["get", "min_height"],
"fill-extrusion-opacity": 0.9
}
});
});
Определяет итоговую высоту объекта. Поддерживает:
Пример интерполяции:
"fill-extrusion-height": [
"interpolate",
["linear"],
["zoom"],
15, 0,
16, ["*", ["get", "height"], 0.5],
17, ["get", "height"]
]
Используется для создания многоуровневых объектов (например, мосты или здания с подиумом):
"fill-extrusion-base": ["get", "min_height"]
Если значение отсутствует, база считается равной нулю.
Поддерживает как статические цвета, так и data-driven стили:
"fill-extrusion-color": [
"interpolate",
["linear"],
["get", "height"],
0, "#d9d9d9",
100, "#a6a6a6",
300, "#6b6b6b"
]
Регулирует прозрачность слоя. Значение важно при наложении нескольких 3D слоёв и при включённом освещении сцены.
3D-эффект усиливается настройкой источника света:
map.setLight({
anchor: "viewport",
color: "#ffffff",
intensity: 0.6
});
Свет влияет на затенение граней экструдированных объектов, создавая визуальное разделение фасадов.
Для отображения только определённых типов объектов используется фильтр:
"filter": [
"all",
["==", "building", "yes"],
["has", "height"]
]
Распространённые стратегии фильтрации:
Реальные данные часто неполные, поэтому применяются fallback-стратегии:
"fill-extrusion-height": [
"case",
["has", "height"],
["get", "height"],
["*", ["get", "building:levels"], 3]
]
При этом важно учитывать, что значения уровней могут быть неточными.
Слой fill-extrusion относится к GPU-ускоряемым и хорошо масштабируется, однако есть ограничения:
Оптимизационные практики:
"minzoom": 15
3D-здания становятся визуально корректными только при включённом наклоне:
map.setPitch(60);
map.setBearing(-30);
Без pitch экструдированные объекты визуально теряют объём и воспринимаются как смещённые плоские фигуры.
Возникает при совпадении высот или дублировании полигонов. Решение — небольшое смещение base:
"fill-extrusion-base": ["+", ["get", "min_height"], 0.1]
Причины:
extrude: trueПричина — height = 0 или отсутствие данных, из-за чего экструзия не формируется.
Fill-extrusion часто используется вместе с:
Порядок слоёв критичен: 3D-здания должны находиться выше базовых полигонов, но ниже UI-символов.
Для создания различий между типами зданий применяется привязка к атрибутам:
"fill-extrusion-color": [
"match",
["get", "building"],
"residential", "#c9c9c9",
"commercial", "#b0b0ff",
"industrial", "#d0b0a0",
"#aaaaaa"
]
Это позволяет визуально сегментировать городскую среду без изменения геометрии.
При работе с неполными данными используется комбинирование источников:
"fill-extrusion-height": [
"coalesce",
["get", "height"],
["*", ["get", "building:levels"], 3],
10
]
Такая конструкция обеспечивает устойчивую деградацию качества данных.
Градиентная окраска усиливает восприятие масштаба:
"fill-extrusion-color": [
"interpolate",
["linear"],
["get", "height"],
0, "#eeeeee",
50, "#cccccc",
200, "#888888"
]
При сочетании с освещением формируется псевдо-фотореалистичный эффект городской застройки.
Fill-extrusion поддерживает динамическое обновление:
map.setFeatureState(
{ source: "openmaptiles", sourceLayer: "building", id: 123 },
{ height: 120 }
);
Это используется для:
Zoom также влияет на интерполяцию высот, что позволяет постепенно «вырастать» зданиям при приближении карты.