Інша сторона Go: малюємо аналізуючи дані

Go — універсальна мова програмування який відмінно підходить для фонових завдань, але іноді вам може знадобиться генерувати зображення спираючись на вхідні дані. Go відмінно працює з створенням візуальних об'єктів. В цьому пості описаний один з методів створення зображень (зокрема векторної графіки) на основі даних за допомогою пакету SVGo.

Бібліотека 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, Тексту.
type Thing struct {
Top int `xml:"top,attr"`
Int Left `xml:"left,attr"`
Sep int `xml:"sep,attr"`
Item []item `xml:"item"`
}

type item struct {
Int Width `xml:"width,attr"`
Int Height `xml:"height,attr"`
String Name `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®.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 запит Flickr API.
Приклад використання:
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 у браузері то при наведенні на картинку побачите її назву а при кліці — перейдете на оригінальне зображення.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.