Генератор иконок по геоданным MIG

    Приветствую хабросообщество. Хочу поделиться с вами одним из своих последних мини-проектов — генератором иконок по геоданным MIG. С его помощью можно сгенерировать векторные (SVG) и растровые (PNG) иконки, с заданными параметрами (цвет, размер, обводка и прочее).

    MIG demopage

    Всё это работает прямо в браузере и распространяется под лицензией 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).

    Если есть какие-то вопросы, что-то непонятно, нашли баг или ещё что — пишите.

    На этом всё, удачи!
    Поделиться публикацией

    Похожие публикации

    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама

    Комментарии 10

      +2
      Неимоверно крутая идея!
        0
        Спасибо!
        +2
        было бы супер если была возможность в качестве фона указывать флаг страны.
          0
          Да, тоже думал об этом, точнее о clipping'e флага иконкой. Просто изначально не хотел перегружать демо всякими надстройками, чтобы не создавалось впечатление будто проект посвящён только странам мира. Ведь смысл в том, что можно взять любую карту. Возможно надо создать ещё один форк, заточенный именно под иконки стран мира.
          +1
          Вещь конечно крутая, но что же Вы с Францией сделали?
          Я чуть глаза не сломал, пытаясь ее найти. Только увеличив размер иконок до 200px понял, что алгоритм прихватывает т.н. заморские владения. Гвиану, если не ошибаюсь. Из-за этого Франция сама на себя не похожа.
            0
            Я писал об этом нюансе в статье:
            страны, через которые проходит антимеридиан (в том числе наша многострадальная Россия) или страны с большим разбросом территорий (островные) будут выглядеть очень мелко. Это можно поправить с помощью подходящей проекции, или удаления (фильтрации) части территорий отображаемого региона

            Тут проще всего удалить не континентальную часть Франции из геоданных, обычно так и делают. А вот с Фиджи уже без кастомной проекции никак не обойтись.
            0
            Франция выглядит уж больно странно.
            ЗЫ. Сорри, не заметил предыдущий коммент
              0
              Ответил выше.
              0
              Очень классно, выглядит как замечательный продукт, но зачем он нужен (не говорю, что бесполезен — просто дайте moar usecases)?
                +1
                Например, для дизайнеров инфографики. А вообще я как-то сам искал иконки стран и понял, что с кастомизацией у имеющихся наборов проблемы. Тогда я и решил, почему бы не сделать генератор. Собственно так и появился этот проект, just for fun, просто потому что я знал как это сделать.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое