Создание собственного стиля с нуля

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

Минимальная структура стиля выглядит следующим образом:

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

Основные компоненты:

  • version — версия спецификации стиля.
  • sources — источники данных.
  • layers — набор отображаемых слоёв.
  • sprite — набор иконок.
  • glyphs — шрифты для подписей.
  • metadata — дополнительные сведения.
  • center, zoom, bearing, pitch — стартовое положение карты.

Создание собственного стиля с нуля означает ручное описание всех элементов карты без использования готовых стилей вроде Streets, Bright или Positron.


Создание базового каркаса стиля

Первым этапом формируется минимальный JSON-файл.

{
  "version": 8,
  "name": "Custom Style",
  "sources": {},
  "layers": []
}

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

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

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


Добавление фона карты

Практически любой стиль начинается со слоя фона.

{
  "id": "background",
  "type": "background",
  "paint": {
    "background-color": "#eef3f8"
  }
}

Полный стиль:

{
  "version": 8,
  "sources": {},
  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "#eef3f8"
      }
    }
  ]
}

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


Подключение векторных тайлов

Большинство современных карт строится на основе векторных тайлов.

Добавление источника:

{
  "sources": {
    "osm": {
      "type": "vector",
      "tiles": [
        "https://example.com/tiles/{z}/{x}/{y}.pbf"
      ],
      "minzoom": 0,
      "maxzoom": 14
    }
  }
}

Параметры:

Свойство Назначение
type Тип источника
tiles URL-шаблон тайлов
minzoom Минимальный масштаб
maxzoom Максимальный масштаб

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


Добавление слоя водоёмов

Предположим, что в тайлах присутствует слой water.

Слой отображения:

{
  "id": "water",
  "type": "fill",
  "source": "osm",
  "source-layer": "water",
  "paint": {
    "fill-color": "#a8d8ff"
  }
}

Здесь:

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

Отрисовка суши

Если в данных присутствует слой landuse, можно создать отдельную заливку.

{
  "id": "land",
  "type": "fill",
  "source": "osm",
  "source-layer": "landuse",
  "paint": {
    "fill-color": "#edf4e8"
  }
}

Порядок слоёв крайне важен.

Например:

[
  {
    "id": "background"
  },
  {
    "id": "land"
  },
  {
    "id": "water"
  }
]

Каждый следующий слой рисуется поверх предыдущего.


Отрисовка дорог

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

{
  "id": "roads",
  "type": "line",
  "source": "osm",
  "source-layer": "transportation",
  "paint": {
    "line-color": "#ffffff",
    "line-width": 2
  }
}

Более реалистичный вариант:

{
  "id": "roads",
  "type": "line",
  "source": "osm",
  "source-layer": "transportation",
  "paint": {
    "line-color": "#fefefe",
    "line-width": [
      "interpolate",
      ["linear"],
      ["zoom"],
      5, 0.5,
      10, 2,
      15, 6
    ]
  }
}

Толщина линии становится зависимой от масштаба.


Использование выражений

Выражения являются фундаментом современных стилей MapLibre.

Пример изменения цвета дороги в зависимости от класса:

{
  "line-color": [
    "match",
    ["get", "class"],
    "motorway", "#ff9f43",
    "primary", "#feca57",
    "secondary", "#ffdd59",
    "#dcdde1"
  ]
}

Логика:

  1. Получить значение поля class.
  2. Сравнить его со списком вариантов.
  3. Назначить соответствующий цвет.

Разделение дорог по категориям

Нередко вместо одного слоя создаётся несколько.

Автомагистрали:

{
  "id": "motorways",
  "type": "line",
  "source": "osm",
  "source-layer": "transportation",
  "filter": [
    "==",
    ["get", "class"],
    "motorway"
  ],
  "paint": {
    "line-color": "#ff8c42",
    "line-width": 4
  }
}

Второстепенные дороги:

{
  "id": "secondary",
  "type": "line",
  "source": "osm",
  "source-layer": "transportation",
  "filter": [
    "==",
    ["get", "class"],
    "secondary"
  ],
  "paint": {
    "line-color": "#ffffff",
    "line-width": 2
  }
}

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


Добавление подписей

Для отображения текста необходимо подключить шрифты.

В корне стиля:

{
  "glyphs": "https://example.com/fonts/{fontstack}/{range}.pbf"
}

Затем создаётся слой подписей.

{
  "id": "city-labels",
  "type": "symbol",
  "source": "osm",
  "source-layer": "place",
  "layout": {
    "text-field": ["get", "name"],
    "text-font": [
      "Noto Sans Regular"
    ],
    "text-size": 14
  },
  "paint": {
    "text-color": "#333333"
  }
}

Подпись автоматически берётся из атрибута name.


Настройка масштабируемых подписей

Размер текста обычно изменяется вместе с масштабом.

{
  "text-size": [
    "interpolate",
    ["linear"],
    ["zoom"],
    5, 10,
    10, 14,
    15, 20
  ]
}

Результат:

Zoom Размер
5 10
10 14
15 20

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


Контур текста

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

{
  "paint": {
    "text-color": "#2d3436",
    "text-halo-color": "#ffffff",
    "text-halo-width": 2
  }
}

Подобный приём применяется практически во всех профессиональных картографических стилях.


Подключение набора иконок

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

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

{
  "sprite": "https://example.com/sprites/sprite"
}

Затем создаётся слой.

{
  "id": "poi",
  "type": "symbol",
  "source": "osm",
  "source-layer": "poi",
  "layout": {
    "icon-image": "restaurant"
  }
}

Имя restaurant должно существовать внутри спрайта.


Работа с фильтрами

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

Например, отображение парков:

{
  "filter": [
    "==",
    ["get", "class"],
    "park"
  ]
}

Несколько условий:

{
  "filter": [
    "all",
    [
      "==",
      ["get", "class"],
      "park"
    ],
    [
      ">",
      ["zoom"],
      10
    ]
  ]
}

Слой будет виден только начиная с десятого масштаба.


Создание многоуровневой дорожной схемы

Классическая техника заключается в рисовании дороги двумя слоями.

Подложка:

{
  "id": "road-casing",
  "type": "line",
  "paint": {
    "line-color": "#b2bec3",
    "line-width": 8
  }
}

Основная линия:

{
  "id": "road-fill",
  "type": "line",
  "paint": {
    "line-color": "#ffffff",
    "line-width": 5
  }
}

Получается эффект дорожного контура, характерный для большинства навигационных карт.


Стилизация зданий

Для зданий применяется тип fill.

{
  "id": "buildings",
  "type": "fill",
  "source": "osm",
  "source-layer": "building",
  "paint": {
    "fill-color": "#dfe6e9",
    "fill-outline-color": "#b2bec3"
  }
}

Такое оформление хорошо подходит для светлых картографических тем.


Создание трёхмерных зданий

MapLibre поддерживает экструзию полигонов.

{
  "id": "3d-buildings",
  "type": "fill-extrusion",
  "source": "osm",
  "source-layer": "building",
  "paint": {
    "fill-extrusion-color": "#c8d6e5",
    "fill-extrusion-height": [
      "get",
      "height"
    ],
    "fill-extrusion-base": 0,
    "fill-extrusion-opacity": 0.9
  }
}

Дополнительно задаётся наклон карты:

map.setPitch(60);

Использование цветовых тем

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

Пример светлой темы:

Элемент Цвет
Фон #eef3f8
Вода #a8d8ff
Парки #c8e6c9
Здания #dfe6e9
Дороги #ffffff
Текст #2d3436

Пример тёмной темы:

Элемент Цвет
Фон #101418
Вода #243447
Парки #1e3d2f
Здания #2f3640
Дороги #5f6770
Текст #ecf0f1

Наличие единой палитры значительно упрощает дальнейшее развитие стиля.


Оптимизация количества слоёв

Каждый слой требует отдельной обработки при рендеринге.

Распространённые способы оптимизации:

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

Пример ограничения масштаба:

{
  "minzoom": 12,
  "maxzoom": 18
}

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


Организация структуры большого стиля

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

  1. Фон.
  2. Землепользование.
  3. Вода.
  4. Парки.
  5. Железные дороги.
  6. Автомобильные дороги.
  7. Здания.
  8. Границы.
  9. Иконки.
  10. Подписи.

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

Пример структуры массива слоёв:

{
  "layers": [
    { "id": "background" },
    { "id": "land" },
    { "id": "water" },
    { "id": "parks" },
    { "id": "railways" },
    { "id": "roads" },
    { "id": "buildings" },
    { "id": "boundaries" },
    { "id": "poi-icons" },
    { "id": "labels" }
  ]
}

Полный минимальный пример собственного стиля

{
  "version": 8,
  "name": "Custom Basic Style",

  "glyphs": "https://example.com/fonts/{fontstack}/{range}.pbf",

  "sources": {
    "osm": {
      "type": "vector",
      "tiles": [
        "https://example.com/tiles/{z}/{x}/{y}.pbf"
      ]
    }
  },

  "layers": [
    {
      "id": "background",
      "type": "background",
      "paint": {
        "background-color": "#eef3f8"
      }
    },

    {
      "id": "water",
      "type": "fill",
      "source": "osm",
      "source-layer": "water",
      "paint": {
        "fill-color": "#a8d8ff"
      }
    },

    {
      "id": "roads",
      "type": "line",
      "source": "osm",
      "source-layer": "transportation",
      "paint": {
        "line-color": "#ffffff",
        "line-width": 2
      }
    },

    {
      "id": "buildings",
      "type": "fill",
      "source": "osm",
      "source-layer": "building",
      "paint": {
        "fill-color": "#dfe6e9"
      }
    },

    {
      "id": "labels",
      "type": "symbol",
      "source": "osm",
      "source-layer": "place",
      "layout": {
        "text-field": [
          "get",
          "name"
        ],
        "text-font": [
          "Noto Sans Regular"
        ],
        "text-size": 14
      },
      "paint": {
        "text-color": "#333333",
        "text-halo-color": "#ffffff",
        "text-halo-width": 2
      }
    }
  ]
}

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