Стиль в MapLibre GL JS представляет собой JSON-документ, описывающий все аспекты отображения карты: источники данных, слои, правила визуализации, изображения, шрифты и параметры взаимодействия. Любая ошибка в структуре такого документа способна привести к неправильному отображению карты, исчезновению слоёв, сбоям при загрузке данных или труднообнаружимым визуальным дефектам.
Валидация стилей — это процесс проверки документа стиля на соответствие спецификации. Она позволяет обнаруживать ошибки ещё до публикации приложения или загрузки карты в браузере.
Основные задачи валидации:
Типичный стиль содержит несколько ключевых разделов:
{
"version": 8,
"sources": {},
"layers": []
}
Каждый из них подлежит отдельной проверке.
Поле version является обязательным.
Пример:
{
"version": 8
}
Если указать неподдерживаемую версию:
{
"version": 7
}
валидатор сообщит об ошибке.
MapLibre GL JS ориентируется на спецификацию стилей версии 8, поэтому любые другие значения считаются некорректными.
Перед анализом структуры выполняется базовая проверка корректности JSON.
Некорректный документ:
{
"version": 8,
"sources": {
"osm": {
"type": "vector",
}
}
}
Ошибка возникает из-за лишней запятой.
Другой пример:
{
version: 8
}
Здесь отсутствуют обязательные кавычки вокруг имени свойства.
Подобные ошибки обнаруживаются ещё до проверки спецификации MapLibre.
Раздел sources описывает наборы данных, используемых
слоями.
Корректный источник:
{
"sources": {
"cities": {
"type": "geojson",
"data": "cities.geojson"
}
}
}
Некорректный пример:
{
"sources": {
"cities": {
"data": "cities.geojson"
}
}
}
Ошибка:
source.type is required
Некорректно:
{
"sources": {
"cities": {
"type": "database"
}
}
}
Допустимыми являются только типы, поддерживаемые спецификацией:
vectorrastergeojsonimagevideocanvasРаздел layers содержит описание визуализации.
Пример корректного слоя:
{
"id": "roads",
"type": "line",
"source": "transport"
}
Каждый слой обязан иметь:
idtypeДля большинства типов также требуется:
sourceНекорректный пример:
{
"layers": [
{
"type": "fill"
}
]
}
Ошибка:
layer.id is required
Идентификатор слоя должен быть уникальным.
Некорректно:
{
"layers": [
{
"id": "roads",
"type": "line"
},
{
"id": "roads",
"type": "fill"
}
]
}
Ошибка:
duplicate layer id: roads
Уникальность обеспечивает возможность корректного управления слоями через API.
Слой может использовать только существующий источник.
Некорректно:
{
"sources": {},
"layers": [
{
"id": "cities",
"type": "circle",
"source": "geo"
}
]
}
Источник geo отсутствует.
Ошибка:
source "geo" not found
Для векторных тайлов требуется свойство
source-layer.
Корректный вариант:
{
"id": "buildings",
"type": "fill",
"source": "tiles",
"source-layer": "building"
}
Некорректно:
{
"id": "buildings",
"type": "fill",
"source": "tiles"
}
Если источник является векторным, отсутствие
source-layer может привести к невозможности отображения
данных.
Каждое свойство должно иметь ожидаемый тип данных.
Пример:
{
"paint": {
"circle-radius": 10
}
}
Корректно.
Некорректно:
{
"paint": {
"circle-radius": "large"
}
}
Ошибка:
number expected
Валидатор отслеживает соответствие типов:
| Свойство | Ожидаемый тип |
|---|---|
| circle-radius | number |
| line-width | number |
| fill-color | color |
| visibility | string |
| text-field | string |
Цветовые параметры проходят отдельную проверку.
Корректно:
{
"fill-color": "#ff0000"
}
Также допустимы:
{
"fill-color": "red"
}
или
{
"fill-color": "rgba(255,0,0,1)"
}
Некорректный вариант:
{
"fill-color": "#xyz"
}
Ошибка:
invalid color value
Многие свойства имеют ограничения.
Пример:
{
"circle-opacity": 0.5
}
Допустимо.
Некорректно:
{
"circle-opacity": 5
}
Ошибка:
value must be between 0 and 1
Аналогичные ограничения существуют для:
Некоторые свойства допускают строго определённый набор значений.
Пример:
{
"layout": {
"visibility": "visible"
}
}
Корректно.
Некорректно:
{
"layout": {
"visibility": "show"
}
}
Ошибка:
expected one of [visible, none]
Такая проверка защищает от опечаток.
Современные стили активно используют Expressions.
Пример:
{
"circle-radius": [
"interpolate",
["linear"],
["zoom"],
5, 2,
15, 10
]
}
Валидатор проверяет:
Некорректный пример:
[
"interpolate",
["zoom"],
5,
2
]
Ошибка связана с отсутствием интерполятора.
Для каждого свойства выражение должно возвращать значение подходящего типа.
Корректно:
{
"circle-radius": [
"get",
"size"
]
}
если поле содержит число.
Некорректно:
{
"circle-radius": [
"get",
"name"
]
}
если свойство возвращает строку.
Результат:
expected number but found string
Фильтры также проходят валидацию.
Корректный пример:
{
"filter": [
"==",
["get", "type"],
"city"
]
}
Некорректно:
{
"filter": [
"=="
]
}
Ошибка:
not enough arguments
Проверяется не только структура, но и совместимость типов сравниваемых данных.
Стили могут ссылаться на изображения.
Пример:
{
"layout": {
"icon-image": "airport"
}
}
Если изображение отсутствует в спрайте, в процессе выполнения может появиться предупреждение.
Валидация помогает выявлять:
Для символических слоёв проверяются настройки текста.
Пример:
{
"layout": {
"text-font": [
"Open Sans Regular"
]
}
}
Ошибки возникают при:
Некоторые ошибки связаны не со структурой, а с логикой расположения слоёв.
Например:
map.addLayer(layer, "roads");
Если слой roads отсутствует, появится ошибка.
Хотя подобная проблема относится к времени выполнения, предварительная валидация конфигурации помогает избежать подобных ситуаций.
Для проверки стиля существует валидатор спецификации.
Пример:
import { validateStyle } from "@maplibre/maplibre-gl-style-spec";
const errors = validateStyle(style);
Результатом будет массив найденных ошибок.
Проверка:
if (errors.length > 0) {
console.error(errors);
}
Типичный результат:
[
{
message: "layers[0].id is required"
}
]
Практика промышленной разработки предполагает обязательную проверку стиля до инициализации карты.
const errors = validateStyle(style);
if (errors.length) {
throw new Error(
errors.map(e => e.message).join("\n")
);
}
const map = new maplibregl.Map({
container: "map",
style
});
Такой подход предотвращает запуск приложения с повреждённой конфигурацией.
Во многих проектах стиль хранится отдельно:
styles/
├─ dark.json
├─ light.json
└─ satellite.json
Во время сборки можно проверять каждый файл.
Пример для Node.js:
import fs from "fs";
import { validateStyle } from "@maplibre/maplibre-gl-style-spec";
const style = JSON.parse(
fs.readFileSync("style.json", "utf8")
);
const errors = validateStyle(style);
if (errors.length) {
process.exit(1);
}
Подобная схема позволяет обнаруживать ошибки ещё до публикации приложения.
Валидация часто становится частью конвейера непрерывной интеграции.
Типовой сценарий:
Преимущества такого подхода:
Некорректно:
{
"paint": {
"circle-raduis": 5
}
}
Ошибка:
unknown property
Некорректно:
{
"type": "line",
"paint": {
"circle-radius": 5
}
}
Ошибка:
property not supported
Некорректно:
{
"text-font": "Open Sans"
}
Ожидается:
{
"text-font": [
"Open Sans"
]
}
Некорректно:
{
"sources": {
"geo": {
"type": "geojson"
}
}
}
Отсутствует поле:
"data"
Некорректно:
{
"line-width": true
}
Ожидается числовое значение.
Проверять стиль до публикации. Даже небольшая ошибка способна сделать карту неработоспособной.
Использовать автоматическую валидацию в CI/CD. Ручная проверка быстро становится ненадёжной при росте проекта.
Хранить стили под контролем версий. Это упрощает поиск момента появления ошибки.
Проверять выражения отдельно. Большинство сложных ошибок возникает именно в блоках Expressions.
Не игнорировать предупреждения валидатора. Предупреждение часто указывает на потенциальную проблему, которая проявится позднее после обновления MapLibre GL JS или изменения структуры данных.
Поддерживать соответствие спецификации. Использование актуальных возможностей спецификации значительно снижает риск несовместимости между различными инструментами и версиями библиотек экосистемы MapLibre.