Валидация стилей

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

Валидация стилей — это процесс проверки документа стиля на соответствие спецификации. Она позволяет обнаруживать ошибки ещё до публикации приложения или загрузки карты в браузере.

Основные задачи валидации:

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

Структура стиля как объект проверки

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

{
  "version": 8,
  "sources": {},
  "layers": []
}

Каждый из них подлежит отдельной проверке.

Проверка версии спецификации

Поле version является обязательным.

Пример:

{
  "version": 8
}

Если указать неподдерживаемую версию:

{
  "version": 7
}

валидатор сообщит об ошибке.

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


Проверка JSON-синтаксиса

Перед анализом структуры выполняется базовая проверка корректности 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"
    }
  }
}

Допустимыми являются только типы, поддерживаемые спецификацией:

  • vector
  • raster
  • geojson
  • image
  • video
  • canvas
  • другие поддерживаемые реализацией типы

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

Раздел layers содержит описание визуализации.

Пример корректного слоя:

{
  "id": "roads",
  "type": "line",
  "source": "transport"
}

Проверка обязательных свойств

Каждый слой обязан иметь:

  • id
  • type

Для большинства типов также требуется:

  • 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

Для векторных тайлов требуется свойство 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 отсутствует, появится ошибка.

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


Использование validateStyle

Для проверки стиля существует валидатор спецификации.

Пример:

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);
}

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


Интеграция в CI/CD

Валидация часто становится частью конвейера непрерывной интеграции.

Типовой сценарий:

  1. Разработчик изменяет стиль.
  2. Запускаются автоматические тесты.
  3. Выполняется проверка JSON.
  4. Выполняется проверка спецификации.
  5. При наличии ошибок сборка отклоняется.

Преимущества такого подхода:

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

Типичные ошибки, обнаруживаемые валидатором

Опечатки в названиях свойств

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

{
  "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.