Интерактивная карта Webix GeoChart

Новый Webix 5.0 включает в себя виджет JavaScript GeoChart, созданный с помощью Google Maps API. GeoChart — это изящный сервис для создания картографических диаграмм c отображением данных стран и регионов. Я покажу вам, как использовать основные функции виджета для создания интерактивной карты мира. Если вы еще не знакомы с виджетом, я надеюсь, что GeoChart станет вашим верным другом. Если вы уже любите GeoChart так же сильно, как я, вы, вероятно, получите лучшее представление о том, как можно с ним взаимодействовать.

Webix with Google GeoChart

Базовый GeoChart для отображения данных

Во-первых, давайте покажем данные в режиме readonly (только для чтения). Макет приложения будет иметь три панели: 

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

initial visualization GeoChart

Прежде чем инициализировать GeoChart, не забудьте получить свой личный ключ

Несколько строк кода добавят GeoChart в приложение. Вот простая карта с небольшими данными: 

webix.ui({
    view:"geochart",
    id:"map",
    // provide your own Google API key
    // https://developers.google.com/maps/documentation/javascript/get-api-key
    key:"AIzaSyAi0oVNVO-e603aUY8SILdD4v9bVBkmiTg",
    data:[
{id:1, country:'Germany', code:"DE", area:357,  population:83 },
      {id:2, country:'United States', code:"US", area:9834, population:323 },
      {id:3, country:'Brazil', code:"BR", area:8516, population:207 },
      //...
    ]
});

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

var mapdata = new webix.DataCollection({data:[
  {id:1, country:'Germany', code:"DE", area:357,  population:83 },
  {id:2, country:'United States', code:"US", area:9834, population:323 },
  {id:3, country:'Brazil', code:"BR", area:8516, population:207 },
//...

Обратите внимание, что GeoChart не имеет собственного источника данных, так как они берутся из DataCollection:

var map = {
    view:"geochart",
    id:"map",
    // provide your own Google API key
    // https://developers.google.com/maps/documentation/javascript/get-api-key
    key:"AIzaSyAi0oVNVO-e603aUY8SILdD4v9bVBkmiTg"
};

Для записей данных я создам JS DataTable

var grid = {
  view:"datatable", id:"grid", autoheight:true,
  scroll:false, autoConfig:true
}

Вот макет:

webix.ui({
  cols:[
    form,
    { view:"scrollview", body:{
      type:"clean", rows:[
        map,
        grid
      ]
    }}
  ]
});

form на данный момент — это просто шаблон, который резервирует место для элементов управления на боковой панели. Карта и DataTable заключены в представление scrollview, чтобы адаптировать демонстрацию под изменения размера. Для этой же цели я отключила прокрутку сетки по умолчанию.

Чтобы синхронизировать GeoChart и DataTable с DataCollection, вызываем sync:

$$("map").data.sync(mapdata);
$$("grid").data.sync(mapdata);

Демо>>

Режимы GeoChart

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

  • regions — цветовые области карты (используется по умолчанию);
  • text — добавляет цветные метки разных размеров;
  • markers — добавляет цветные круги разных размеров.

JavaScript interactive map Webix GeoChart with markers

Я могу изменить режим, установив свойство chart.displayMode. Например, чтобы показать круги на графике, я установлю свойство маркерам:

webix.ui({
  view:"geochart",
  //...config
    chart:{
      displayMode:"markers"
    }
});

Если данные имеют несколько столбцов, то размер кругов зависит от второго числового столбца — население (population) в моих данных. 

Я также могу динамически изменять режим с помощью setDisplayMode (”markers»). Например, давайте добавим сегментированную кнопку, которая будет переключать GeoChart в разные режимы. Я помещу кнопку на боковую панель: 

var chartForm = {
  view:"form", id:"chartForm", elements:[
    {view:"segmented", id:"modes", value:"Regions",  
      options:["Markers","Regions","Text"],
      click:function(){
        $$("map").setDisplayMode(this.getValue().toLowerCase());
    }}
  ]
};

Webix GeoChart modes

Мне бы хотелось иметь возможность спрятать боковую панель. Поэтому я помещаю форму в  Webix Accordion:

var form = {
  header:"Configure", body:{
    width:300, type:"wide", rows:[
      chartForm
    ]
}};

Легенды и цвета

Давайте раскрасим GeoChart. Во-первых, я изменю цветовую схему легенды и раскрашу карту в цвета радуги. Для этого я установлю свойство chart.colorAxis:

chart:{
  colorAxis: {  
    colors:['violet','indigo','blue','green','yellow','orange','red']  
  }
  //...
}

Атрибут colors также принимает шестнадцатеричные значения. Цветовая схема легенды может быть изменена динамически с помощью настройки config.chart.colorAxis. Давайте добавим другие цветовые схемы и кнопку для переключения между ними: 

{ view:"segmented", id:"colors", value:"Rainbow",
  options:["Rainbow", "Volcano"],
  click:function(){
    var color = this.getValue();
    switch(color){
      case "Rainbow":
        $$("map").config.chart.colorAxis = {colors: ['violet','indigo','blue','green','yellow','orange','red']};
        $$("map").refresh();
      break;
      case "Volcano":
        $$("map").config.chart.colorAxis = {colors: ["#e7711c", "#4374e0"]};
        $$("map").refresh();
      break;
    }
}}

Не забудьте обновить refresh GeoChart после изменения цветовых схем, чтобы повторно визуализировать диаграмму с новыми цветами. 

Теперь я изменю цвет фона свойством chart.backgroundColor. Цвет областей без данных можно задать с помощью chart.datalessRegionColor. 

Webix GeoChart with different colors - gif

Демо >>

Легенда карты может быть дополнительно настроена и даже скрыта. Для получения более подробной информации ознакомьтесь с API Google GeoChart

Регионы и страны в GeoChart

Я также хочу, чтобы моя интерактивная карта показывала конкретные регионы или страны. Это можно сделать с помощью свойства chart.region. Оно принимает коды стран, такие как «US » или «BY», коды регионов, такие как «142 «(для Азии) и ‘world’. Список поддерживаемых кодов можно найти в разделе иерархия континентов и коды

Помимо установки начальной региональной конфигурации GeoChart, я могу динамически изменять отображаемую область с помощью setRegion (”code»). Давайте добавим радиоуправление на боковую панель. Радио переключит GeoChart с картины мира на другие континенты: 

{view:"radio", id:"regions", value:1, vertical:true,
  options:[ {id:1, value:"World"}, {id:2, value:"Europe"} ],  
  click:function(){
    var region = $$("regions").getValue();
    switch(region){
      case "1":
        $$("map").setRegion("world");
        break;
      case "2":
        $$("map").setRegion("150");
        break;
}}}

Webix with Google GeoChart region Europe

Подсказки

В дополнение к визуальному представлению, Webix GeoChart имеет HTML подсказки, которые показывают подробную информацию о каждой области в наборе данных. Всплывающие подсказки появляются, когда пользователь наводит указатель мыши на область или круг. По умолчанию всплывающие подсказки показывают список всех столбцов данных. Я могу изменить содержимое всплывающих подсказок по умолчанию, перечислив столбцы, которые я хочу показать, например, с помощью этого кода всплывающая подсказка будет показывать только область area: 

tooltip:"<i>Area</i>: #area#"

Кроме того, я могу стилизовать всплывающие подсказки. Я сделаю это, обратившись к классу .google-visualization-tooltip class:

.google-visualization-tooltip {
    background-color: lightblue;
    border: 1px solid #ccc;
    box-shadow: 0 2px 2px 0 rgba(204, 204, 204, 0.6);
}

Webix GeoChart a different tooltip

В посте на Stack Overflow вы найдете более подробные советы по стилизации всплывающих подсказок. 

Данные с несколькими столбцами

В случае данных, где элементы сравниваются по нескольким параметрам, например, по площади и населению, я могу выбрать, какой параметр отображать на карте. Остальные столбцы можно отобразить во всплывающих подсказках. Чтобы получить такой результат, я изменю порядок полей в свойстве columns. Давайте сделаем population (население) столбцом, который будет определять цвета диаграммы. Посмотрите:

var map = {
    view:"geochart",
    id:"map",
    key:"...",
    columns:["country", "population", "area"],
    //...
}

Webix GeoChart with population map

Размер кругов теперь зависит от площади.

Демо>>

Интерактивный GeoChart

Интерактивность в Webix GeoChart включена по умолчанию. Я могу нажать на регионы и круги. Если есть связанные данные, то область будет выделена. Я расширю интерактивность с помощью события onItemClick. Например, давайте выберем строку в таблице DataTable с данными в той же области:

var map = {
  view:"geochart",
  id:”map”,
  key:"...",
  on:{
    onItemClick:function(id){
      $$("grid").select(id);
    }
  }
};

onItemClick срабатывает, когда вы нажимаете на цветную область или круг, т. е. области с данными. 

Я хочу, чтобы была возможность нажимать на любую область, в том числе и без каких-либо данных. Еще одно доступное событие — onRegionClick. Это событие срабатывает только в режиме регионов. Например: 

var map = {
    view:"geochart",
    id:"map",
    key:"...",
    on:{
        onRegionClick:function(obj){
          webix.message(“Region code ”+obj.region);
        }
    }
};

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

Использование GeoChart для редактирования данных

Поскольку GeoChart — это компонент данных, я могу загружать, редактировать и удалять данные из него так же, как и с другими компонентами данных. Давайте добавим форму для редактирования, добавления и удаления элементов GeoChart:

var editForm = {
    view:"form", id:"editForm", elements:[
      { view:"combo", name:"country", options:countries },
      { view:"text", name:"area", label:"Area" },
      { view:"text", name:"population", label:"Population" },
      { view:"button", value:"Add/Update Item" },
      { view:"button", value:"Remove Item" }
    ]
};

Webix GeoChart edit form

combo берет свои опции из DataCollection, которая содержит все названия стран и коды регионов:

var countries = new webix.DataCollection({
  url:"https://docs.webix.com/samples/36_geochart/data/countries.json"
});

/* countries.json
  [
   {
    "value": "Afghanistan",
    "code": "AF",
    "id": "Afghanistan"
   },...
  ]
*/

Когда выбрана страна, я хочу заполнить форму данными, если эта страна уже существует в наборе данных. Если для выбранной области нет данных, пусть форма будет пустой. Чтобы найти элементы данных, я могу вызвать метод find. Однако, поскольку карта в демо-версии синхронизирована с DataCollection, я могу искать элементы методом mapdata:

{ view:"combo", name:"country", options:countries,
  on:{
    onChange:function(newv){
      //is the country in data?
      var item = mapdata.find(function(obj){
        return obj.country.toLowerCase() === newv.toLowerCase();
      });
         
      if(item.length)
        this.getFormView().setValues(item[0]);
      else {
        //if not, get its code
        var itemc = countries.find(function(obj){
          return newv === obj.value;
        },true);
        this.getFormView().setValues({country:newv, code:itemc.code, id:", area:", population:"});
      }
  }
}}

Если я выберу страну из combobox, название этой страны будет сравниваться с именами в картографических данных. mapdata.find возвращает массив с элементом из набора данных, если он был найден. Если страна не была найдена, возвращается пустой массив. 

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

Если массив элементов не пуст, то форма заполняется реальными значениями. В противном случае он получает название страны и код, другие элементы управления заполняются пустыми строками. Обратите внимание на пустую строку, переданную в id. Я использую ее позже как метку, чтобы проверить, находится ли уже страна в DataCollection. 

geochart edit combo list

Добавление и обновление элементов

Теперь я хочу добавить и обновить элементы данных. Здесь есть два варианта:

  1. страна уже находится в наборе данных;
  2. я добавляю новую страну.

Чтобы разобраться с первым случаем, давайте воспользуемся методом updateItem данных DataCollection. У GeoChart есть такой же метод. updateItem принимает два параметра: ID элемента и сам элемент как объект.

{ view:"button", value:"Add/Update Item", click:function(){
  var values = this.getFormView().getValues();
  if(values.id)
    mapdata.updateItem(values.id, { area:values.area, population:values.population});
}}

values.id правдиво, если страна находится в DataCollection.

Теперь давайте рассмотрим второй случай: добавление новой страны в набор данных. Я буду использовать метод add, который получает один обязательный параметр — объект country.

{ view:"button", value:"Add/Update Item", click:function(){
    var values = this.getFormView().getValues();
    if(values.id)
      mapdata.updateItem(values.id, { area:values.area, population:values.population});
    else{
      mapdata.add({ country:values.country, code:values.code, area:values.area, population:values.population});
    }
}}

Было бы неплохо провести валидацию входных данных формы. Для начала я добавлю правила валидации в форму:

var editForm = {
    view:"form", id:"editForm", elements:[
      //elements    
    ],
    rules:{
        area:webix.rules.isNumber,
        population:webix.rules.isNumber,
        country:function(value){ return value != 0; }
    }
};

Теперь я добавлю валидацию в обработчик кнопокAdd/Update:

{ view:"button", value:"Add/Update Item", click:function(){
  if(this.getFormView().validate()){
    var values = this.getFormView().getValues();
    if(values.id)
      mapdata.updateItem(values.id, { area:values.area, population:values.population});
    else{
      mapdata.add({ country:values.country, code:values.code, area:values.area, population:values.population});
    }
  }
}}

Когда я добавляю или обновляю элемент в mapdata, список записей в DataTable также обновляется.

Webix with geochart interactive map cameroon added

Удаление элементов

Теперь давайте включим удаление элементов из GeoChart. Нужно проверить, существует ли элемент, прежде чем удалить его. Я снова буду использовать значение id формы в качестве метки. И GeoChart, и DataCollection имеют метод remove, давайте его использовать.

{ view:"button", value:"Remove Item", click:function(){
    if(this.getFormView().validate()){
      var id = this.getFormView().getValues().id;
      if(id){
        mapdata.remove(id);
        this.getFormView().clear();
      }
    }
}}

remove удаляет страну только после проверки входных данных и если элемент существует в наборе данных. После удаления элемента форма будет очищена.

Еще один момент с интерактивностью

Вот еще один пример того, как повысить интерактивность с помощью события onClick. Довольно удобно редактировать элементы одним кликом по карте. Чтобы обновить страны из DataCollection, я обработаю onItemClick: :

on:{
  onItemClick:function(id){
    $$("editForm").setValues(this.getItem(id));
    $$("grid").select(id);
  }
}

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

Я возьму код региона, полученный из события, и сравню его с кодами в списке стран, отмеченных на карте. Затем, если результат поиска пуст, окно подтверждения Webix спросит пользователей, хотят ли они добавить новую запись. Если да, то они могут перейти к добавлению нового элемента. 

onRegionClick:function(obj){
  var code = obj.region;
  var item = mapdata.find(function(obj){
    return obj.code === code;
  });
     
  if(!item.length) {
    $$("grid").clearSelection();
    webix.confirm("Add a new country?", function(){
      //...add the country
    });
  }
}

Затем я получу название страны, сравнивая коды из списка всех стран. После того, как имя будет найдено, значение combo будет изменено.

webix.confirm("Add a new country?", function(result){
  if (result) {
    var newv = countries.find(function(obj){
      return obj.code === code;
    },true);
    $$("editForm").setValues({country:newv.value});
  }
});

Webix GeoChart Interactive Map

А вот и демо с полным исходным кодом.

Прежде чем вы умчитесь наслаждаться GeoChart…

GeoChart хорош для представления различных статистических данных, связанных с регионами, странами или государствами. Вы можете адаптировать GeoChart к вашим потребностям и использовать его в приложениях для бизнеса, образования, отчетов или просто для удовольствия. Если у вас есть какие-либо идеи и советы по GeoChart, буду рада их услышать:)

Получите демо-версию и развлекайтесь. Если вы хотите копнуть глубже и получить более подробную информацию о GeoChart, ознакомьтесь с документацией Webix и документацией Google