Начать сие увлекательное путешествие можно под катом.
Дела картографические.
В принципе, этот раздел можно пропустить, если не интересно, ссылка на нужный файл в самом конце раздела. Кому интересно, разбираемся дальше. Что такое карта, по сути это информация о геометрии некоего объекта с привязкой к координатам. В GIS системах для этих целей обычно используют shapefile’ы. Мы будем рисовать карту России, скорее всего поиск приведёт вас туда же куда и меня, а именно на GIS-Lab. Я выбрал проекцию Albers-Siberia. Скачиваем. Первым делом нам нужно преобразовать наш shapefile по стандарту WGS 84 (оно же EPSG:4326). Для этого необходимо создать файл проекции, например Albers_Siberia.prj
со следующим содержимым :
+proj=aea +lat_1=52 +lat_2=64 +lat_0=0 +lon_0=105 +x_0=18500000 +y_0=0 +ellps=krass +units=m +towgs84=28,-130,-95,0,0,0,0 +no_defs
Затем при помощи GDAL, а точнее одной из его библиотек OGR выполним преобразование. Я для этих нужд скачал Quantum GIS, который уже содержит всё что нужно и даже больше. После установки у вас появится несколько ярлыков, ищем среди них OSGeo4W
, жмакаем, переходим в каталог с нашими файлами и вводим команду следующего вида:
ogr2ogr -f 'ESRI Shapefile' -s_srs Albers_Siberia.prj -t_srs EPSG:4326 input-fixed.shp input.shp
Таким образом мы получили нужный нам shapefile, хотя как нужный, для веба он нам совсем не подходит, поэтому теперь сгенерируем GeoJSON файл на основе наших данных. Затем из GeoJSON’а сгенерируем TopoJSON, который-то и нужен для нашей картограммы. Такие вот дела, но вы не расстраивайтесь, GeoJSON- штука тож полезная, авось пригодится. Итак, идём опять в консоль и пишем примерно следующее:
ogr2ogr -f GeoJSON output.json input.shp
Получаем наш GeoJSON файл, открываем его и видим сюрприз от GIS-Lab.
Теперь перейдём к генерации TopoJSON, это позволит нам уменьшить размер файла. Вообще TopoJSON является оптимизацией GeoJSON’а в плане топологий, он убирает избыточную информацию, например убирает дублирование общих границ у соседних регионов. Но мы можем уменьшить размер файла ещё больше, упростив геометрию. Итак, приступим! Запускаем командную строку Node.js (он нужен для имплементации TopoJSON) и пишем следующее:
topojson -o output_topo.json -p -s 1e-7 -- name=input_geo.json
Здесь параметр -p
отвечает за сохранение feature properties
, а -s 1e-7
за упрощение геометрии, 1e-7
это порог в стерадианах чем меньше, тем точнее геометрия: 1e-3
это Швейцария относительно карты мира, а 1e-9
футбольное поле. Для чего это может быть нужно — если вы захотите сделать возможность зума на вашей карте. Разделитель --
это просто разделитель (ваш К.О.) выходного и входного файлов, а префикс russia
задаёт имя объекта, если его не указывать, то в качестве имени будет использоваться имя входного файла, что не всегда удобно (может быть громоздким). В полученном файле я заменил названия регионов на коды в соответствии с ISO 3166-2:RU. Всё. Файл можно взять на GitHub.
Рисуем картограмму
Карту отрисовывать будем вообще или как?! Теперь у нас есть всё, что необходимо для отрисовки карты средствами d3.js. Скопируйте следующий шаблон и приступим:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Accidents on the Road - Choropleth</title> <script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script> <script type="text/javascript" src="http://d3js.org/queue.v1.min.js"></script> <script type="text/javascript" src="http://d3js.org/topojson.v0.min.js"></script> <!-- <script type="text/javascript" src="http://d3js.org/topojson.v1.min.js"></script> --> </head> <style> your awesome CSS </style> <body> <h1>Cool Header</h1> <script type="text/javascript"> Your awesome d3.js code </script> </body> </html>
Сначала зададим размеры нашей SVG карты.
var width = 960, height = 500;
Зададим домен цветов для картограммы, домен для легенды и подписи к легенде.
var color_domain = [50, 150, 350, 750, 1500] var ext_color_domain = [0, 50, 150, 350, 750, 1500] var legend_labels = ["< 50", "50+", "150+", "350+", "750+", "> 1500"] var color = d3.scale.threshold() .domain(color_domain) .range(["#adfcad", "#ffcb40", "#ffba00", "#ff7d73", "#ff4e40", "#ff1300"]);
Добавим в документ элемент и класс tooltip.
var div = d3.select("body").append("div") .attr("class", "tooltip") .style("opacity", 0);
Добавим SVG с атрибутами, задающими размер.
var svg = d3.select("body").append("svg") .attr("width", width) .attr("height", height);
Зададим параметры проецирования (вспоминаем/смотрим Albers_Siberia.prj из начала статьи):
var projection = d3.geo.albers() .rotate([-105, 0]) .center([-10, 65]) .parallels([52, 64]) .scale(700) .translate([width / 2, height / 2]); var path = d3.geo.path().projection(projection);
Читаем данные.
queue() .defer(d3.json, "/d/5685937/russia_1e-7sr.json") .defer(d3.csv, "Accidents.csv") .await(ready);
Начинаем отрисовку. Создаем объекты для пар код региона: кол-во смертей
и код региона: название региона
.
function ready(error, map, data) { var rateById = {}; var nameById = {}; data.forEach(function(d) { rateById[d.RegionCode] = +d.Deaths; nameById[d.RegionCode] = d.RegionName; });
Отрисовка и раскраска картограммы.
svg.append("g") .attr("class", "region") .selectAll("path") .data(topojson.object(map, map.objects.russia).geometries) //.data(topojson.feature(map, map.objects.russia).features) <-- in case topojson.v1.js .enter().append("path") .attr("d", path) .style("fill", function(d) { return color(rateById[d.properties.region]); }) .style("opacity", 0.8)
Обрабатываем события: меняем яркость региона (для подсветки) и выводим в tooltip’е название региона и точное численное значение.
.on("mouseover", function(d) { d3.select(this).transition().duration(300).style("opacity", 1); div.transition().duration(300) .style("opacity", 1) div.text(nameById[d.properties.region] + " : " + rateById[d.properties.region]) .style("left", (d3.event.pageX) + "px") .style("top", (d3.event.pageY -30) + "px"); }) .on("mouseout", function() { d3.select(this) .transition().duration(300) .style("opacity", 0.8); div.transition().duration(300) .style("opacity", 0); })
Теперь хотелось бы научиться добавлять чего-нибудь на эту самую карту, я решил добавить города-милионники России, для этого нам собственно нужен сам город и его координаты (широта и долгота в десятичных градусах), к сожалению найти геокодер вроде этого gpsvisualizer.com/geocoder, чтобы он понимал русский язык- я не смог (может кто знает?), а лезть в API Яндекс.Карт не хотелось, тем более что список маленький. Хорошо бы они сами колдунчик такой сделали, ну да ладно, отвлекся я. В итоге получили список следующего вида:
City lat lon Москва 55.7522200 37.6155600 Санкт-Петербург 59.8944400 30.2641700
Ну собственно и добавим их, группой: точка-подпись
.
d3.tsv("cities.tsv", function(error, data) { var city = svg.selectAll("g.city") .data(data) .enter() .append("g") .attr("class", "city") .attr("transform", function(d) { return "translate(" + projection([d.lon, d.lat]) + ")"; }); city.append("circle") .attr("r", 3) .style("fill", "lime") .style("opacity", 0.75); city.append("text") .attr("x", 5) .text(function(d) { return d.City; }); }); };
Тут хотелось бы добавить, что вместо точек можно добавить, например, круговую/кольцевую диаграмму, увеличив тем самым информационную нагрузку на нашу картограмму. Вообще возможностей масса, всё ограничивается вашими задачами, воображением и целесообразностью с точки зрения UI/UX.
Ну и под конец добавим легенду нашей картограмме:
var legend = svg.selectAll("g.legend") .data(ext_color_domain) .enter().append("g") .attr("class", "legend"); var ls_w = 20, ls_h = 20; legend.append("rect") .attr("x", 20) .attr("y", function(d, i){ return height - (i*ls_h) - 2*ls_h;}) .attr("width", ls_w) .attr("height", ls_h) .style("fill", function(d, i) { return color(d); }) .style("opacity", 0.8); legend.append("text") .attr("x", 50) .attr("y", function(d, i){ return height - (i*ls_h) - ls_h - 4;}) .text(function(d, i){ return legend_labels[i]; });
Вот в принципе и всё, я старался показать основные моменты на простом примере, надеюсь что мне это удалось. Код конечно не идеален (идеального ничего не бывает), но цель, повторюсь, была сделать его понятным, а не супер универсальным/эффективным. Исходники можно найти на GitHub, а пощупать результат можно через сервис bl.ocks.org. Да, CSS мы тут не рассматривали, но там всё тривиально.
Итоги. Что дальше?
Ну вот мы и создали простенькую картограмму, без возможности приближения, сложной анимации и прочих наворотов, но при желании добавить их сюда не составит большого труда. Вообще данная библиотека имеет широчайшие возможности для визуализации всего и вся: графики, диаграммы, картограммы, деревья, графы, чарты, тепловые карты…. Такой вот DataViz комбайн, всё что нужно можно найти через сайт d3js.org. Mike Bostock активно развивает проект, почти каждый день выкладывает новые примеры (один минус, почти все без комментариев), на stackoverflow тоже много чего и отвечают там оперативно, в том числе и сам Майк. Так что дерзайте, имхо эта библиотека, являющаяся по сути основным инструментом визуализации за бугром, у нас несправедливо обделена вниманием. Ну и я, если это будет интересно хабросообществу, буду периодически разбирать интересные примеры. Собственно всё, комментарии, вопросы и предложения приветствуются.
P.S. Чуть не забыл самое главное, будьте осторожнее на дорогах, особенно если вы не с Чукотки, уродов всяких у нас хватает и масса видео с регистраторов тому подтверждение! А вообще это печально, почти 28 тысяч смертей за год, ужас просто (данные брал с офф. сайта ГИБДД).
ссылка на оригинал статьи http://habrahabr.ru/post/181766/
Добавить комментарий