[Перевод] Создание карты потоков с помощью JavaScript: пошаговое руководство

9csbjsbj7g6vnktjmojz2ttifu0.png


Карты потоков — это мощный способ представления движения объектов между различными географическими локациями, который очень легко реализуется с помощью JavaScript. По сути, они совмещают в себе функциональность карты и потоковой диаграммы. Такой тип визуализации показывает направление движения людей, товаров, денег или информации, а также их количество.

В текущем руководстве мы познакомим вас с процессом создания карты потоков. А чтобы сделать этот процесс более практичным, мы используем пример визуализации количества студентов из Индии, обучающихся в разных странах. Следуя этому руководству, вы научитесь создавать собственные карты потоков с помощью JS для любых видов данных.

Превью карты


Ниже вы можете видеть, как будет выглядеть наша итоговая карта потоков.

lr6xi1p75-x562aa6zm_sb6xfvc.png


Создание базовой карты потоков с помощью JavaScript


С помощью JS построить карту потоков можно в четыре основных шага:

  1. Создать веб-страницу с помощью HTML.
  2. Включить необходимые файлы JS.
  3. Добавить данные.
  4. Написать JS-код для отрисовки графика.


Наличие хорошего понимания HTML, CSS и JS будет весьма кстати, но не стоит беспокоиться, если эти инструменты для вас малознакомы. В данном руководстве я подробно опишу каждый шаг, максимально упростив для вас задачу.

▍ 1. Создание веб-страницы на HTML


Для начала создадим простую HTML-страницу в качестве контейнера для отображения нашей карты. Сделаем мы это путём создания элемента

и установки его атрибута id на container. Это позволит позднее обращаться к созданному контейнеру, когда мы добавим карту.

Чтобы карта потоков отображалась на всю страницу, мы с помощью CSS установим высоту и ширину

на 100%. Можете подстроить стилизацию на своё усмотрение.



  
    
    Flow Map
    
  
  
    


▍ 2. Добавление необходимых файлов JavaScript


Далее мы добавим в раздел шапки нашего HTML-файла необходимые скрипты для построения карты потоков.

Проще и быстрее всего создать интерактивную карту потоков можно с помощью графической библиотеки JS. Однако не все библиотеки поддерживают такую возможность. Для этого руководства мы будем использовать JS Charts, которая, помимо поддержки карт потоков, также имеет прекрасную документацию по картированию и множество готовых примеров.

Для создания карты нам нужно добавить несколько модулей AnyChart: модули Core и Geo Map для визуализации данных, а также geodata для карты. Для обработки географических координат мы используем библиотеку Proj4js, так что она тоже включена. Кроме того, для добавления в UI возможности масштабирования нам потребуется AnyChart модуль Common UI и CSS.



  
    
    Flow Map
    
    
    
    
    
    
    
    
  
  
    


▍ 3. Добавление данных


В этом руководстве мы будем визуализировать Топ-10 стран, в которые направлялись студенты из Индии в 2022 году. Источником данных здесь выступает индийское Министерство иностранных дел, которое предоставляет информацию по количеству студентов, обучающихся за рубежом.

Чтобы отобрать 10 ведущих стран, я проанализировал данные и на основе количества учащихся в них студентов выбрал следующие: Австралия, Канада, Германия, Оман, Катар, Российская Федерация, Саудовская Аравия, Объединённые Арабские Эмираты, Великобритания и США.

При создании карты потоков в качестве конечных точек следования учащихся мы будем использовать столицы этих стран, а в качестве отправной — Нью Дели, Индия. Координаты широты и долготы столиц я определял с помощью сайта LatLong.net. В таблице ниже показан список выбранных стран, а также общее число обучающихся в них студентов из Индии и координаты столиц.

wy5wttg7tlt72l4okqle46yw6je.png


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

var data = [
  { points: [28.610198, 77.207584, -30.59, 145.94], to: "Australia", total: 100009 },
  { points: [28.610198, 77.207584, 45.41, -75.69], to: "Canada", total: 183310 },
  { points: [28.610198, 77.207584, 52.51, 13.40], to: "Germany", total: 34864 },
  { points: [28.610198, 77.207584, 23.58, 58.38], to: "Oman", total: 39550 },
  { points: [28.610198, 77.207584, 25.28, 51.53], to: "Qatar", total: 46000 },
  { points: [28.610198, 77.207584, 55.74, 37.62], to: "Russian Federation", total: 18039 },
  { points: [28.610198, 77.207584, 24.71, 46.67], to: "Saudi Arabia", total: 65800 },
  { points: [28.610198, 77.207584, 24.45, 54.37], to: "United Arab Emirates", total: 164000 },
  { points: [28.610198, 77.207584, 52.66, -0.95], to: "United Kingdom", total: 55465 },
  { points: [28.610198, 77.207584, 38.88, -77.03], to: "United States", total: 465791 }
];


▍ 4. Написание JS-кода для отрисовки карты


Теперь, когда данные подготовлены, пора перейти к основной части — размещению карты потоков на веб-странице.

Прежде чем начать, нужно убедиться, что наш код выполняется всего раз, и страница загружается полноценно. Сделаем мы это с помощью функции anychart.onDocumentReady(), которая включит весь код карты потоков.


Далее мы добавим данные, которые подготовили на первом этапе.

anychart.onDocumentReady(function () {
  var data = [
    { points: [28.610198, 77.207584, -30.59, 145.94], to: "Australia", total:    100009 },
    { points: [28.610198, 77.207584, 45.41, -75.69], to: "Canada", total: 183310 },
    { points: [28.610198, 77.207584, 52.51, 13.40], to: "Germany", total: 34864 },
    { points: [28.610198, 77.207584, 23.58, 58.38], to: "Oman", total: 39550 },
    { points: [28.610198, 77.207584, 25.28, 51.53], to: "Qatar", total: 46000 },
    { points: [28.610198, 77.207584, 55.74, 37.62], to: "Russian Federation", total: 18039 },
    { points: [28.610198, 77.207584, 24.71, 46.67], to: "Saudi Arabia", total: 65800 },
    { points: [28.610198, 77.207584, 24.45, 54.37], to: "United Arab Emirates", total: 164000 },
    { points: [28.610198, 77.207584, 52.66, -0.95], to: "United Kingdom", total: 55465 },
    { points: [28.610198, 77.207584, 38.88, -77.03], to: "United States", total: 465791 }
  ];
});


Для создания графика карты потоков мы используем AnyChart функцию connector(), передав ей данные для соединительных линий и включив геоданные карты мира.

var map = anychart.connector();
var connectorSeries = map.connector(data);
map.geoData(anychart.maps.world);


Далее мы установим метки и добавим стрелочки в конец линии каждого потока.

connectorSeries
  .labels()
  .enabled(true)
  .position('100%')
  .format(function () {
    return this.getData('to')
  });

connectorSeries
  .markers()
  .position('100%')
  .size(12);


Теперь нужно указать для нашей карты потоков заголовок.

map.title("Top 10 Destination Countries for Indian Students Studying Abroad");


Наконец, мы сошлёмся на ID контейнера, поместим карту в элемент container и отобразим его на странице с помощью функции draw().

map.container('container');
map.draw();


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

И это совсем несложно — потребуется всего три дополнительные строчки кода.

var zoomController = anychart.ui.zoom();
zoomController.target(map);
zoomController.render();


Вот и всё! Наша базовая карта потоков готова.

Взгляните на её интерактивную версию и поиграйтесь с ней в песочнице AnyChart. Вот весь исходный код:



  
    
    JavaScript Flow Map
    
    
    
    
    
    
    
    
  
  
    


Кастомизация


Отличная работа! Создавать простые интерактивные карты потоков с помощью JS действительно не так уж сложно. Теперь перейдём к следующим шагам, чтобы улучшить и персонализировать нашу карту, сделав её более информативной и привлекательной.

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

▍ A. Оптимизация кривых линий и позиций меток


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

Чтобы это исправить, можно изменить датасет, добавив в него свойство curvature, которое позволит скорректировать кривизну отдельных линий. Помимо этого, мы добавим свойство label и установим его значения, чтобы метки отображались в нужных точках карты.

var data = [
  { points: [28.610198, 77.207584, -30.592659, 145.943667], to: "Australia", total: 100009, curvature: 0.5 },
  { points: [28.610198, 77.207584, 45.411673, -75.69629], to: "Canada", total: 183310, curvature: 0.8, label: { offsetY: -30 } },
  { points: [28.610198, 77.207584, 52.511693, 13.403121], to: "Germany", total: 34864, curvature: 0.3, label: { anchor: 'center-top', offsetY: -2 } },
  { points: [28.610198, 77.207584, 23.5880, 58.3829], to: "Oman", total: 39550, curvature: -0.5, label: { anchor: 'left-top' } },
  { points: [28.610198, 77.207584, 25.2854, 51.5310], to: "Qatar", total: 46000, curvature: 0.4, label: { anchor: 'right-top', offsetY: -20 } },
  { points: [28.610198, 77.207584, 55.747362, 37.621273], to: "Russian Federation", total: 18039, curvature: 0.4, label: { anchor: 'left-bottom' } },
  { points: [28.610198, 77.207584, 24.710437, 46.675164], to: "Saudi Arabia", total: 65800, curvature: 0.7, label: { offsetY: -5 } },
  { points: [28.610198, 77.207584, 24.4539, 54.3773], to: "United Arab Emirates", total: 164000, curvature: 0, label: { anchor: 'left-top', offsetY: -15 } },
  { points: [28.610198, 77.207584, 52.667078, -0.955920], to: "United Kingdom", total: 55465, curvature: 0.4, label: { anchor: 'right-top', offsetY: -25, offsetX: -10 } },
  { points: [28.610198, 77.207584, 38.884053, -77.033513], to: "USA", total: 465791, curvature: -0.6 },
];


Благодаря этим изменениям, теперь можно отображать метки даже в случае их наложения. Реализовать это можно одной строкой кода.

map.overlapMode("allow-overlap");


pmjg-cxoh0ejgu8fasvheu25g2u.png


▍ B. Доработка справочной информации


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

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

connectorSeries
  .tooltip()
  .useHtml(true)
  .format(function () {
    return (
      '
To: ' + this.getData('to') + '
' + '
Total Students: ' + this.getData('total').toLocaleString() + '
' ); });


xekgenxl5vi2ku8v2sqg1dpx97e.png


▍ C. Доработка заголовка


Чтобы повысить привлекательность заголовка нашей карты, можно выделить его при помощи HTML-стилизации, чтобы он был более заметен и привлекал внимание.

map.title()
  .enabled(true)
  .useHtml(true)
  .text(
    'Top 10 Destination Countries for Indian Students Studying Abroad
' + 'The top destination is the U.S.' );


zlu4_vc6vn82z04oeblthhu8eri.png


▍ D. Выбор цветов для соединительных линий и маркеров


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

Для этого мы изменим создающий линии код, и применим к ним новый цвет с помощью функций fill() и stroke().

var connectorSeries = map.connector(data)
  .fill(['#e8dd09', 'red'])
  .stroke(['#e8dd09', 'red']);
connectorSeries
  .hovered()
  .stroke('#808080')
  .fill('#808080');
connectorSeries
  .hovered()
  .markers()
  .stroke('#808080')
  .fill('#808080');


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

krhyalddzy1p9c86nwvcksvauhm.png


▍ E. Изменение цвета и толщины линий на основе данных


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

Для этого мы сначала сгруппируем страны в четыре категории, исходя из общего числа обучающихся в них студентов.

  • менее 50,000;
  • от 50,000 до 100,000;
  • от 100,000 до 200,000;
  • больше 200,000.


Мы также создадим filter_function(), которая будет фильтровать данные согласно этим категориям.

function filter_function(val1, val2) {
  if (val2)
    return function (fieldVal) {
      return val1 <= fieldVal && fieldVal < val2;
    };
    else
      return function (fieldVal) {
        return val1 <= fieldVal;
      };
}


Далее мы создадим функцию createSeries(), которая будет получать датасеты и конфигурации, создавая на их основе соединительные линии.

function createSeries(data, name, color, size) {

  // Создание соединительных линий.
  var connectorSeries = map.connector(data)
    .name(name)
    .fill(color)
    .stroke(color)
    .color(color);
  connectorSeries
    .hovered()
    .stroke('#808080')
    .fill('#808080');
  connectorSeries
    .hovered()
    .markers()
    .stroke('#808080')
    .fill('#808080');

  // Установка меток для линий.
  connectorSeries
    .labels()
    .enabled(true)
    .position('100%')
    .fontColor('#2D2D2D')
    .format(function () {
      return this.getData('to')
    });

  // Установка стрелочек в конце линий.
  connectorSeries
    .markers()
    .position('100%')
    .size(12);

  // Настройка справочной информации для линий.
  connectorSeries
    .tooltip()
    .useHtml(true)
    .format(function () {
      return (
        '
To: ' + this.getData('to') + '
' + '
Total Students: ' + this.getData('total').toLocaleString() + '
' ); }); // Установка толщины линии, исходя из количества студентов. connectorSeries .startSize(size[0]) .endSize(size[1]); }


Мы также создадим из наших данных датасет.

var dataSet = anychart.data.set(data).mapAs();


После чего отфильтруем его с помощью filter_function() и передадим в функцию createSeries() для создания набора линий.

createSeries(dataSet.filter('total', filter_function(0, 50000)), 'Less than 50,000', '#A149FA', [1, 0]);

createSeries(dataSet.filter('total', filter_function(50000, 100000)), '50,000 - 100,000', '#3B44F6', [2, 1]);

createSeries(dataSet.filter('total', filter_function(100000, 200000)), '100,000 - 200,000', '#3EC70B', [4, 1]);

createSeries(dataSet.filter('total', filter_function(200000, 500000)), 'More than 200,000', '#F7EC09', [6, 1]);


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

map.legend().enabled(true).position('bottom').padding([20, 0, 0, 0]).fontSize(10);

map.legend().title().enabled(true).text('Number of Students').fontSize(13).padding([0, 0, 5, 0]);


Итак, цель достигнута! Мы создали прекрасную интерактивную карту потоков с помощью JS.

lr6xi1p75-x562aa6zm_sb6xfvc.png


В результате мы видим, что цвета и толщина соединительных линий варьируются на основе количества студентов. Конечную интерактивную версию этой карты можно просмотреть в песочнице AnyChart. Вы также можете внести в код дополнительные изменения и поэкспериментировать с ним.

Вот весь код карты:



  
    
    JavaScript Flow Map
    
    
    
    
    
    
    
    
  
  
    


Заключение


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

Успехов!

Telegram-канал с розыгрышами призов, новостями IT и постами о ретроиграх

© Habrahabr.ru