Веб-карта
Веб-карта
В этой главе мы рассмотрим
- понятие веб-карты
- типы веб-карт
- клиент-серверную архитектуру
- HTML, CSS, JavaScript
В рамках практической части создадим карту мира на основе GeoJSON-файлов с использованием открытой библиотеки MapLibre GL JS. При желании посмотрите полный код и возможный результат.
Карты в Интернете
Заголовок раздела «Карты в Интернете»Любую ли карту в Интернете можно назвать веб-картой?
Яндекс Карты — да!
Скан карты России, который я отправил по почте — пожалуй, нет.
И между ними ещё огромное множество различных вариантов карт в Интернете.
Определение веб-карты
Заголовок раздела «Определение веб-карты»Веб-карту определяет цель размещения карты в сети. Если карту разместили в Интернете для того, чтобы пользователи могли работать с ней по сети, то это веб-карта. А файлы карт, передаваемые по сети или размещаемые на сайтах, изображения карт, используемые для оформления веб-страницы, веб-картами не являются.
Типы веб-карт
Заголовок раздела «Типы веб-карт»Веб-карты можно разделить, во-первых, на интерактивные / неинтерактивные и, во-вторых, на статические / динамические. Интерактивность касается интерфейса и клиентской части, а деление на статические и динамические веб-карты связано с обработкой данных в серверной части.
| статические | динамические | |
|---|---|---|
| неинтерактивные | карты-картинки [↗] | генераторы карт-картинок [↗] |
| интерактивные | ”простые” веб-карты | картографические приложения |
Интерактивность подразумевает возможность пользователя перемещаться по карте, менять масштаб, получать подробную информацию по клику, скрывать слои, менять цвета и так далее.
Неинтерактивные карты лишены этих возможностей. Но неинтерактивная карта будет веб-картой, если предназначена для использования в Интернете.
Статические веб-карты получают данные на клиент точно в том виде, в котором они хранятся на сервере, без какой-либо обработки. Из программного обеспечения требуется только веб-сервер, который будет принимать запросы от клиентов и возвращать в ответ файлы данных с сервера клиенту.
Динамические веб-карты подразумевают серверную обработку данных и позволяют запрашивать данные более гибко. Из программного обеспечения обычно используются база данных, программа для обработки данных и веб-сервер. В общем случае веб-сервер примет запрос и обратится к программе для обработки данных, она извлечёт данные из базы, обработает их и передаст веб-серверу, который отправит подготовленные данные клиенту.
Клиент-серверная архитектура
Заголовок раздела «Клиент-серверная архитектура»Веб-ресурсы работают по клиент-серверной архитектуре. Рассмотрим пример работы веб-ресурса.

Пользователь вводит URL-адрес запрашиваемого веб-ресурса в адресную строку браузера и нажимает клавишу Enter (1). Браузер инициирует и выполняет HTTP-запрос к серверу на получение данных для создания веб-страницы (2). Сервер возвращает ответ (3). Из этого ответа браузер формирует веб-страницу, которую видит пользователь (4).
Короче
Браузер — это клиент. Сервер — это сервер. Клиент обращается к серверу с запросом, сервер возвращает клиенту ответ. Вот суть клиент-серверной архитектуры.
В реальности клиент может выполнять множество запросов к серверу, чтобы показать пользователю веб-страницу.
Первый запрос пользователь выполняет путём ввода адреса. Дальнейшие действия пользователя на веб-странице могут инициировать последующие запросы. Запрашиваться могут как веб-страницы целиком, так и отдельные данные для обновления содержания текущей веб-страницы. Если мы обновляем только часть содержания веб-страницы данными с сервера, то речь идёт об асинхронном запросе.

Запросы, выполняемые с веб-страницы
Веб-карты тоже строятся на основе клиент-серверной архитектуры. Рассмотрим интерактивную статическую карту (пока воображаемую).
Разметка, стили, логика (программный код) веб-карты, а также наборы пространственных данных веб-карты хранятся на сервере. Пользователь вводит адрес карты. Браузер запрашивает с сервера разметку веб-карты (HTML). Разметка приходит в браузер. Разметка содержит запросы к стилям (CSS) и логике веб-карты (JS). Логика веб-карты приходит в браузер и запрашивает наборы пространственных данных для создания веб-карты. Пространственные данные приходят [Взаимодействие с сервером закончено!] и кодом веб-карты превращаются в картографические слои.
В клиент-серверной архитектуре браузер это
Узнать ответ
Пользователь использует браузер (клиент), чтобы выполнить запрос к серверу
Создание первой веб-карты
Заголовок раздела «Создание первой веб-карты»Мы рассмотрели интерактивную статическую карту, а сейчас мы её сделаем своими руками!
Выделим нашей первой веб-карте отдельную папку.
В качестве среды разработки можно использовать VS Code, а чтобы локально запустить сервер — расширение Live Server.
Инициализация карты
Заголовок раздела «Инициализация карты»Создадим файл разметки index.html.
<!DOCTYPE html><html lang="en">
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Население мира</title> <!-- Запрашиваем стили 👇 --> <link rel="stylesheet" href="style.css"> <!-- Запрашиваем библиотеку MapLibre 👇 --> <script src="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.js"></script> <link href="https://unpkg.com/maplibre-gl@latest/dist/maplibre-gl.css" rel="stylesheet" /></head>
<body> <!-- Размечаем контейнер для карты 👇 --> <div id="map"></div> <!-- Запрашиваем логику карты 👇 --> <script src="main.js"></script></body>
</html>Библиотеку MapLibre мы запрашиваем из внешнего ресурса, а вот стили и логику карты нам нужно создать.
Создадим файл стилей style.css.
/* Объявляем, что контейнер карты должен занимать всю страницу */#map { position: absolute; top: 0; bottom: 0; left: 0; right: 0;}И файл с логикой карты main.js.
// Инициализируем картуconst map = new maplibregl.Map({ container: 'map', style: "https://raw.githubusercontent.com/gtitov/basemaps/refs/heads/master/positron-nolabels.json", center: [51, 0], zoom: 4});Наименование файла
index.htmlважно тем, что именно страницаindex.htmlзагружается при обращении к корневому URL. Наименования файлов CSS и JavaScript особой роли не играют.Страница HTML является корневой. Ей необходимо дать информацию о том, какие внешние библиотеки и файлы будут использоваться. Например,
style.cssиmain.jsявляются внешними файлами, а MapLibre является внешней библиотекой. Находящиеся на сервере файлы необходимо подключать по URL.
Запуск локального сервера
Заголовок раздела «Запуск локального сервера»После этого запустим Live Server, перейдём по адресу локального сервера и увидим карту.
Live Server обычно запускается по адресу
127.0.0.1:5500.127.0.0.1илиlocalhost— это внутренний адрес сервера на нашем компьютере. Он будет одним и тем же у всех компьютеров. И он недоступен для запросов снаружи. На одном веб-сервере может быть запущено несколько приложений. Для их разграничения используется порт, в нашем случае5500.
Добавление слоёв
Заголовок раздела «Добавление слоёв»Создадим подпапку data и загрузим в неё данные о странах, городах, реках и озёрах.
Получится такая структура. HTML отвечает за структуру веб-страницы, CSS за оформление веб-страницы, JavaScript за логику работы веб-страницы. GeoJSON-файлы хранят пространственные данные.
Директорияdata/ # данные
- cities.geojson # города
- countries.geojson # страны
- lakes.geojson # озёра
- rivers.geojson # реки
- index.html # разметка
- style.css # стили
- main.js # логика
Все действия с картой выполняются после первичной загрузки исходной карты.
Добавление картографических слоёв включает два шага: добавление источника данных addSource и добавление слоя addLayer. На первом шаге указываем, откуда мы будем брать данные, а на втором — как их оформить. Из одного источника можно создать несколько слоёв.
// Инициализируем карту// ... этим многоточием отмечен код, который мы уже написали// в случае сомнений всегда можно свериться с полным кодом веб-карты// ссылка на него есть в конце упражнения
map.on('load', () => { // Выполняется после загрузки карты // Добавление источника данных map.addSource('countries', { type: 'geojson', data: './data/countries.geojson', attribution: 'Natural Earth' })
// Добавление слоя map.addLayer({ id: 'countries-layer', type: 'fill', source: 'countries', paint: { 'fill-color': 'lightgray', } })})Мы добавили полигональный слой (type: 'fill'). Аналогично добавляем слой линий и слой точек.
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.addSource('rivers', { type: 'geojson', data: './data/rivers.geojson' })
map.addLayer({ id: 'rivers-layer', type: 'line', source: 'rivers', paint: { 'line-color': '#00BFFF' } })
map.addSource('lakes', { type: 'geojson', data: './data/lakes.geojson' })
map.addLayer({ id: 'lakes-layer', type: 'fill', source: 'lakes', paint: { 'fill-color': 'lightblue', 'fill-outline-color': '#00BFFF' } })
map.addSource('cities', { type: 'geojson', data: './data/cities.geojson' })
map.addLayer({ id: 'cities-layer', type: 'circle', source: 'cities', paint: { 'circle-color': 'rgb(123, 12, 234)', 'circle-radius': 3 } })})В MapLibre слои можно фильтровать и оформлять на основе атрибутов с помощью выражений.
Например, оставим только города с численностью населения больше 1 000 000.
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.addLayer({ id: 'cities-layer', type: 'circle', source: 'cities', paint: { 'circle-color': 'rgb(123, 12, 234)', 'circle-radius': 3 }, filter: ['>', ['get', 'POP_MAX'], 1000000] })})Изобразим красным (red) цветом страны, у которых атрибут MAPCOLOR7 равен 1, а остальные изобразим светло-серым (lightgray).
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.addLayer({ id: 'countries-layer', type: 'fill', source: 'countries', paint: { 'fill-color': 'lightgray', 'fill-color': ['match', ['get', 'MAPCOLOR7'], 1, 'red', 'lightgray'] } }) ...})Расширение интерактивности
Заголовок раздела «Расширение интерактивности»Созданная нами карта сразу даёт пользователю возможности перемещения, зума и даже наклона (попробуйте зажать правую кнопку мыши). Однако чтобы, например, выводить атрибутивные сведения о слое по клику, надо указать это в коде.
Отследим событие клика по слою cities-layer. Назовём событие клика переменной e. Посмотрим в консоли браузера, что собой представляет это событие. Если мы отслеживаем событие клика по конкретному слою, а не по всей карте, то мы можем обратиться к набору объектов, по которым был выполнен клик e.features.
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.on('click', ['cities-layer'], (e) => { console.log(e) console.log(e.features) })})Закомментируем вывод в консоль и выведем по клику на слой попап.
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.on('click', ['cities-layer'], (e) => { // console.log(e) // console.log(e.features) new maplibregl.Popup() // создадим попап .setLngLat(e.features[0].geometry.coordinates) // установим на координатах объекта .setHTML(e.features[0].properties.NAME) // заполним текстом из атрибута с именем объекта .addTo(map); // добавим на карту })})Попап отображается, но надо показать пользователю, что на объект можно кликать. При попадании мыши на слой cities-layer поменяем курсор на pointer, а при покидании слоя cities-layer вернём значение по умолчанию.
// Инициализируем карту...map.on('load', () => { // Выполняется после загрузки карты ... map.on('mouseenter', 'cities-layer', () => { map.getCanvas().style.cursor = 'pointer' }) map.on('mouseleave', 'cities-layer', () => { map.getCanvas().style.cursor = '' })})В качестве завершающего штриха уберём карту-подложку и добавим фон. При этом фон добавляем перед всеми слоями, так как все слои должны рисоваться после фона, поверх него.
// Инициализируем картуconst map = new maplibregl.Map({ container: 'map', style: "https://raw.githubusercontent.com/gtitov/basemaps/refs/heads/master/positron-nolabels.json", style: { "version": 8, "sources": {}, "layers": [] }, center: [51, 0], zoom: 4});
map.on('load', () => { // Выполняется после загрузки карты map.addLayer({ id: 'background', type: 'background', paint: { 'background-color': 'lightblue' } }) ...})Что мы получили
Заголовок раздела «Что мы получили»У нас получилась отличная карта!
При желании посмотрите полный код и возможный результат.
Откроем вкладку Сеть в инструментах разработчика и ещё разок проследим поток данных

- Пользователь вводит адрес карты в браузере (в клиенте)
- Клиент выполняет запрос к серверу по введённому адресу
- Сервер обрабатывает запрос и возвращает разметку (HTML) (1)
- В разметке содержатся запросы к оформлению (CSS), картографической библиотеке (MapLibre) и программной логике работы (JavaScript) веб-страницы (2)
- Клиент (браузер), получив все необходимые сведения, отображает веб-страницу
- Программная логика работы полученной веб-страницы выполняется и в соответствии с кодом инициирует запросы к данным (GeoJSON) для составления карты (3)
- Полученные данные оформляются на веб-карте в рамках логики, описанной разработчиком на языке JavaScript, с использованием функций библиотеки MapLibre
- Пользователь получает веб-карту
- Веб-карта обогащается дополнительной интерактивностью в рамках описанной разработчиком логики
Такая карта удобна, когда мы показываем небольшое количество объектов. Мы отправляем пользователю данные как они есть. Это требует минимальных серверных мощностей. Подобные веб-ресурсы называются статическими. Их легко разместить в Интернете, есть варианты бесплатной публикации.
Прежде чем браться за следующие упражнения, полезно немного погрузиться в теорию веб-картографии
Упражнения
Заголовок раздела «Упражнения»- Присвойте Москве красный цвет
- Выведите в попап один из атрибутов стран
- Добавьте слой с границами озёр, установите им толщину в 2 пикселя
- Замените курсор на перекрестие (
crosshair) при расположении поверх стран - ⁎ Внешний вид карты оставляет желать лучшего, оформите карту в соответствии с собственными представлениями о прекрасном
⁎ Это упражнение полезно делать для всех карт, которые мы создадим в рамках пособия. Самостоятельная работа над оформлением веб-карты даст ощутимый толчок в понимании процесса веб-картографирования.
Контрольные вопросы
Заголовок раздела «Контрольные вопросы»- В каком файле описывается разметка веб-страницы?
- Какой тег содержит ссылку на стили веб-страницы?
- Что означает
container: 'map'в кодеnew maplibregl.Map({ container: 'map', ... })? - Какой метод библиотеки MapLibre позволяет добавить картографический слой на карту?
- Какой тип слоя используется для отображения полигонов озёр?
- Какой текстовый формат пространственных данных может использоваться в качестве источника пространственных данных для библиотеки MapLibre?