Высота зданий из данных

В трёхмерной визуализации карт в MapLibre GL JS высота зданий формируется не из геометрии растрового изображения, а из атрибутов векторных тайлов. Основной принцип заключается в том, что каждое здание представляет собой полигон с набором свойств, среди которых ключевым является значение высоты.

Чаще всего используются данные OpenStreetMap, где высота может храниться в различных тегах:

  • height — фактическая высота здания в метрах
  • building:levels — количество этажей
  • min_height — высота основания (для зданий на опорах или стилобатах)

Если height отсутствует, значение может быть вычислено как произведение числа этажей на усреднённую высоту одного этажа (обычно 3–3.5 метра).


Подготовка слоя 3D-зданий

В MapLibre GL JS визуализация высот реализуется через слой типа fill-extrusion. Этот тип слоя предназначен для построения объёмных объектов на основе 2D-полигонов.

Базовая структура добавления слоя:

map.on('load', () => {
  map.addLayer({
    id: 'buildings-3d',
    source: 'composite',
    'source-layer': 'building',
    type: 'fill-extrusion',
    minzoom: 14,
    paint: {
      'fill-extrusion-color': '#aaa',
      'fill-extrusion-height': 10,
      'fill-extrusion-base': 0,
      'fill-extrusion-opacity': 0.9
    }
  });
});

В этом примере высота задана фиксированным значением, что подходит только для тестирования или схематичных представлений.


Данные высоты из атрибутов

Реалистичная визуализация требует использования свойств данных. Векторные тайлы позволяют обращаться к атрибутам через выражения.

Типовой подход:

'fill-extrusion-height': ['get', 'height']

Если данные не унифицированы, используется резервная логика:

'fill-extrusion-height': [
  'case',
  ['has', 'height'],
  ['get', 'height'],
  [
    '*',
    ['to-number', ['get', 'building:levels']],
    3
  ]
]

Такая конструкция обеспечивает гибкость при неоднородности исходных данных.


Учёт основания здания

Некоторые здания начинаются не от уровня земли. Для корректного отображения используется свойство fill-extrusion-base.

'fill-extrusion-base': [
  'case',
  ['has', 'min_height'],
  ['get', 'min_height'],
  0
]

Это позволяет визуально отделить здания на платформах, парковки на нижних уровнях и сложные архитектурные конструкции.


Масштабирование и уровни детализации

3D-здания не должны отображаться на всех уровнях масштабирования. Это связано как с производительностью, так и с читаемостью карты.

minzoom: 15

На низких зумах используется упрощённая 2D-геометрия, а при приближении активируется экструдированная модель.

Дополнительно применяется плавное появление высот:

'fill-extrusion-height': [
  'interpolate',
  ['linear'],
  ['zoom'],
  14,
  0,
  15,
  ['get', 'height']
]

Это создаёт эффект «вырастания» зданий при увеличении масштаба.


Цвет и визуальная дифференциация высот

Высота может влиять не только на геометрию, но и на визуальные характеристики. Часто используется градиентное окрашивание:

'fill-extrusion-color': [
  'interpolate',
  ['linear'],
  ['get', 'height'],
  0, '#d9d9d9',
  50, '#a6a6a6',
  150, '#6b6b6b',
  300, '#3d3d3d'
]

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


Использование выражений для нормализации данных

Данные в реальных наборах редко бывают идеальными. Часто встречаются строки, отсутствующие значения или нестандартные единицы измерения.

Для очистки используется приведение типов:

['to-number', ['get', 'height'], 0]

Также возможна обработка значений с единицами:

[
  'to-number',
  ['slice', ['get', 'height'], 0, -1],
  0
]

(например, удаление суффикса “m”)


Комбинация этажности и высоты

Наиболее распространённый практический сценарий — комбинирование height и building:levels.

'fill-extrusion-height': [
  'case',
  ['has', 'height'],
  ['to-number', ['get', 'height']],
  [
    '*',
    ['to-number', ['get', 'building:levels']],
    3
  ]
]

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


Работа с 3D-ландшафтом и контекстом

Высота зданий становится особенно выразительной при включённом наклоне карты:

map.setPitch(60);
map.setBearing(-20);

В этом режиме экструдированные здания взаимодействуют с перспективой камеры, создавая эффект городской сцены.

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


Оптимизация производительности

Отрисовка большого количества 3D-объектов требует оптимизации:

  • ограничение minzoom для слоя
  • упрощение геометрии тайлов
  • использование атрибутов вместо вычислений на лету
  • уменьшение количества цветов и интерполяций
  • группировка слоёв по стилям

В MapLibre GL JS рендеринг выполняется через WebGL, поэтому ключевым фактором становится баланс между детализацией и количеством полигонов.


Пример комплексного слоя зданий

map.addLayer({
  id: '3d-buildings',
  source: 'composite',
  'source-layer': 'building',
  type: 'fill-extrusion',
  minzoom: 15,
  paint: {
    'fill-extrusion-color': [
      'interpolate',
      ['linear'],
      ['get', 'height'],
      0, '#e0e0e0',
      100, '#b0b0b0',
      300, '#707070'
    ],
    'fill-extrusion-height': [
      'case',
      ['has', 'height'],
      ['to-number', ['get', 'height']],
      [
        '*',
        ['to-number', ['get', 'building:levels']],
        3
      ]
    ],
    'fill-extrusion-base': [
      'case',
      ['has', 'min_height'],
      ['to-number', ['get', 'min_height']],
      0
    ],
    'fill-extrusion-opacity': 0.85
  }
});

Этот слой объединяет основные техники: извлечение атрибутов, резервные вычисления и визуальную дифференциацию по высоте.


Поведение при неполных данных

В реальных городских наборах значительная часть зданий не содержит высоты. В таких случаях применяются стратегии:

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

Подобные подходы позволяют сохранить целостность 3D-сцены даже при фрагментарных данных.