The other side of Go: draw by analyzing data
- Transfer
Go is a universal programming language that is great for background tasks, but sometimes you may need to generate images based on incoming data. Go works great with creating visual objects. This post describes one of the methods for creating images (in particular vector graphics) based on data using the SVGo package .
The SVGo library does one single task: it generates SVG and gives it to io.Writer . I \ O package in Go allows you to output results using the necessary interface (standard output, files, network connections, web server).
For SVGo, high-level objects such as circles, rectangles, lines, polygons, and curves are paramount. Styles and attributes are secondary and apply as needed.
Our example will include the following steps:
Here is a simple example that takes data from XML, draws primitive SVG objects from them, and returns them to standard output.
You need to understand that for your own data you can describe the structure as you like, at the same time if you get data from the API of third-party services, you will need to describe the data structure for each separately.
Example input:
First, we determine the structure of the input data. You can see the correspondence between elements and attributes in Go. For example, the struct “thing” contains Top and Left parameters that specify the indentation, sep parameter which sets the distance between the elements and Items parameter which is a list and will contain nested elements. In turn, each of the nested elements also has a set of parameters such as Width , Height , Name , Color , Text .
Then we must specify the output destination for SVG (in our example, this will be standard output) and the canvas sizes:
After that, we define a function for reading incoming data:
The next important step is to identify the function that is responsible for loading and analyzing incoming data. Here we will use the XML package included in the Go standard library.
When all the data is loaded, we go through it and draw the objects. To do this, we use the capabilities of the SVGo package. In our case, we set the coordinates (x, y) for each element and draw a circle that corresponds to the sizes and colors specified in the element attributes and also add text and apply vertical intervals.
Further we describe the main function of our example in which we will get the name of the data file
An example of the launch of our example and the results of its work:
The finished SVG will look like this:
Using this example, you can create many different visualization tools. For example, in my work I use tools that can build both simple barcharts and bulletgraphs and more complex pie-charts and component diagrams .
You can also create images based on the data from the API of any services. For example, the “f50” program takes a word and based on it generates a grid of images obtained from the Flickr API. f50 uses the same approach except that the data is not taken from a local file but an Flickr API HTTPS request is generated.
Usage example:
The Flickr API returns this response:
To create an SVG, we need the id, secret, farm, server, and title parameters.
If you open the resulting SVG in a browser, then when you hover over the picture you will see its title and when you click it, you will go to the original image.
The SVGo library does one single task: it generates SVG and gives it to io.Writer . I \ O package in Go allows you to output results using the necessary interface (standard output, files, network connections, web server).
For SVGo, high-level objects such as circles, rectangles, lines, polygons, and curves are paramount. Styles and attributes are secondary and apply as needed.
Reading, analysis, drawing objects.
Our example will include the following steps:
- Defining the structure of incoming data
- Reading data
- Parsing and storing data structures
- Image rendering
Here is a simple example that takes data from XML, draws primitive SVG objects from them, and returns them to standard output.
You need to understand that for your own data you can describe the structure as you like, at the same time if you get data from the API of third-party services, you will need to describe the data structure for each separately.
Example input:
- This is small
- This is medium
- This is large
First, we determine the structure of the input data. You can see the correspondence between elements and attributes in Go. For example, the struct “thing” contains Top and Left parameters that specify the indentation, sep parameter which sets the distance between the elements and Items parameter which is a list and will contain nested elements. In turn, each of the nested elements also has a set of parameters such as 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"`
}
Then we must specify the output destination for SVG (in our example, this will be standard output) and the canvas sizes:
var (
canvas = svg.New(os.Stdout)
width = flag.Int("w", 1024, "width")
height = flag.Int("h", 768, "height")
)
After that, we define a function for reading incoming data:
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)
}
The next important step is to identify the function that is responsible for loading and analyzing incoming data. Here we will use the XML package included in the Go standard library.
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)
}
When all the data is loaded, we go through it and draw the objects. To do this, we use the capabilities of the SVGo package. In our case, we set the coordinates (x, y) for each element and draw a circle that corresponds to the sizes and colors specified in the element attributes and also add text and apply vertical intervals.
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
}
}
Further we describe the main function of our example in which we will get the name of the data file
func main() {
flag.Parse()
for _, f := range flag.Args() {
canvas.Start(*width, *height)
dothing(f)
canvas.End()
}
}
An example of the launch of our example and the results of its work:
$ go run rpd.go thing.xml
The finished SVG will look like this:
Using this example, you can create many different visualization tools. For example, in my work I use tools that can build both simple barcharts and bulletgraphs and more complex pie-charts and component diagrams .
You can also create images based on the data from the API of any services. For example, the “f50” program takes a word and based on it generates a grid of images obtained from the Flickr API. f50 uses the same approach except that the data is not taken from a local file but an Flickr API HTTPS request is generated.
Usage example:
f50 sunset
The Flickr API returns this response:
...
To create an SVG, we need the id, secret, farm, server, and title parameters.
// 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)
}
}
If you open the resulting SVG in a browser, then when you hover over the picture you will see its title and when you click it, you will go to the original image.