Сегодня я расскажу вам как сделать интерактивную SVG картограмму при помощи
d3js.org, о возможностях этой JavaScript библиотеки в общем, а также придётся немного разобраться в том как и где лучше хранить геоинформацию для веба. В финале мы получим следующее:
Начать сие увлекательное путешествие можно под катом.
Дела картографические.
В принципе, этот раздел можно пропустить, если не интересно, ссылка на нужный файл в самом конце раздела. Кому интересно, разбираемся дальше. Что такое карта, по сути это информация о геометрии некоего объекта с привязкой к координатам. В 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.
Шифровка от GIS-Lab
Вообще говоря, эта фича присутствует в shapefile изначально, но заметил я этот «приятный» сюрприз только на этом этапе. Фича в том, что все названия регионов зашифрованы от врагов Родины и отображаются крякозябрами. Но наш то человек знает где искать
шифровальную книгу. Но не тут-то было. Ни одна кириллическая кодировка не подошла (на DOS-866 были осмысленные названия, но часть букв отображались различными квадратиками), тут я призадумался и пошёл искать истину в интернете, может искал я плохо, но на форумах GIS-Lab, да и в других местах ничего по поводу кракозябр на этой карте не было вообще (а карта от 2010 года, как я понимаю) тут я совсем отчаялся, открыл снова EditPad (там, по-моему, больше всего кодировок представлено, да и вообще он весьма удобен для работы с текстом и регулярками) и начал перебирать все кодировки подряд, и, о чудо, при выборе кодировки MIK: Bulgarian (!?) получил почти что хотел, а именно названия регионов. Правда все буквы в названиях были разделены досовским символом ├, ну простенькая регулярка решила эту проблему довольно быстро. Хотя почему проблему, это ведь шифр и мы прошли проверку свой-чужой =). Да кстати есть у этого файла ещё одна фича, о которой правда сразу упоминают на 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. Скопируйте следующий шаблон и приступим:
Accidents on the Road - Choroplethtitle>