Статьи

Динамические географические карты с SVG и jQuery

Когда мне нужно создать диаграммы, я выбираю Google Charts или другую выделенную библиотеку. Иногда, однако, мне нужны некоторые специфические особенности, которые я не могу найти там. В этих случаях изображения SVG оказываются очень ценными.

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

Создание карты SVG в Illustrator

Сначала я нарисовал карту Италии с помощью Illustrator:

Иллюстратор Карта Италии

Каждый регион рисуется как один объект, и каждый из них имеет свой собственный уровень с именем, совпадающим с кодом, используемым в базе данных для идентификации его относительных данных (например, «tos» для Тосканы).

Наконец карта должна быть сохранена как файл SVG. Необходимо обратить внимание на то, чтобы в «Illustrator» для параметра «Свойство CSS» было установлено значение «Элементы стиля», как показано ниже:

Панель настроек SVG Illustrator

Открыв только что созданный файл, вы увидите, что он содержит набор тегов g , идентификаторы которых соответствуют названиям уровней Illustrator.

Создание нашего HTML-файла

Каждый элемент, содержащийся в тегах g имеет класс st0 так что им могут быть назначены свойства CSS stroke и fill :

Файл отображается в скобках

Если вы попытаетесь изменить эти значения, карта изменится немедленно:

Измененная карта

Теперь мы можем использовать этот код для построения нашего html-файла со встроенным SVG, как показано ниже (код был сокращен для удобства):

 <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Map Sample</title> <style type="text/css" media="all"> .map svg { height: auto; width: 350px; } .map g { fill: #ccc; stroke: #333; stroke-width: 1; } </style> </head> <body> <div class="map"> <svg version="1.1" id="Livello_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 -21.6 761 919" style="enable-background:new 0 -21.6 761 919;" xml:space="preserve"> <g id="sar"> <polygon class="st0" points="193,463 ... "/> </g> <!-- etc ... --> </svg> </div> </body> </html> 

Вы можете видеть, что атрибут style внутри тега svg был удален и заменен новым атрибутом, расположенным внутри svg документа; все g элементов были изначально заполнены светло-серым.

Класс st0 больше не используется (вы можете удалить его из кода SVG) и был заменен селектором .map g . В любом случае, это не обязательно, вы можете использовать CSS-селекторы, которые вы предпочитаете.

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

Добавление данных JSON и JavaScript

Данные извлекаются в формате JSON и вставляются непосредственно в наш HTML-файл (в реальном мире, конечно, данные будут извлекаться с использованием Ajax или аналогичного).

Теперь наша страница будет содержать JSON в нашем файле JavaScript, который выглядит следующим образом (снова сокращенно):

 var regions=[ { "region_name": "Lombardia", "region_code": "lom", "population": 9794525 }, { "region_name": "Campania", "region_code": "cam", "population": 5769750 }, // etc ... ]; 

После этого выбирается цвет (в данном случае #0b68aa ), и мы присваиваем его области с наибольшим значением населения. Другие регионы будут окрашены в тона основного цвета пропорционально их доле населения.

Далее мы можем добавить немного JavaScript.

Прежде всего, мы должны определить регион с максимальной численностью населения. Это можно сделать с помощью нескольких строк кода.

После создания временного массива, содержащего значения совокупности, мы можем использовать для него метод Math.max :

 var temp_array= regions.map( function( item ) { return item.population; }); var highest_value = Math.max.apply( Math, temp_array ); 

Затем мы можем циклически просмотреть элементы всех regions и применить к ним процент прозрачности в соответствии с расчетом населения / максимального значения (с небольшой помощью jQuery):

 $(function() { for(i=0; i < regions.length; i++) { $('#'+ regions[i].region_code).css({'fill': 'rgba(11, 104, 170,' + regions[i].population/highest_value + ')'}); } }); 

Это результат:

Цветная карта

Добавление интерактивности с помощью CSS и jQuery

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

Во-первых, мы добавляем правило CSS для g:hover и новый класс info_panel чтобы info_panel наши информационные блоки:

 .map g:hover { fill: #fc0 !important; cursor: help; } .info_panel { background-color: rgba(255,255,255, .7); padding: .3em; font-size: .8em; font-family: Helvetica, Arial, sans-serif; position: absolute; } .info_panel::first-line { font-weight: bold; } 

Модификатор !important в .map g:hover необходим для улучшения специфики правила fill , в противном случае его можно было бы обойти встроенным CSS.

Затем мы должны изменить наш предыдущий цикл for , добавив .data() для хранения информации, которая будет отображаться при наведении:

 for (i = 0; i < regions.length; i++) { $('#'+ regions[i].region_code) .css({'fill': 'rgba(11, 104, 170,' + regions[i].population/highest_value +')'}).data('region', regions[i]); } 

Наконец, мы можем завершить наш скрипт, добавив несколько эффектов наведения мыши:

 $('.map g').mouseover(function (e) { var region_data=$(this).data('region'); $('<div class="info_panel">' + region_data.region_name + '<br>' + 'Population: ' + region_data.population.toLocaleString("en-UK") + '</div>').appendTo('body'); }).mouseleave(function () { $('.info_panel').remove(); }).mousemove(function(e) { var mouseX = e.pageX, // X coordinates of mouse mouseY = e.pageY; // Y coordinates of mouse $('.info_panel').css({ top: mouseY-50, left: mouseX - ($('.info_panel').width() / 2) }); }); 

Как это устроено:

  • Во-первых, при наведении mouseover мы создаем div содержащий информацию для отображения (название региона и население). Div создается каждый раз, когда мышь наводит курсор на элемент g и добавляется к body документа;
  • mouseleave удаляет этот div, когда курсор находится вне наведенной области;
  • Последний метод, mousemove , извлекает координаты мыши и присваивает их сгенерированным mousemove .

Вот окончательный результат на CodePen: