1
// radar roadmap (via Ernst and Young)
8
"github.com/ajstarks/svgo"
16
width, height, iscale, fontsize, margin int
17
bgcolor, itemcolor, title string
20
gstyle = "font-family:Calibri;font-size:%dpx;text-anchor:middle"
21
canvas = svg.New(os.Stdout)
24
// Roadmap XML structure:
25
// a roadmap consists of sections, which contain items, which indicate maturity and impact
27
Title string `xml:"title,attr"`
28
Duration int `xml:"duration,attr"`
29
Unit string `xml:"unit,attr"`
30
Section []section `xml:"section"`
34
Name string `xml:"name,attr"`
35
Spacing float64 `xml:"spacing,attr"`
36
Item []item `xml:"item"`
40
Impact int `xml:"impact,attr"`
41
Effort int `xml:"effort,attr"`
42
Begin string `xml:"begin,attr"`
43
Age float64 `xml:"age,attr"`
44
Name string `xml:",chardata"`
45
Desc desc `xml:"desc"`
49
Description string `xml:",chardata"`
53
func dorr(location string) {
56
if len(location) > 0 {
57
f, err = os.Open(location)
65
fmt.Fprintf(os.Stderr, "%v\n", err)
69
// readrr reads and parses the XML specification
70
func readrr(r io.Reader) {
72
if err := xml.NewDecoder(r).Decode(&rm); err == nil {
75
fmt.Fprintf(os.Stderr, "%v\n", err)
79
// drawrr draws the roadmap
80
func drawrr(rm Roadmap) {
82
if len(rm.Title) > 0 {
86
canvas.Gstyle(fmt.Sprintf(gstyle, fontsize))
87
canvas.Rect(0, 0, width, height, "fill:"+bgcolor)
88
duration := rm.Duration
95
r := ((width - margin) / duration) / 2
97
midsize := width / 100
99
// for each unit of time, draw cencentric circles
100
for i := 0; i < duration; i++ {
101
canvas.Circle(cx, cy, sr, "fill:none;stroke:lightgray;stroke-dasharray:7,7")
102
canvas.Text(cx, (cy - sr), fmt.Sprintf("%s %d", rm.Unit, i+1), "font-size:150%;fill:gray")
105
// for each section, define its boundaries and draw its label
106
angle := 360.0 / float64(ns)
109
for _, s := range rm.Section {
110
drawseclines(cx, cy, float64(r*duration), a, a2, s.Name)
113
spacing = (angle / float64(len(s.Item))) - 1
115
iangle := a + spacing
116
// for each item in the section, place the marker and label
117
for _, i := range s.Item {
118
itemx, itemy := dpolar(cx, cy, i.Age*float64(r), iangle)
119
drawitem(itemx, itemy, i.Impact*iscale, i.Effort, i.Name)
125
canvas.Circle(cx, cy, midsize, "fill:red")
126
canvas.Text(cx, cy, "READY", "baseline-shift:-25%")
133
// radians converts degrees to radians
134
func radians(d float64) float64 {
135
return d * (math.Pi / 180.0)
138
// dotitle places the title text
139
func dotitle(s string) {
140
canvas.Text(width/2, height-10, s, "font-size:200%;text-anchor:middle")
143
// dpolar returns the cartesion coordinates given the center, size, and angle (in degrees)
144
func dpolar(cx, cy int, r, d float64) (int, int) {
145
x := r * math.Cos(radians(d))
146
y := r * math.Sin(radians(d))
147
return cx + int(x), cy + int(y)
150
// drawseclines defines and labels the sections
151
func drawseclines(cx, cy int, size, a, h float64, s string) {
152
fs := fontsize + (fontsize / 2)
153
ix, iy := dpolar(cx, cy, size, a)
154
ix2, iy2 := dpolar(cx, cy, size+50, a+h)
155
canvas.Line(cx, cy, ix, iy, "stroke:lightgray")
156
textlines(ix2, iy2, fs, fs+2, "middle", "black", strings.Split(s, "\\n"))
159
// drawitem draws a roadmap item
160
func drawitem(x, y, isize, ieffort int, s string) {
163
op = opacity * (float64(ieffort) / 10.0)
167
style := fmt.Sprintf("fill:%s;fill-opacity:%.2f;stroke:white", itemcolor, op)
168
canvas.Circle(x, y, isize/2, style)
169
textlines(x-(isize/2)-2, y, fontsize, fontsize+2, "end", "black", strings.Split(s, "\\n"))
172
// textlines displays text at a specified size, leading, fill, and alignment
173
func textlines(x, y, fs, leading int, align, fill string, s []string) {
174
canvas.Gstyle(fmt.Sprintf("font-size:%dpx;text-anchor:%s;fill:%s", fs, align, fill))
175
for _, v := range s {
182
// init sets up the command flags
184
flag.StringVar(&bgcolor, "bg", "white", "background color")
185
flag.StringVar(&itemcolor, "ic", "rgb(131,206,226)", "item color")
186
flag.IntVar(&width, "w", 800, "width")
187
flag.IntVar(&height, "h", 800, "height")
188
flag.IntVar(&fontsize, "f", 12, "fontsize (px)")
189
flag.IntVar(&iscale, "s", int(float64(width)*.009), "impact scale")
190
flag.IntVar(&margin, "m", 150, "outside margin")
191
flag.BoolVar(&showtitle, "showtitle", false, "show title")
192
flag.StringVar(&title, "t", "Roadmap", "title")
193
flag.Float64Var(&opacity, "o", 1.0, "opacity")
197
// for every input file (or stdin), draw a roadmap as specified by command flags
199
canvas.Start(width, height)
200
if len(flag.Args()) == 0 {
203
for _, f := range flag.Args() {