Частые ошибки

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

Неправильное подключение:

<script src="maplibre-gl.js"></script>

Правильное подключение:

<link
  href="https://unpkg.com/maplibre-gl/dist/maplibre-gl.css"
  rel="stylesheet"
/>

<script src="https://unpkg.com/maplibre-gl/dist/maplibre-gl.js"></script>

Если CSS не подключён, карта обычно отображается некорректно:

  • отсутствуют элементы управления;
  • нарушается позиционирование контролов;
  • возникают проблемы с размерами контейнера.

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


Отсутствие размеров контейнера карты

MapLibre не может корректно отобразить карту внутри элемента без заданной высоты.

Ошибка:

<div id="map"></div>
new maplibregl.Map({
    container: "map",
    style: styleUrl
});

В результате контейнер существует, но его высота равна нулю.

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

<div id="map"></div>
#map {
    width: 100%;
    height: 500px;
}

Либо:

html,
body,
#map {
    width: 100%;
    height: 100%;
    margin: 0;
}

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


Создание карты до появления контейнера в DOM

Если скрипт выполняется раньше, чем создаётся HTML-элемент контейнера, MapLibre не сможет найти целевой элемент.

Ошибка:

const map = new maplibregl.Map({
    container: "map",
    style: styleUrl
});
<div id="map"></div>

В момент выполнения кода элемента ещё не существует.

Корректное решение:

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

<script>
const map = new maplibregl.Map({
    container: "map",
    style: styleUrl
});
</script>

Либо:

window.addEventListener("DOMContentLoaded", () => {
    const map = new maplibregl.Map({
        container: "map",
        style: styleUrl
    });
});

Неверный идентификатор контейнера

Часто ошибка возникает из-за опечатки в имени контейнера.

HTML:

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

Jav * aScript:

container: "Map"

MapLibre чувствителен к регистру символов.

Правильно:

container: "map"

Иногда безопаснее передавать сам DOM-элемент:

container: document.getElementById("map")

Такой подход помогает быстрее обнаруживать ошибки.


Использование недоступного Style JSON

Без корректного описания стиля карта не сможет загрузиться.

Ошибка:

style: "style.json"

При этом:

  • файл отсутствует;
  • сервер возвращает ошибку 404;
  • файл содержит невалидный JSON.

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

Полезно также подписываться на событие ошибок:

map.on("error", (event) => {
    console.error(event.error);
});

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

MapLibre работает со спецификацией стилей, совместимой с Mapbox Style Specification. Однако не все стили, найденные в интернете, гарантированно совместимы.

Проблемы возникают при использовании:

  • нестандартных расширений;
  • экспериментальных свойств;
  • устаревших версий спецификации.

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


Добавление источника до загрузки карты

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

Ошибка:

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

map.addSource("cities", {
    type: "geojson",
    data: data
});

Карта ещё не успела загрузиться.

Правильно:

map.on("load", () => {
    map.addSource("cities", {
        type: "geojson",
        data: data
    });
});

Событие load гарантирует готовность стиля и внутренних компонентов.


Добавление слоя до создания источника

Каждый слой должен ссылаться на существующий источник данных.

Ошибка:

map.addLayer({
    id: "cities",
    type: "circle",
    source: "cities"
});

Если источник не зарегистрирован, будет выброшено исключение.

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

map.addSource("cities", {
    type: "geojson",
    data: data
});

map.addLayer({
    id: "cities",
    type: "circle",
    source: "cities"
});

Сначала источник, затем слой.


Повторное использование идентификаторов

Идентификаторы источников и слоёв должны быть уникальными.

Ошибка:

map.addSource("data", source1);

map.addSource("data", source2);

Либо:

map.addLayer({
    id: "markers"
});

map.addLayer({
    id: "markers"
});

MapLibre выдаст сообщение о конфликте идентификаторов.

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

if (!map.getSource("data")) {
    map.addSource("data", source);
}

Для слоёв:

if (!map.getLayer("markers")) {
    map.addLayer(layerConfig);
}

Попытка удалить несуществующий слой

Ошибка возникает во время динамического обновления карты.

Проблемный код:

map.removeLayer("roads");

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

Безопасный вариант:

if (map.getLayer("roads")) {
    map.removeLayer("roads");
}

Аналогично следует поступать и с источниками.


Удаление источника до удаления связанных слоёв

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

Ошибка:

map.removeSource("roads");

Если существуют слои, использующие этот источник, появится исключение.

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

map.removeLayer("roads-layer");

map.removeSource("roads");

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


Некорректный GeoJSON

Очень большое количество проблем связано именно с ошибками в GeoJSON.

Пример невалидного объекта:

{
    type: "Feature",
    geometry: {
        type: "Point",
        coordinates: [55.7, 37.6]
    }
}

Отсутствует обязательное свойство:

properties

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

{
    type: "Feature",
    properties: {},
    geometry: {
        type: "Point",
        coordinates: [37.6, 55.7]
    }
}

Важно помнить порядок координат:

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

а не наоборот.


Перепутанный порядок координат

Это одна из самых распространённых логических ошибок.

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

[55.7558, 37.6176]

Разработчик предполагает:

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

MapLibre ожидает:

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

Правильно:

[37.6176, 55.7558]

При перепутанных координатах объекты могут оказаться:

  • в океане;
  • на другом континенте;
  • за пределами текущего масштаба.

Ошибки при работе с изображениями

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

Ошибка:

map.addImage("marker", image);

Если объект изображения отсутствует или не успел загрузиться, операция завершится неудачей.

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

map.loadImage("marker.png", (error, image) => {
    if (error) {
        throw error;
    }

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

Либо использование современных Promise-обёрток.


Работа с картой до завершения загрузки стиля

Событие load и загрузка конкретного стиля — не всегда одно и то же.

Ошибка:

map.setStyle(newStyle);

map.addLayer(layerConfig);

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

Правильное решение:

map.setStyle(newStyle);

map.once("styledata", () => {
    map.addLayer(layerConfig);
});

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


Утечки памяти при работе в SPA-приложениях

В React, Vue, Angular и других SPA-фреймворках часто забывают уничтожать экземпляр карты.

Ошибка:

const map = new maplibregl.Map(...);

Компонент удаляется, а карта продолжает существовать в памяти.

Правильно:

map.remove();

Например:

useEffect(() => {
    const map = new maplibregl.Map(...);

    return () => {
        map.remove();
    };
}, []);

Игнорирование очистки приводит к:

  • росту потребления памяти;
  • утечкам WebGL-ресурсов;
  • ухудшению производительности приложения.

Создание большого количества маркеров через DOM

Для небольшого числа объектов DOM-маркеры работают отлично.

Проблема появляется при тысячах объектов:

new maplibregl.Marker()

для каждой точки.

Последствия:

  • перегрузка DOM;
  • медленная отрисовка;
  • задержки интерфейса.

Для больших наборов данных предпочтительнее использовать:

  • GeoJSON-источники;
  • Circle Layer;
  • Symbol Layer;
  • кластеризацию.

Пример:

map.addSource("points", {
    type: "geojson",
    data: data,
    cluster: true
});

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


Игнорирование обработки ошибок

Многие проекты не содержат ни одной точки централизованной диагностики.

Полезная практика:

map.on("error", (event) => {
    console.error("MapLibre error:", event.error);
});

Для сетевых запросов:

fetch(url)
    .then(response => {
        if (!response.ok) {
            throw new Error("Request failed");
        }

        return response.json();
    })
    .catch(error => {
        console.error(error);
    });

Своевременное логирование значительно сокращает время поиска проблем.


Неправильная работа с асинхронностью

Часто данные загружаются позже, чем создаётся слой.

Ошибка:

map.addSource("cities", {
    type: "geojson",
    data: citiesData
});

Переменная ещё не содержит данных.

Корректный подход:

const response = await fetch("cities.geojson");
const data = await response.json();

map.addSource("cities", {
    type: "geojson",
    data
});

Либо обновление уже существующего источника:

map.getSource("cities").setData(data);

Чрезмерно частые обновления данных

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

Проблемный код:

setInterval(() => {
    source.setData(data);
}, 10);

Это создаёт значительную нагрузку:

  • на процессор;
  • на механизм рендеринга;
  • на WebGL.

Обычно используются:

  • debounce;
  • throttle;
  • пакетные обновления;
  • обновление по событию изменения данных.

Игнорирование ограничений WebGL

MapLibre использует WebGL для визуализации.

Типичные проблемы:

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

Симптомы:

  • низкий FPS;
  • подвисания карты;
  • ошибки контекста WebGL.

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

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

Отсутствие проверки существования объектов

Крупные приложения часто работают с динамически создаваемыми элементами.

Опасный код:

map.getSource("cities").setData(data);

Если источник отсутствует:

Cannot read properties of undefined

Безопасный вариант:

const source = map.getSource("cities");

if (source) {
    source.setData(data);
}

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


Использование жёстко закодированных значений

Ошибка проектирования:

center: [37.6176, 55.7558],
zoom: 12

во множестве различных файлов проекта.

Подобный подход усложняет сопровождение приложения.

Предпочтительно использовать конфигурацию:

const MAP_CONFIG = {
    center: [37.6176, 55.7558],
    zoom: 12
};

Затем обращаться к параметрам централизованно:

center: MAP_CONFIG.center,
zoom: MAP_CONFIG.zoom

Это упрощает поддержку, тестирование и масштабирование картографических приложений на основе MapLibre GL JS.