Проблемы с отображением

Корректное отображение карты в MapLibre GL JS зависит не только от настроек самой библиотеки, но и от структуры HTML-документа, CSS-стилей, параметров камеры, источников данных и состояния WebGL. Большинство визуальных ошибок связано именно с этими компонентами.

Карта не отображается вообще

Наиболее распространённая проблема — контейнер карты присутствует в DOM, но карта остаётся невидимой.

Пример создания карты:

const map = new maplibregl.Map({
    container: 'map',
    style: 'style.json',
    center: [37.6176, 55.7558],
    zoom: 10
});

HTML:

<div id="map"></div>

Если контейнер не имеет размеров, карта не сможет отрисоваться.

Неправильный вариант:

#map {
}

Правильный вариант:

#map {
    width: 100%;
    height: 500px;
}

Также часто встречается ситуация, когда высота родительского элемента равна нулю.

Например:

<div class="wrapper">
    <div id="map"></div>
</div>
.wrapper {
    height: 0;
}

В таком случае карта также останется невидимой.


Белый экран вместо карты

Если контейнер имеет размеры, но отображается только белый фон, причины обычно связаны со стилем карты.

Проверка загрузки стиля:

map.on('load', () => {
    console.log('Стиль загружен');
});

Проверка ошибок:

map.on('error', (e) => {
    console.error(e.error);
});

Типичные причины:

  • неверный URL стиля;
  • ошибка JSON в стиле;
  • недоступный сервер тайлов;
  • проблемы CORS;
  • отсутствие интернет-соединения.

Пример ошибочного пути:

style: './styles/map.json'

Если файл фактически находится в другом каталоге:

style: './assets/styles/map.json'

карта не сможет загрузить данные.


Карта отображается частично

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

Причина чаще всего связана с изменением размеров контейнера после инициализации карты.

Например:

const map = new maplibregl.Map({
    container: 'map',
    style: style
});

document.getElementById('map').style.height = '600px';

MapLibre уже вычислил размеры контейнера и не знает об изменениях.

Необходимо вызвать:

map.resize();

Пример:

window.addEventListener('resize', () => {
    map.resize();
});

Карта внутри скрытого блока

Особенно часто проблема возникает при использовании вкладок, модальных окон и аккордеонов.

Пример:

<div id="modal" style="display:none;">
    <div id="map"></div>
</div>

Если карта создаётся в момент, когда контейнер скрыт:

const map = new maplibregl.Map({
    container: 'map',
    style: style
});

MapLibre получает размеры контейнера как 0×0.

После открытия блока требуется выполнить:

map.resize();

Например:

openModal();

map.resize();

Серые квадраты вместо карты

Серые области свидетельствуют о том, что тайлы не были загружены.

Возможные причины:

  • неправильный URL источника;
  • сервер тайлов недоступен;
  • превышены лимиты запросов;
  • ошибка авторизации;
  • отсутствует API-ключ.

Пример источника:

{
    type: 'raster',
    tiles: [
        'https://server.example.com/{z}/{x}/{y}.png'
    ],
    tileSize: 256
}

Если сервер возвращает ошибку 404:

GET ... 404 Not Found

соответствующие тайлы не будут отображены.

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


Карта отображается смещённой

Иногда маркеры и объекты находятся не там, где ожидается.

Причина часто заключается в неправильном порядке координат.

MapLibre использует формат:

[долгота, широта]

Верный пример:

[37.6176, 55.7558]

Ошибочный пример:

[55.7558, 37.6176]

Такая ошибка может перенести объект на другой континент.


Маркеры не появляются

Создание маркера:

new maplibregl.Marker()
    .setLngLat([37.6176, 55.7558])
    .addTo(map);

Если маркер отсутствует, следует проверить:

  1. Координаты.
  2. Масштаб карты.
  3. Видимость слоя.
  4. CSS-стили.

Иногда маркер оказывается за пределами текущего вида карты.

Проверка:

map.flyTo({
    center: [37.6176, 55.7558],
    zoom: 14
});

Маркеры есть, но не видны

Причина может скрываться в пользовательском HTML.

Пример:

const el = document.createElement('div');

el.style.width = '0px';
el.style.height = '0px';

new maplibregl.Marker(el)
    .setLngLat([37.6176, 55.7558])
    .addTo(map);

Элемент фактически существует, но имеет нулевой размер.

Корректный вариант:

el.style.width = '20px';
el.style.height = '20px';

Слои не отображаются

Добавление слоя:

map.addLayer({
    id: 'roads',
    type: 'line',
    source: 'roads'
});

Частая ошибка — слой добавляется раньше источника.

Неправильно:

map.addLayer({
    id: 'roads',
    source: 'roads',
    type: 'line'
});

map.addSource('roads', source);

Правильно:

map.addSource('roads', source);

map.addLayer({
    id: 'roads',
    source: 'roads',
    type: 'line'
});

Слой существует, но ничего не рисует

Следует проверить наличие данных.

Проверка GeoJSON:

console.log(data.features.length);

Пустой набор данных:

{
  "type": "FeatureCollection",
  "features": []
}

не приведёт к появлению объектов на карте.

Также необходимо проверить фильтры слоя.

Например:

filter: ['==', 'type', 'road']

Если объектов с таким свойством нет, слой останется пустым.


Объекты исчезают на определённых масштабах

Причина часто связана с параметрами масштабирования.

Пример:

{
    id: 'buildings',
    type: 'fill',
    source: 'buildings',
    minzoom: 15
}

Слой начнёт отображаться только после достижения масштаба 15.

При масштабе:

zoom: 10

объекты не будут видны.

Проверка:

const layer = map.getLayer('buildings');

console.log(layer.minzoom);

Текст подписей не отображается

Для символов используется слой типа symbol.

Пример:

{
    id: 'cities',
    type: 'symbol',
    source: 'cities',
    layout: {
        'text-field': ['get', 'name']
    }
}

Если поле отсутствует:

{
  "population": 500000
}

надпись не появится.

Следует убедиться в наличии нужного атрибута:

{
  "name": "Москва"
}

Иконки отсутствуют

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

Пример:

map.loadImage('/marker.png', (error, image) => {

    if (error) throw error;

    map.addImage('marker', image);
});

После этого:

layout: {
    'icon-image': 'marker'
}

Если изображение не зарегистрировано, иконка отображаться не будет.

Проверка:

console.log(
    map.hasImage('marker')
);

Мерцание слоёв

Причины:

  • частое обновление данных;
  • повторное создание источников;
  • удаление и повторное добавление слоёв;
  • перегруженные обработчики событий.

Плохая практика:

setInterval(() => {

    map.removeLayer('points');

    map.addLayer(layer);

}, 100);

Лучше обновлять данные источника:

source.setData(newData);

Размытая карта на экранах высокой плотности

На устройствах Retina карта иногда выглядит нечёткой.

Причины:

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

Плохой вариант:

#map {
    transform: scale(1.5);
}

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

Предпочтительно изменять размеры контейнера напрямую:

#map {
    width: 1200px;
    height: 800px;
}

Артефакты при наклоне карты

При использовании параметра pitch:

map.setPitch(60);

могут появляться:

  • мерцания;
  • разрывы геометрии;
  • исчезновение объектов;
  • проблемы глубины отображения.

Особенно это заметно при работе с большими полигонами и 3D-экструзией.

Пример слоя:

{
    id: 'buildings',
    type: 'fill-extrusion'
}

Для диагностики полезно временно убрать наклон:

map.setPitch(0);

Если проблема исчезает, причина связана именно с перспективной проекцией.


Ошибки WebGL

MapLibre полностью зависит от WebGL.

Проверка поддержки:

if (!maplibregl.supported()) {
    console.error('WebGL недоступен');
}

Причины отказа:

  • устаревший браузер;
  • отключённое аппаратное ускорение;
  • проблемы драйверов видеокарты;
  • ограничение корпоративной среды.

Дополнительная диагностика:

map.on('error', console.error);

Потеря контекста WebGL

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

Симптомы:

  • карта становится чёрной;
  • исчезают слои;
  • прекращается обновление изображения.

Причины:

  • чрезмерное количество тайлов;
  • очень большие GeoJSON-файлы;
  • множество одновременно активных карт.

Рекомендуется уменьшать объём данных и избегать создания большого количества экземпляров Map.


Проблемы с GeoJSON большого размера

Источник:

map.addSource('data', {
    type: 'geojson',
    data: hugeGeoJSON
});

Файлы размером в десятки мегабайт могут приводить к:

  • длительной загрузке;
  • зависанию интерфейса;
  • пропаданию кадров анимации;
  • временной пустой карте.

Способы решения:

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

Отображение устаревших данных

После обновления GeoJSON на сервере карта может показывать старую версию.

Причина часто связана с кэшированием.

Источник:

data: '/api/data.geojson'

Для принудительного обновления иногда добавляют параметр версии:

data: `/api/data.geojson?v=${Date.now()}`

или корректно настраивают HTTP-заголовки кэширования на сервере.


Диагностика проблем отображения

Наиболее полезные инструменты поиска ошибок:

Проверка загрузки карты:

map.on('load', () => {
    console.log('Map loaded');
});

Отслеживание ошибок:

map.on('error', (e) => {
    console.error(e);
});

Проверка состояния источников:

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

Проверка слоёв:

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

Проверка размеров контейнера:

console.log(
    map.getContainer().clientWidth,
    map.getContainer().clientHeight
);

Систематическая проверка размеров контейнера, загрузки стиля, состояния источников, корректности координат и работы WebGL позволяет выявить подавляющее большинство проблем с отображением в приложениях на базе MapLibre GL JS.