Go — универсальный язык программирования который отлично подходит для фоновых задач, но иногда вам может понадобится генерировать изображения опираясь на входящие данные. Go отлично работает с созданием визуальных объектов. В этом посте описан один из методов создания изображений (в частности векторной графики) на основе данных с помощью пакета SVGo.
Библиотека SVGo занимется одной единственной задачей: генерирует SVG и отдает его в io.Writer. I\O пакет в Go позволяет вам выводить результаты используя необходимый интерфейс (стандартный вывод, файлы, сетевые соеденения, веб сервер).
Для SVGo первостепенны высокоуровневые объекты такие как круги, прямоугольники, линии, полигоны и кривые. Стили и атрибуты являются второстепенными и применяются по мере необходимости.
Наш пример будет включать в себя следующие шаги:
Вот простой пример, который берет данные из XML, на основе их рисует примитивные SVG объекты и возвращает их в стандартный вывод.
Необходимо понимать что для своих собственных данных вы можете описывать струтктуру как вам захочется, в то же время если вы получаете данные из API сторонних сервисов то вам необходимо будет описывать струтуру данных для каждого отдельно.
Пример входящих данных:
Сначала определим структуру входящих данных. Вы можете видеть соотвтствие между элементами и атрибутами в Go. К примеру struct «thing» содержит параметры Top и Left которые задают отступы, параметр sep который задает расстояние между элементами и параметр Items который является список и будет содержать в себе вложенные элементы. В свою очередь каждый из вложенных элементов тоже имеет набор параметров таких как Width, Height, Name, Color, Text.
Затем мы должны указать назначение вывода для SVG (в нашем примере это будет стандартный вывод) и размеры холста:
После этого определяем функцию для чтения входящих данных:
Следующим важным шагом является определение функции которая ответчает за загрузку и анализ входящих данных. Здесь мы будем использовать XML пакет входящий в стандартную библиотеку Go.
Когда все данные загружены, мы проходим по ним и отрисовываем объекты. Для этого мы используем возможности пакета SVGo. В нашем случае мы утсанавливаем координаты (x,y) для каждого элемента и рисуем круг который соответствует заданным в атрибутах элемента размерам и цветам а также добавляем текст и применяем вертикальные интервалы.
Дальше мы описываем main функцию нашего примера в которой будем получать имя файла с данными
Пример запуска нашего примера и результаты его работы:
Готовый SVG будет выглядеть следующим образом:
Используя этот пример вы можете создавать много разных инструментов визуализации. Например в своей работе я использую инструменты которые умеют строить как простые графики barcharts и bulletgraphs так и более сложные pie-charts и component diagrams.
Так же вы можете создавать изображения на основе данный из API любых сервисов. Для примера программа “f50” берет слово и на основе его генерирует сетку изображений полученных из Flickr API. f50 использует такой же подход за исключением того что данные беруться не из локального файла а формируется HTTPS запрос API Flickr.
Пример использования:
Flickr API возвращает такой ответ:
Для создания SVG нам понадобяться параметры id, secret, farm, server и title.
Если вы откроете получившийся SVG в браузере то при наведении на картинку увидите её заголовок а при клике — перейдете на оригинальное изображение.
Библиотека SVGo занимется одной единственной задачей: генерирует SVG и отдает его в io.Writer. I\O пакет в Go позволяет вам выводить результаты используя необходимый интерфейс (стандартный вывод, файлы, сетевые соеденения, веб сервер).
Для SVGo первостепенны высокоуровневые объекты такие как круги, прямоугольники, линии, полигоны и кривые. Стили и атрибуты являются второстепенными и применяются по мере необходимости.
Чтение, анализ, отрисовка объектов.
Наш пример будет включать в себя следующие шаги:
- Определение структуры входящих данных
- Чтение данных
- Разбор и загурзка структуры даных
- Отрисовка изображения
Вот простой пример, который берет данные из XML, на основе их рисует примитивные SVG объекты и возвращает их в стандартный вывод.
Необходимо понимать что для своих собственных данных вы можете описывать струтктуру как вам захочется, в то же время если вы получаете данные из API сторонних сервисов то вам необходимо будет описывать струтуру данных для каждого отдельно.
Пример входящих данных:
<thing top="100" left="100" sep="100">
<item width="50" height="50" name="Little" color="blue">This is small</item>
<item width="75" height="100" name="Med" color="green">This is medium</item>
<item width="100" height="200" name="Big" color="red">This is large</item>
</thing>
Сначала определим структуру входящих данных. Вы можете видеть соотвтствие между элементами и атрибутами в Go. К примеру struct «thing» содержит параметры Top и Left которые задают отступы, параметр sep который задает расстояние между элементами и параметр Items который является список и будет содержать в себе вложенные элементы. В свою очередь каждый из вложенных элементов тоже имеет набор параметров таких как Width, Height, Name, Color, Text.
type Thing struct {
Top int `xml:"top,attr"`
Left int `xml:"left,attr"`
Sep int `xml:"sep,attr"`
Item []item `xml:"item"`
}
type item struct {
Width int `xml:"width,attr"`
Height int `xml:"height,attr"`
Name string `xml:"name,attr"`
Color string `xml:"color,attr"`
Text string `xml:",chardata"`
}
Затем мы должны указать назначение вывода для SVG (в нашем примере это будет стандартный вывод) и размеры холста:
var (
canvas = svg.New(os.Stdout)
width = flag.Int("w", 1024, "width")
height = flag.Int("h", 768, "height")
)
После этого определяем функцию для чтения входящих данных:
func dothing(location string) {
f, err := os.Open(location)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return
}
defer f.Close()
readthing(f)
}
Следующим важным шагом является определение функции которая ответчает за загрузку и анализ входящих данных. Здесь мы будем использовать XML пакет входящий в стандартную библиотеку Go.
func readthing(r io.Reader) {
var t Thing
if err := xml.NewDecoder(r).Decode(&t); err != nil {
fmt.Fprintf(os.Stderr, "Unable to parse components (%v)\n", err)
return
}
drawthing(t)
}
Когда все данные загружены, мы проходим по ним и отрисовываем объекты. Для этого мы используем возможности пакета SVGo. В нашем случае мы утсанавливаем координаты (x,y) для каждого элемента и рисуем круг который соответствует заданным в атрибутах элемента размерам и цветам а также добавляем текст и применяем вертикальные интервалы.
func drawthing(t Thing) {
x := t.Left
y := t.Top
for _, v := range t.Item {
style := fmt.Sprintf("font-size:%dpx;fill:%s", v.Width/2, v.Color)
canvas.Circle(x, y, v.Height/4, "fill:"+v.Color)
canvas.Text(x+t.Sep, y, v.Name+":"+v.Text+"/"+v.Color, style)
y += v.Height
}
}
Дальше мы описываем main функцию нашего примера в которой будем получать имя файла с данными
func main() {
flag.Parse()
for _, f := range flag.Args() {
canvas.Start(*width, *height)
dothing(f)
canvas.End()
}
}
Пример запуска нашего примера и результаты его работы:
$ go run rpd.go thing.xml
<?xml version="1.0"?>
<!-- Generated by SVGo -->
<svg width="1024" height="768"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<circle cx="100" cy="100" r="12" style="fill:blue"/>
<text x="200" y="100" style="font-size:25px;fill:blue">Little:This is small/blue</text>
<circle cx="100" cy="150" r="25" style="fill:green"/>
<text x="200" y="150" style="font-size:37px;fill:green">Med:This is medium/green</text>
<circle cx="100" cy="250" r="50" style="fill:red"/>
<text x="200" y="250" style="font-size:50px;fill:red">Big:This is large/red</text>
</svg>
Готовый SVG будет выглядеть следующим образом:
Используя этот пример вы можете создавать много разных инструментов визуализации. Например в своей работе я использую инструменты которые умеют строить как простые графики barcharts и bulletgraphs так и более сложные pie-charts и component diagrams.
Так же вы можете создавать изображения на основе данный из API любых сервисов. Для примера программа “f50” берет слово и на основе его генерирует сетку изображений полученных из Flickr API. f50 использует такой же подход за исключением того что данные беруться не из локального файла а формируется HTTPS запрос API Flickr.
Пример использования:
f50 sunset
Flickr API возвращает такой ответ:
<?xml version="1.0" encoding="utf-8" ?>
<rsp stat="ok">
<photo id="15871035007" ... secret="84d59df678" server="7546" farm="8" title="flickr-gopher" ... />
<photo id="15433662714" ... secret="3b9358c61d" server="7559" farm="8" title="Laurence Maroney 2006..." ... />
...
</rsp>
Для создания SVG нам понадобяться параметры id, secret, farm, server и title.
// makeURI converts the elements of a photo into a Flickr photo URI
func makeURI(p Photo, imsize string) string {
im := p.Id + "_" + p.Secret
if len(imsize) > 0 {
im += "_" + imsize
}
return fmt.Sprintf(urifmt, p.Farm, p.Server, im)
}
// imageGrid reads the response from Flickr, and creates a grid of images
func imageGrid(f FlickrResp, x, y, cols, gutter int, imgsize string) {
if f.Stat != "ok" {
fmt.Fprintf(os.Stderr, "Status: %v\n", f.Stat)
return
}
xpos := x
for i, p := range f.Photos.Photo {
if i%cols == 0 && i > 0 {
xpos = x
y += (imageHeight + gutter)
}
canvas.Link(makeURI(p, ""), p.Title)
canvas.Image(xpos, y, imageWidth, imageHeight, makeURI(p, "s"))
canvas.LinkEnd()
xpos += (imageWidth + gutter)
}
}
Если вы откроете получившийся SVG в браузере то при наведении на картинку увидите её заголовок а при клике — перейдете на оригинальное изображение.