Приветствую хабросообщество. Хочу поделиться с вами одним из своих последних мини-проектов — генератором иконок по геоданным MIG. С его помощью можно сгенерировать векторные (SVG) и растровые (PNG) иконки, с заданными параметрами (цвет, размер, обводка и прочее).
Всё это работает прямо в браузере и распространяется под лицензией MIT. Под катом можно узнать, как этим пользоваться и как оно работает.
Как пользоваться
По умолчанию MIG для генерации иконок использует карту мира в масштабе 1:110m, основанную на геоданных natural earth data. Там содержатся данные для (174+3) стран мира (остальные при таком масштабе вырождаются), плюс три страны — это Северный Кипр, Косово и Сомалиленд. Если вам этого достаточно, то просто задайте нужные вам параметры иконок напрямую в коде, либо воспользуйтесь GUI на демостранице MIG'а. Если нужно отфильтровать часть стран, это можно легко сделать, задав условия для имеющихся свойств (properties
). Например вот так выглядит фильтрация по айдишнику:
.data(topojson.feature(world, world.objects.countries).features
.filter(function(d) {return contryList.indexOf(d.id) > -1;}))
Демка выглядит весьма аскетично, потому что для достижения интересных результатов всё равно придётся менять код. Так что смело форкайте MIG и меняйте его под себя. Да, в демке используются новые input'ы (HTML5), они пока ещё не везде поддерживаются: caniuse, но fallback есть, так что работать в любом случае должно.
Итак, что же делать, если вариант по умолчанию вас не устраивает. Первым делом необходимо определится с новыми геоданными — это может быть карта мира, карта страны с административными границами, карта города с делением на районы или любая другая карта. Как только определились с геоданными необходимо сконвертировать их в TopoJSON. Это можно сделать с помощью Command-line TopoJSON, предварительно установив имплементацию TopoJSON для Node.js. На этапе конвертации необходимо удалить все лишние свойства, также можно добавить названия, если вы не собираетесь использовать для этого внешний файл. Когда новый TopoJSON файл будет готов, обновите пути и названия:
.defer(d3.json, "data/TopoJSON_file.json")
.defer(d3.tsv, "data/external_file_for_names.tsv")
Измените название feature
в соответствии с вашим новым файлом:
.data(topojson.feature(world, world.objects.YourNewFeatureName).features
Всё, теперь можно генерировать иконки!
Дополнительные возможности
Для тех случаев, когда необходимо использовать не стандартную проекцию Меркатора, можно легко добавить одну из доступных проекций или даже создать собственную. При этом можно применять отдельную проекцию хоть для каждой иконки. К слову, в демо примере используются отдельные проекции для России и США.
switch (d.id) {
//Russia
case "RUS": var projection = d3.geo.albers().rotate([-105, 0]).center([-10, 65]).parallels([52, 64]);
path = d3.geo.path().projection(projection);
break;
//USA
case "USA": var projection = d3.geo.albersUsa();
path = d3.geo.path().projection(projection);
break;
default: var projection = d3.geo.mercator();
path = d3.geo.path().projection(projection);
break;
}
В общем есть обширное поле для экспериментов.
Что под капотом
Генератор написан с использованием библиотеки D3.js, код можно посмотреть на GitHub: Map Icons Generator. Итак, что же тут происходит. Сначала грузятся геоданные и соответствующие названия (для случая, когда они не зашиты в TopoJSON файл). Затем для каждого объекта создаётся SVG элемент заданного размера и в него отрисовывается соответствующий регион карты в заданной проекции, при этом центровка и масштаб вычисляются исходя из BoundingBox
. С этим моментом связано несколько нюансов, так как представление геоданных зависит от проекции, в ряде случаев автоцентровка и автомасштабирование дадут неприглядный результат. Например, страны, через которые проходит антимеридиан (в том числе наша многострадальная Россия) или страны с большим разбросом территорий (островные) будут выглядеть очень мелко. Это можно поправить с помощью подходящей проекции, или удаления (фильтрации) части территорий отображаемого региона. Затем переходим к стилизации иконок. На этом этапе можно использовать различные SVG фильтры, градиенты, паттерны, clipping
— в общем всю мощь SVG.
Далее на основе созданных векторных иконок, создаются растровые. Происходит это следующим образом: в цикле пробегаемся по всем SVG элементам, создаём canvas элементы и отрисовываем туда нашу иконку с помощью context.drawImage()
, тут тоже есть небольшой нюанс, у SVG должно быть всё в порядке с xmlns
атрибутом, а у его Blob
'a указан верный MIME тип, иначе ничего у нас не выйдет. Теперь прикручиваем сохранение, запускаем и радуемся полученным иконкам.
Дальнейшее развитие
Думаю добавить фильтрацию по странам и улучшить дизайн демостраницы. Изменить механизм сохранения — сейчас иконки сохраняются либо по отдельности, при нажатии на страну, либо сразу весь регион(континент), при этом на каждую иконку создаётся модальное окно, планирую заменить это на возможность скачать иконки одним архивом. Также планирую добавить возможность упрощения геометрии и точности для геоданных, чтобы можно было регулировать их соответственно размеру иконок. Сейчас это можно сделать на этапе создания TopoJSON (simplification
и quantization
).
Если есть какие-то вопросы, что-то непонятно, нашли баг или ещё что — пишите.
На этом всё, удачи!
Автор: KoGor