
15 мая 1935 была открыта первая очередь Московского метрополитена. С этого момента началась новая эра в истории города, в котором подземка, безусловно, играет важную роль.
Любопытная деталь, что в течение следующих десятилетий схема метро изображалась с привязкой к реальному расположению на карте города. И только в семидесятых годах прошлого века, когда общее количество станций приблизилось к сотне, начали использовать схематичное изображение линий метро, которое оказалось удобнее и проще для восприятия пассажирами.
Упрощенная схема читается быстрее. Но, в то же время, не дает представления о реальном расположении линий, скрывает детали. Длинные перегоны метро ничем не отличаются от коротких. Нет привязки к географии.
Давайте создадим интерактивную карту линий Московского метрополитена и посмотрим на его историю — как развивалась одна из самых больших сетей мира.
Для создания такой карты нам потребуются:
-
Механизм для отображения карты
-
Удобная модель хранения данных
-
Сами данные
Механизм
Для начала, определяемся с требования к механизму. Он должен уметь:
-
отображать базовый слой с «обычной» картой — дорогами, водоемами, улицами, зданиями и т.д.;
-
отображать поверх базового слоя круги и линии, которые будут изображать станции и линии метро соответственно;
-
обновлять отрисованные станции и линии метро в зависимости от выбранной даты;
-
переключать текущую дату в интервале от 15 мая 1935 года (запуск первой линии метро) до настоящего времени.
Первые три пункта из коробки есть в open source библиотеке Leaflet. Четвертый будем реализовывать самостоятельно.
Базовый слой карты
Пример 1: Создание базового слоя карты
<html> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin=""/> <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script> </head> <body> <div id="map" style="width: 720px; height: 400px;"></div> <script> // Создаем карту const mymap = L.map('map').setView([55.754181, 37.622821], 12); // Создаем tile layer и добавляем его на карту const osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { minZoom: 1, maxZoom: 18, attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(mymap); </script> </body> </html>

Что такое tile server?
Небольшое пояснение для тех, кто никогда не сталкивался с понятиями тайлов (tiles) и тайлового сервера (tile server).
Онлайн карты, к которым привыкло большинство пользователей, — OpenStreetMap, Яндекс.Карты, 2ГИС и т.п. — состоят из тайлов (tile): квадратов с длиной стороны в 256 пикселей.
Каждый тайл имеет координаты x, y и z:
-
z — масштаб, при котором он отображается (чем больше значение z, тем больше увеличение);
-
x — порядковый номер тайла по горизонтали при выбранном масштабе z;
-
y — порядковый номер тайла по вертикали при выбранном масштабе z.
Например, тайл с координатами x=2476, y=1280 и z=12 выглядит вот так:

Эти “квадраты” хранятся на тайловом сервере (tile server) в определенной структуре. Например, тайл выше доступен по адресу https://tile.openstreetmap.org/12/2476/1280.png
Полное изображение, которое мы, в конечном счете, видим, склеивается из множества тайлов в единую картину:

Когда мы работаем с картой в браузере — изменяем масштаб или прокручиваем в стороны — нужные тайлы, по мере необходимости, загружаются с сервера.
В нашем случае в качестве тайлового сервера используется сервер tile.openstreetmap.org.
Станции и линии метро
Для отображения станций метро используем circleMarker: круг с заданными в пикселях радиусом, цветом обводки и заливки, прозрачностью и прочими свойствами.
Пример 2: Добавляем станцию метро на карту
Создаем красную окружность с координатами 55.7578487° северной широты и 37.6163659° восточной долготы (станция «Охотный ряд»), с белой заливкой, радиусом 6 пикселей и добавляем его на карту:
L.circleMarker([55.7578487, 37.6163659], { radius: 6, width: 2.5, color: "red", fillColor: "white", fillOpacity: 1 }).bindPopup("Охотный Ряд") .addTo(mymap);

Для отображения линий метро будем использовать Polyline: ломаную с заданной шириной, цветом, прозрачностью и другими параметрами.
Пример: Добавляем линию метро на карту
Добавим первую очередь московского метро, открытую 15 мая 1935 года. Это будет ломаная красного цвета, шириной 4, проходящая через соответствующие станции метро.
const plln = [ [ [55.7888499, 37.6801924], // Сокольники [55.7798626, 37.6666841], // Красносельская [55.7754719, 37.6566036], // Комсомольская [55.7691137, 37.64887], // Красные Ворота [55.7658915, 37.6388871], // Кировская [55.7598058, 37.6264977], // Дзержинская [55.7578487, 37.6163659], // Охотный Ряд [55.7515247, 37.6102524], // Библиотека имени Ленина [55.7454796, 37.6036755], // Дворец Советов [55.7352856, 37.5940414] // Парк культуры имени Горького ], [ [55.7578487, 37.6163659], // Охотный Ряд [55.7524242, 37.6086732], // Улица Коминтерна [55.7519189, 37.6007512], // Арбатская [55.7488633, 37.5826955] // Смоленская ] ]; L.polyline(plln, { color: "red", weight: 6, }).bindPopup("Кировско-Фрунзенская линия") .addTo(mymap);
Обратим внимание, что при движении от Сокольников, после Охотного ряда, была развилка. Поезда следовали в двух направлениях: в сторону Смоленской и в сторону Парка культуры имени Горького.

Удаление и добавление новых кругов (станций) и ломаных (линий метро) также является встроенными функциями Leaflet — это потребуется для перерисовки схемы метро в зависимости от выбранной даты.
Переключение текущей даты
Следующим шагом нужно будет прикрутить слайдер с диапазоном от «15 мая 1935» и до сего дня. С помощью него мы будем задавать «текущую» дату для отображения схемы метро, какой она была в этот день.
Алгоритм прост: при изменении значения слайдера выполняем по порядку:
-
очищаем текущую схему,
-
проходим по всем объектам (станциям и линиям) в “базе”,
-
выбираем те объекты, которые существовали в новую выбранную дату,
-
отображаем их на карте.
Чтобы определить, какие объекты существовали в указанный день, необходимы правильным образом подготовленные данные. Например, нужно сохранить в “базе” детали о том, что с момента открытия в 1935 году и до 1990 года станция метро «Чистые пруды» называлась «Кировской». Или, например, что первая версия станции «Первомайская» полностью закрылась в 1961 году.
Чем проще и удобнее будет модель хранения данных, тем удобнее будет работа с самими данными.
Модель
За основу возьмем готовое решение, прошедшим проверку временем — модель данных OpenStreetMap. И доработаем ее до наших целей там, где её будет не хватать.
В OSM все данные состоят из трех видов элементов (elements): точки (node), линии (way) и отношения (relation).
Точка (Node)
Node — простейший элемент, который хранит в себе координаты широты и долготы.
Пример Node
Nodes, с координатами станций первой линии метро на момент её открытия 15 мая 1935 года в нотации OSM XML формате (без служебных данных):
<!-- Сокольники --> <node id="1" lon="55.7888499" lat="37.6801924"/> <!-- Красносельская --> <node id="2" lon="55.7798626" lat="37.6666841"/> <!-- Комсомольская --> <node id="3" lon="55.7754719" lat="37.6566036"/> <!-- Красные Ворота --> <node id="4" lon="55.7691137" lat="37.64887"/> <!-- Кировская --> <node id="5" lon="55.7658915" lat="37.6388871"/> <!-- Дзержинская --> <node id="6" lon="55.7598058" lat="37.6264977"/> <!-- Охотный Ряд --> <node id="7" lon="55.7578487" lat="37.6163659"/> <!-- Библиотека имени Ленина --> <node id="8" lon="55.7515247" lat="37.6102524"/> <!-- Дворец Советов --> <node id="9" lon="55.7454796" lat="37.6036755"/> <!-- Парк культуры имени Горького --> <node id="10" lon="55.7352856" lat="37.5940414"/> <!-- Смоленская --> <node id="11" lon="55.7488633" lat="37.5826955"/> <!-- Арбатская --> <node id="12" lon="55.7519189" lat="37.6007512"/> <!-- Улица Коминтерна --> <node id="13" lon="55.7524242" lat="37.6086732"/>
Линия (Way)
Way — упорядоченная ломаная линия, состоящая из последовательно заданных точек (nodes).
Пример Way
<!-- Участок «Сокольники — Парк Культуры имени Горького» --> <way id="1"> <nd rf="1"/> <nd rf="2"/> <nd rf="3"/> <nd rf="4"/> <nd rf="5"/> <nd rf="6"/> <nd rf="7"/> <nd rf="8"/> <nd rf="9"/> <nd rf="10"/> </way> <!-- Участок «Охотный ряд — Смоленская» --> <way id=”2”> <nd rf="7"/> <nd rf="13"/> <nd rf="12"/> <nd rf="11"/> </way>
Отношение (Relation)
Отношение (relation) — это группировка элементов (elements) по определенному признаку. В отношение могут входит точки (nodes), линии (ways), а также, другие отношения (relations).
Пример Relation
Например, объединение всех станций и участков метро могут объединяться под одним отношением (relation) — «Московский метрополитен».
<!-- Московский метрополитен --> <relation id="1"> <member type="node" ref="1" role=""/> <member type="node" ref="2" role=""/> … <member type="node" ref="13" role=""/> <member type="way" ref="1 role=""/> <member type="way" ref="2" role=""/> </relation>
Тег (Tag)
Также, у каждого из элементов (elements) могут быть заданы теги: пара «ключ=значение». Теги определяют свойства объекта, и используются для описания того, как объекты будут отображаться на карте.
Пример Tags
Например, станция метро может иметь такой набор тегов
<!-- Сокольники --> <node id="1" lon="55.7888499" lat="37.6801924"> <tag k=”name” v=”Сокольники”/> <tag k=”type” v=”subway_station”/> <tag k=”color” v=”red”/> </node>
Доработка модели данных OSM
OpenStreetMap создана и развивается для отображения актуальных деталей — того, что существует в данный момент. Для некоторых типов объектов есть возможность указать год их возникновения (например, год постройки здания). Но про объекты, которых уже не существует, или которые изменялись в прошлом, данных нет.
Чтобы это исправить, к каждому объекту, который может отображаться на карте, добавляем две характеристики:
-
from_date — дата, с которой объект появляется на карте
-
to_date — дата, с которой объект более не должен отображаться на карте (например, временная станция Калужская: from_date = 1964-04-15, to_date = 1974-08-12).
С помощью этих характеристик можно будет управлять отображаемыми на карте данными, в зависимости от выбранной даты.
Собираем всё вместе
Точки (nodes) и линии (ways) будут использоваться для определения геометрии — точек и ломаных линий, из которых состоят все объекты. У них есть только уникальные идентификаторы (id) и их координаты: широта и долгота.
Отношения (relations) будут использоваться для определения самих объектов (станция или линия) и их характеристик. Relations состоят из комбинации nodes или ways, и дополняются необходимыми тегами. Теги from_date и to_date являются обязательными.
Такой подход позволяет использовать одни и те же nodes и ways повторно: вся информация об объекте хранится на уровне relation.
Например, чтобы отобразить, что станция метро Кировская была переименована в Чистые пруды, будет использовано два relation. Один: с момента открытия до момента переименования. Второй: с момента переименования и до настоящего времени.
Пример relations для переименованной станции метро
<relation id=”2”> <member type=”node” ref=”5”/> <tag k=”name” value=”Кировская”/> <tag k=”type” value=”subway_station”/> <tag k=”color” value=”red”/> <tag k=”from_date” value=”1935-05-15”/> <tag k=”to_date” value=”1990-11-05”/> </relation> <relation id=”3”> <member type=”node” ref=”5”/> <tag k=”name” value=”Чистые пруды”/> <tag k=”type” value=”subway_station”/> <tag k=”color” value=”red”/> <tag k=”from_date” value=”1990-11-05”/> <tag k=”to_date” value=”now”/> </relation>

Аналогичная история с участками линий. Например, если с 1935 года участок путей принадлежал Сокольнической линии, а с 1938 года стал участком Арбатской линии, то нам потребуется три relation.
Пример relations для изменения линий
<relation id=”247”> <member type=”way” ref=”1”/> <member type=”way” ref=”102”/> <member type=”way” ref=”2”/> <member type=”way” ref=”3”/> <member type=”way” ref=”4”/> <tag k=”name” value=”Кировско-Фрунзенская линия”/> <tag k=”type” value=”subway_line”/> <tag k=”color” value=”red”/> <tag k=”from_date” value=”1937-03-20”/> <tag k=”to_date” value=”1938-03-13”/> </relation> <relation id=”248”> <member type=”way” ref=”1”/> <member type=”way” ref=”102”/> <tag k=”name” value=”Кировско-Фрунзенская линия”/> <tag k=”type” value=”subway_line”/> <tag k=”color” value=”red”/> <tag k=”from_date” value=”1938-03-13”/> <tag k=”to_date” value=”1957-05-01”/> </relation> <relation id=”249”> <member type=”way” ref=”3”/> <member type=”way” ref=”4”/> <member type=”way” ref=”5”/> <member type=”way” ref=”6”/> <tag k=”name” value=”Арбатско-покровская линия”/> <tag k=”type” value=”subway_line”/> <tag k=”color” value=”blue”/> <tag k=”from_date” value=”1938-03-13”/> <tag k=”to_date” value=”1944-01-18”/> </relation>

Данные
Подготовка данных состояла из трех шагов.
Первым шагом была начальная загрузка данных из википедии со страницы список станций Московского метрополитена, которая содержит всю нужную информацию — список станций, их координаты, дату открытия. Все детали были загружены в электронные таблицы Google документов для дальнейшей обработки и доработки.
Второй шаг — уточнение и добавление деталей по тем станциям и линиям, «свойства» которых изменялись.
Лирическое отступление
Переименование станций метро — история известная.
Переход участка линии из-под одной линии к другой — тоже не что-то из ряда вон выходящее (например, Каховская линия (ныне закрытая) до 20 ноября 1995 была частью Замоскворецкой; участок от Кунцевской до Крылатского принадлежал Филевской линии, а с 7 января 2008 стал частью Арбатско-Покровской линии).
Но некоторые изменения оказались довольно неожиданными. Например, что в Москве было две станции метро, которые изначально создавались как временные: Калужская и Первомайская.
Впрочем, мой победитель: переименование в 1963 году станций «Измайловская» и «Измайловский парк» в…. «Измайловский парк» и «Измайловскую». Две подряд идущие станции «поменяли местами». Остается только догадываться, как должно было рвать шаблоны у москвичей и гостей столицы, которых это изменение непосредственно коснулось.
И, заключительный третий шаг: выгрузка таблиц в CSV и конвертация её в json структуру данных для использования через JavaScript.
Немного косметики
Карта-подложка, которую предоставляет OSM, — достаточно яркая и шумная. На таком фоне наша схема метро теряется. Чтобы сделать её более наглядной, добавим второй базовый слой, с менее выраженным акцентом. Здесь очень хорошо подойдет светлый слой от Mapbox — Mapbox Light.
Стоит заметить, что в отличии от тайлов OSM, бесплатное использование тайлов Mapbox ограничено, и при превышении определенного количества в месяц тайлы перестанут подгружаться.
Пример: Добавляем альтернативный базовый слой — Mapbox Light
// Создаем новый слой const grayscale = L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoidGltZWxhcHNlbWFwIiwiYSI6ImNrcWZuemE3dzBxbjIyb3Njazk4ODd4M3oifQ._f0k3m2zgmxrdleisUaymQ', { minZoom: 1, maxZoom: 18, id: 'mapbox/light-v10', tileSize: 512, zoomOffset: -1, attribution: '© <a href="https://www.mapbox.com/about/maps/">Mapbox</a> | © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> | <strong><a href="https://www.mapbox.com/map-feedback/" target="_blank">Improve this map</a></strong>', }); // Создаем и добавляем на карту контрол для переключениям между слоями const mapLayers = { "OpenStreetMap": osm, "Grayscale": grayscale, }; L.control.layers(mapLayers).addTo(mymap);

Ещё одно визуальное улучшение, которое напрашивается сразу, — изменение размеров станций и линий метро в зависимости от масштаба.
Для каждого масштаба (zoom) определяем стили окружностей и линий — толщину, прозрачность, радиус, толщину обводки. При изменении масштаба (zoom) вызываем принудительную перерисовку всех объектов с использованием соответствующих стилей.
Вот теперь — красота:

Итоги

Получилась интерактивная карта московского метро, на которой можно проследить, как развивалась сеть. Например, в каких направлениях она росла, с какой скоростью шел запуск новых станций, да и просто поностальгировать.
Итоговый результат можно посмотреть здесь: https://mm.timelapsemap.com.
Карта по ссылке выше будет развиваться дальше. Для истории здесь https://timelapsemap.com/h/bc4fc2 лежит сохраненная копия, соответствующая этой статье.
При подготовке использованы материалы OpenStreetMap и Wikipedia. КДПВ — с сайта http://n-metro.ru.
PS
Несмотря на большое количество данных в Википедии, не про все изменения удалось найти точную информации.
Поделитесь, пожалуйста, в комментариях если знаете:
-
С какой даты «Парк Культуры имени Горького» стал просто «Парком культуры»?
-
С какой даты «Измайловский парк культуры и отдыха имени Сталина» был переименован в «Измайловскую»?
-
С какой даты станция «Имени Кагановича» была переименована в «Охотный ряд»?
-
Детали о закрытии станции «Чистые пруды» во время Великой Отечественной войны
-
Детали о переименовании станции «Калининская» в станцию «Воздвиженка» (ныне «Александровский сад»)
ссылка на оригинал статьи https://habr.com/ru/articles/594043/
Добавить комментарий