Интерактивная карта развития Московского метрополитена

aa976ee2d94544366ffae8c5085bf013.jpg

15 мая 1935 была открыта первая очередь Московского метрополитена. С этого момента началась новая эра в истории города, в котором подземка, безусловно, играет важную роль.

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

Упрощенная схема читается быстрее. Но, в то же время, не дает представления о реальном расположении линий, скрывает детали. Длинные перегоны метро ничем не отличаются от коротких. Нет привязки к географии.

Давайте создадим интерактивную карту линий Московского метрополитена и посмотрим на его историю — как развивалась одна из самых больших сетей мира.

Для создания такой карты нам потребуются:

Механизм

Для начала, определяемся с требования к механизму. Он должен уметь:

  • отображать базовый слой с «обычной» картой — дорогами, водоемами, улицами, зданиями и т.д.;

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

  • обновлять отрисованные станции и линии метро в зависимости от выбранной даты;

  • переключать текущую дату в интервале от 15 мая 1935 года (запуск первой линии метро) до настоящего времени.

Первые три пункта из коробки есть в open source библиотеке Leaflet. Четвертый будем реализовывать самостоятельно.

Базовый слой карты

Пример 1: Создание базового слоя карты










image-loader.svgЧто такое tile server?

Небольшое пояснение для тех, кто никогда не сталкивался с понятиями тайлов (tiles) и тайлового сервера (tile server).

Онлайн карты, к которым привыкло большинство пользователей, — OpenStreetMap, Яндекс.Карты, 2ГИС и т.п. — состоят из тайлов (tile): квадратов с длиной стороны в 256 пикселей. 

Каждый тайл имеет координаты x, y и z:

  • z — масштаб, при котором он отображается (чем больше значение z,  тем больше увеличение);

  • x — порядковый номер тайла по горизонтали при выбранном масштабе z;

  • y — порядковый номер тайла по вертикали при выбранном масштабе z.

Например, тайл с координатами x=2476, y=1280 и z=12 выглядит вот так:

image-loader.svg

Эти «квадраты» хранятся на тайловом сервере (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);

image-loader.svg

Для отображения линий метро будем использовать 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);

Обратим внимание, что при движении от Сокольников, после Охотного ряда, была развилка. Поезда следовали в двух направлениях: в сторону Смоленской и в сторону Парка культуры имени Горького.

image-loader.svg

Удаление и добавление новых кругов (станций) и ломаных (линий метро) также является встроенными функциями Leaflet — это потребуется для перерисовки схемы метро в зависимости от выбранной даты.

Переключение текущей даты

Следующим шагом нужно будет прикрутить слайдер с диапазоном от »15 мая 1935» и до сего дня. С помощью него мы будем задавать «текущую» дату для отображения схемы метро, какой она была в этот день.

Алгоритм прост: при изменении значения слайдера выполняем по порядку:

  • очищаем текущую схему,

  • проходим по всем объектам (станциям и линиям) в «базе»,

  • выбираем те объекты, которые существовали в новую выбранную дату,

  • отображаем их на карте.

Чтобы определить, какие объекты существовали в указанный день, необходимы правильным образом подготовленные данные. Например, нужно сохранить в «базе» детали о том, что с момента открытия в 1935 году и до 1990 года станция метро «Чистые пруды» называлась «Кировской». Или, например, что первая версия станции «Первомайская» полностью закрылась в 1961 году.

Чем проще и удобнее будет модель хранения данных, тем удобнее будет работа с самими данными.

Модель

За основу возьмем готовое решение, прошедшим проверку временем — модель данных OpenStreetMap. И доработаем ее до наших целей там, где её будет не хватать.

В OSM все данные состоят из трех видов элементов (elements): точки (node), линии (way) и отношения (relation).

Точка (Node)

Node — простейший элемент, который хранит в себе координаты широты и долготы.

Пример Node

Nodes, с координатами станций первой линии метро на момент её открытия 15 мая 1935 года в нотации OSM XML формате (без служебных данных):


 

 

 

 

 

 

 

 

 

 

 

 

Линия (Way)

Way — упорядоченная ломаная линия, состоящая из последовательно заданных точек (nodes).

Пример Way


 












 




Отношение (Relation)

Отношение (relation) — это группировка элементов (elements) по определенному признаку. В отношение могут входит точки (nodes), линии (ways), а также, другие отношения (relations).

Пример Relation

Например, объединение всех станций и участков метро могут объединяться под одним отношением (relation) — «Московский метрополитен».



  
  
  
  

Тег (Tag)

Также, у каждого из элементов (elements) могут быть заданы теги: пара «ключ=значение». Теги определяют свойства объекта, и используются для описания того, как объекты будут отображаться на карте.

Пример Tags

Например, станция метро может иметь такой набор тегов

 

	
	
	

Доработка модели данных 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 для переименованной станции метро


	
	
	
	
	
	


	
	
	
	
	
	

image-loader.svg

Аналогичная история с участками линий. Например, если с 1935 года участок путей принадлежал Сокольнической линии, а с 1938 года стал участком Арбатской линии, то нам потребуется три relation.

Пример relations для изменения линий


	
	
	
	
	
	
	
	
	
	



	
	
	
	
	
	
	



	
	
	
	
	
	
	
	
	

image-loader.svg

Данные

Подготовка данных состояла из трех шагов.

Первым шагом была начальная загрузка данных из википедии со страницы список станций Московского метрополитена, которая содержит всю нужную информацию — список станций, их координаты, дату открытия. Все детали были загружены в электронные таблицы 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: '© Mapbox | © OpenStreetMap | Improve this map',
});

// Создаем и добавляем на карту контрол для переключениям между слоями
const mapLayers = {
	"OpenStreetMap": osm,
	"Grayscale": grayscale,
};

L.control.layers(mapLayers).addTo(mymap);

image-loader.svg

Ещё одно визуальное улучшение, которое напрашивается сразу, — изменение размеров станций и линий метро в зависимости от масштаба.

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

Вот теперь — красота:

image-loader.svg

Итоги

image-loader.svg

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

Итоговый результат можно посмотреть здесь: https://mm.timelapsemap.com.

Карта по ссылке выше будет развиваться дальше. Для истории здесь https://timelapsemap.com/h/bc4fc2 лежит сохраненная копия, соответствующая этой статье.

При подготовке использованы материалы OpenStreetMap и Wikipedia. КДПВ — с сайта http://n-metro.ru.

PS

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

Поделитесь, пожалуйста, в комментариях если знаете:

  • С какой даты «Парк Культуры имени Горького» стал просто «Парком культуры»?

  • С какой даты «Измайловский парк культуры и отдыха имени Сталина» был переименован в «Партизанскую»?

  • С какой даты станция «Имени Кагановича» была переименована в «Охотный ряд»?

  • Детали о закрытии станции «Чистые пруды» во время Великой Отечественной войны

  • Детали о переименовании станции «Калининская» в станцию «Воздвиженка» (ныне «Александровский сад»)

© Habrahabr.ru