http://ivanpoukhkal.ru/interactive/flights/

Cделано с помощью js-библиотеки Kartograph.js Георга Айша, предназначенной для создания интерактивных векторных карт на основе SVG. Её основная «фишка» — в поддержке различных картографических проекций.

Рассказ автора о том, почему он решил создать «Картографа».

Данные

Чтобы не тратить много времени на подготовку геопривязанных данных, я решил ещё раз обратиться к авиационной тематике. Я знал, что на openflights.org ведётся база данных аэропортов с их географическими координатами, так что оставалось подобрать что-нибудь связанное с перелётами между ними. В августе как раз свернула работу авиакомпания Wind Jet и я решил нарисовать европейские маршруты «схлопнувшихся» в этом году перевозчиков пассажиров.

Первоначально я думал собрать данные по двум-трём авиакомпаниям вручную, но на том же openflights оказалась база маршрутов по состоянию на январь 2012 года, так что препятствий к рисованию всех линий не было. Количество самолётов к моменту прекращения деятельности я взял из Википедии. Ещё по Википедии я уточнил названия некоторых аэропортов (например, чешский аэропорт «Брно—Туржаны», который в базе назывался просто «Туржаны»). Там, где возможно, я уточнил действовавшие расписания авиакомпаний по Airline Route и FlightMapper.

Все три таблицы (аэропорты, маршруты и авиакомпании) были сохранены в CSV (50 КБ в сумме).

Внутреннее устройство библиотеки

«Картограф» отвечает за географическую часть работы: преобразование координат из широты и долготы в абсциссу и ординату рисунка в соответствии с выбранной проекцией, а также умеет наносить данные по нескольким типовых решениям: с кругами или иконками в заданных точках, либо с кодированием цветом регионов в зависимости от данных (то, что по-русски называют картограммой, а по-английски — choropleth map).

Непосредственно для рисования SVG в браузере для работы требуется подключить «Рафаэль» — в результате все элементы карты являются объектами «Рафаэля» и интерактивность обеспечивается именно возможностями этой библиотеки.

Также использованы:

  • jquery;
  • arc.js — для расчёта ортодром, кратчайших линий между точками земного шара;
  • jquery-csv — для чтения данных из CSV-таблиц;
  • underscore.js — библиотека для работы с коллекциями в функциональном стиле.

Общий размер скриптов: 277 КБ (библиотеки в минифицированном виде) + 11 КБ (относящийся к созданию конкретно этой карты).

Начало работы

«Картограф» на входе понимает SVG-файл с географическими метаданными: в какой проекции карта и каковы 2 широты и 2 долготы, ограничивающие рисунок по краям. Есть два пути получения такого файла: (а) взять обычную SVG-карту, узнать проекцию и проставить ограничения в файл вручную, (б) воспользоваться написанном на Питоне скриптом kartograph.py, который сделает из шейп-файлов с геоданными SVG-карту с уже прописанной метаинформацией. При работе с ним также можно указать желаемую проекцию на выходе или какие объекты включать в итоговую карту.

Я выбрал путь (б) — в конце концов, библиотеку-то мы выбрали именно из-за большого количества проекций. Найти шейп-файл для обзорной карты Европы — дело несложное, достаточно просто взять файлы Natural Earth в нужном масштабе, где уже прописаны коды стран и регионов (впрочем, на моей карте со странами манипуляций нет, так что коды тут не понадобятся).

Предлагается автоматическая установка, которая на Маке работает ровно до того момента, когда ей требуются установленные Command Line Tools. Точнее, эта причина устанавливается позже, сообщение выдаётся немного другое: «AAA не нашло BBB». В итоге CLT были приняты благосклонно и установка завершилась успешно.

Преобразование управляется конфигурационным файлом (в yaml или json-формате). Вот, например, мой для «спутниковой» проекции (вид со спутника, висящего над определённой точкой земной поверхности):

proj:
	id: «satellite»
	«lon0»: 13.5
	«lat0»: 50
	«dist»: 1.52
layers:
— id: «countries»
  src: shp/ne_50m_admin_0_countries.shp # шейп-файл
  simplify: false # не упрощать SVG-пути
  attributes: # переносить коды стран в карту
	  «ISO_A3»: «iso»
	  «NAME»: «name»
  filter: [«ISO_A3», «in not», [«DZA», «SYR», «TUN»]] # исключить Алжир, Сирию и Тунис
  styles: # стили для объектов этого слоя
	  fill: «#000»
	  stroke: «#ccc»
	  stroke-opacity: 0.5
bounds: # границы, примерно соответствующие Европе
   mode: bbox
   data: [-11,36,38,68] # порядок: долгота левая, правая; широта нижняя, верхняя
export: # размер на выходе и число знаков после запятой
	width: 800
	round: 2

Для выбора проекции Георг предлагает использовать визуальный редактор для «Картографа» и окно экспорта Openstreetmap для границ отреза.

Всё это прекрасно, но увы, преобразование — то, за чем мы вообще пришли — работает не для всех проекций. Либо что-то не доделано, либо какие-то проблемы с зависимостями, но большая часть из них просто выдаёт ошибку «ImportError: cannot import name MultiPolygon» или какую-нибудь другую.

Из работающих можно, например, посмотреть на проекцию Вагнера IV:

SVG-карта в «спутниковой» проекции (но размер от выбора проекции зависит мало) в итоге заняла 190 КБ.

Приступаем к картографингу

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

Отрисовка карты основывается на слоях. Сначала загружается SVG-карта, а затем на неё накладываются слои регионов в цветовом кодировании или символов одного из типов: круги, текстовые метки или иконки (из графических файлов). При создании слоя ему передается массив необходимых данных и функции, генерирующие местоположение и связанные с объектом данные.

На моей карте создано два слоя: оранжевые круги аэропортов и текстовые метки с их названиями (а именно: городами, если крупный аэропорт один, или городами с аэропортами в скобках, если их несколько). Линии перелётов я рисую в отдельным проходе по базе маршрутов функцией addGeoPath.

Про возможность штатной передачи идентификатора через параметр я не знал и приспособил для этого атрибут title, куда записывался код аэропорта по классификации IATA (LED, SVO, HEL…). Но заботливый «Картограф» при генерации страницы оборачивает элемент «circle» в «a», чтобы показывать всплывающую подсказку. Поэтому после окончания формирования карты этот элемент приходится снова убирать.

Гистограмма

Гистограмма рисуется примитивами «Рафаэля» прямо на карте. Здесь создаются линии сетки и прямоугольники, которым придаются обработчики событий.