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

Источник (source) в MapLibre GL JS представляет собой объект, поставляющий географические данные для отображения на карте. Все визуальные слои (layers) получают данные именно из источников. Если источник настроен неправильно, отсутствует, недоступен или содержит ошибки в данных, это приводит к сбоям отображения, пустым слоям, ошибкам консоли и проблемам производительности.

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

Основные типы источников:

  • GeoJSON Source
  • Vector Tile Source
  • Raster Source
  • Raster DEM Source
  • Image Source
  • Video Source

Каждый тип имеет собственные особенности и набор типичных проблем.


Ошибка «Source not found»

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

Некорректный пример:

map.addLayer({
    id: 'cities',
    type: 'circle',
    source: 'city-data'
});

Если источник не был зарегистрирован:

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

в консоли появится сообщение наподобие:

Source "city-data" not found

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

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

map.addSource('city-data', {
    type: 'geojson',
    data: 'cities.geojson'
});

map.addLayer({
    id: 'cities',
    type: 'circle',
    source: 'city-data'
});

Полезно придерживаться единого соглашения об именовании источников и слоёв.


Добавление слоя раньше источника

MapLibre обрабатывает слои и источники последовательно. Если слой создаётся раньше соответствующего источника, возникает ошибка.

Некорректно:

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

map.addSource('roads-source', {
    type: 'geojson',
    data: roadsData
});

Корректно:

map.addSource('roads-source', {
    type: 'geojson',
    data: roadsData
});

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

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


Работа до события load

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

Ошибка:

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

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

В некоторых ситуациях карта ещё не готова принимать изменения.

Рекомендуемый подход:

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

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

});

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


Проблемы загрузки GeoJSON

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

Пример подключения:

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

Возможные причины неудачной загрузки:

  • неверный путь к файлу;
  • файл отсутствует на сервере;
  • повреждённый JSON;
  • неправильный MIME-тип;
  • ошибки CORS.

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


Некорректный формат GeoJSON

MapLibre ожидает корректную структуру GeoJSON согласно спецификации.

Ошибка:

{
    "name": "Park"
}

Такой объект не является GeoJSON-документом.

Корректный пример:

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

При сомнениях полезно использовать специализированные валидаторы GeoJSON.


Ошибки координат

Часто данные присутствуют, но объекты не отображаются на карте.

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

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

[longitude, latitude]

Пример:

[37.6176, 55.7558]

Ошибка:

[55.7558, 37.6176]

MapLibre интерпретирует координаты строго как:

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

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


Координаты вне допустимого диапазона

Допустимые диапазоны:

Широта

от -90 до 90

Долгота

от -180 до 180

Некорректные значения:

[250, 120]

могут вызывать ошибки отображения или приводить к игнорированию объектов.

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


Ошибки при использовании Vector Tile Source

Подключение векторных тайлов выглядит следующим образом:

map.addSource('osm', {
    type: 'vector',
    tiles: [
        'https://server.com/tiles/{z}/{x}/{y}.pbf'
    ]
});

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

  • сервер возвращает 404;
  • сервер возвращает HTML вместо PBF;
  • отсутствует слой внутри тайла;
  • ошибка имени source-layer;
  • неверная схема адресации тайлов.

Неверное значение source-layer

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

source-layer

Пример:

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

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

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


Ошибки CORS

Браузер может блокировать загрузку данных с другого домена.

Пример запроса:

https://api.example.com/data.geojson

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

Access-Control-Allow-Origin

Результат:

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

Решение находится на стороне сервера и заключается в корректной настройке CORS-заголовков.


Источник успешно создан, но данные не отображаются

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

map.getSource('cities')

возвращает объект, однако слой пустой.

Следует проверить:

  1. Масштаб отображения.
  2. Видимость слоя.
  3. Фильтры.
  4. Координаты объектов.
  5. Цвета и прозрачность.
  6. Ограничения по масштабу.

Например:

minzoom: 10

скроет слой при масштабе меньше 10.


Ошибки при обновлении GeoJSON

Источник может обновляться динамически.

Пример:

map.getSource('vehicles').setData(newData);

Ошибка возникает, если источник ещё не существует:

Cannot read properties of undefined

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

const source = map.getSource('vehicles');

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

Частые обновления данных

Некоторые приложения обновляют данные каждую секунду:

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

Для небольших наборов данных это допустимо.

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

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

В таких случаях следует:

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

Проблемы памяти при больших GeoJSON

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

Например:

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

Если коллекция содержит сотни тысяч объектов, возможны:

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

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


Ошибки кластеризации

GeoJSON поддерживает встроенную кластеризацию.

Источник:

map.addSource('earthquakes', {
    type: 'geojson',
    data: data,
    cluster: true
});

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

  • слой кластеров отсутствует;
  • слой обычных точек перекрывает кластеры;
  • неверные фильтры;
  • неправильные значения clusterRadius.

Пример слишком большого радиуса:

clusterRadius: 500

В результате почти все точки объединяются в один кластер.


Ошибки Image Source

Источник изображения:

map.addSource('overlay', {
    type: 'image',
    url: '/images/map.png',
    coordinates: [...]
});

Проблемы:

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

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


Ошибки Video Source

Видеоисточник:

map.addSource('video', {
    type: 'video',
    urls: [
        'video.mp4'
    ],
    coordinates: [...]
});

Распространённые причины проблем:

  • неподдерживаемый кодек;
  • ошибка загрузки файла;
  • ограничения браузера;
  • неверные координаты размещения.

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


Проблемы Raster Source

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

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

Возможные ошибки:

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

Особенно часто возникают проблемы при смешивании HTTP и HTTPS ресурсов.


Ошибка смешанного контента

Если карта открыта через HTTPS:

https://site.com

а источник использует HTTP:

http://tiles.com

браузер может заблокировать загрузку.

В консоли появляется сообщение:

Mixed Content

Все внешние ресурсы рекомендуется загружать через HTTPS.


Удаление источников

Источник нельзя удалить, пока существуют связанные слои.

Ошибка:

map.removeSource('roads');

при наличии слоя:

source: 'roads'

приведёт к исключению.

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

map.removeLayer('roads-layer');

map.removeSource('roads');

Сначала удаляются все зависимые слои, затем источник.


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

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

Пример:

if (!map.getSource('cities')) {

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

}

Это предотвращает ошибки вида:

Source already exists

при повторной инициализации компонентов.


Отладка источников

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

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

console.log(
    map.getSource('cities')
);

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

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

Контроль сетевых запросов:

Developer Tools → Network

Просмотр данных GeoJSON:

fetch('/data.geojson')
    .then(r => r.json())
    .then(console.log);

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

map.isSourceLoaded('cities');

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