Жизнь, веб, XMPP, TAS, электронные самоделки

Рендеринг PNG из SVG на PHP

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

Для облегчения парсинга файла нужно объединить содержащиеся в нём контуры (элементы path) в группы (элементы g). Каждой группе нужно дать атрибут id, по которому эта группа будет идентифицироваться. Дополнительные данные можно хранить в других атрибутах, например, заведя для них отдельное пространство имён. Групп, не являющихся регионами, в файле быть не должно — это упростит нашу задачу.

Для работы с таким файлом из PHP нам понадобятся функции DOM и IMagick. Конечно же, понадобится также и сам SVG-файл. Файл можно взять готовый, а можно создать самостоятельно.

Код генерации изображения следующий:

// Загрузим SVG-файл
$document = new DOMDocument('1.0', 'utf-8');
$document->load(__DIR__ . '/russia-map-structured.svg');

// Регионы, которые нужно выделить
$records = array('OryolOblast', 'AstrakhanOblast', 'Yakutia', 'StavropolKrai', 'KrasnoyarskiyKrai');

// Обходим SVG-файл		
$regions = $document->getElementsByTagName('g');
for($i = 0; $i < $regions->length; $i ++) {
	$region = $regions->item($i);
	$region->setAttribute('fill', in_array($region->getAttribute('id'), $records) ? '#EC4700' : '#CCCCCC');
}

// Делаем рендеринг при помощи ImageMagick
$renderer = new Imagick();
$renderer->readImageBlob($document->saveXML());
$renderer->setImageFormat("png24");
$renderer->resizeImage(640, 388, Imagick::FILTER_LANCZOS, 1);
$renderer->writeImage(__DIR__ . '/out.png');
$renderer->clear();
$renderer->destroy();

Можно заметить, что мы сначала делаем экспорт изображения в PNG с «родным» размером, и лишь потом выполняем масштабирование через фильтр Ланцоша. Это вызывает некоторые накладные расходы и слегка снижает качество изображения, однако, это самый простой способ получить на выходе картинку заданного размера. Так или иначе, выполнение этого кода приведёт к генерированию изображения следующего вида.

На карте можно заметить присутствие некоторых ныне упразднённых регионов РФ. Их можно убрать вручную, отредактировав файл в Inkscape или Xara Extreme, а можно просто включить в нужные группы.

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

Альтернативные методы

Для решения этой задачи можно также использовать расширение rsvg, зависящее, в свою очередь, от расширения cairo.