basics of stacked bar.
This commit is contained in:
parent
020ec8f4a4
commit
a1fb284797
8 changed files with 272 additions and 25 deletions
2
chart.go
2
chart.go
|
@ -463,7 +463,7 @@ func (c Chart) styleDefaultsCanvas() Style {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Chart) styleDefaultsSeries(seriesIndex int) Style {
|
func (c Chart) styleDefaultsSeries(seriesIndex int) Style {
|
||||||
strokeColor := GetDefaultSeriesStrokeColor(seriesIndex)
|
strokeColor := GetDefaultColor(seriesIndex)
|
||||||
return Style{
|
return Style{
|
||||||
StrokeColor: strokeColor,
|
StrokeColor: strokeColor,
|
||||||
StrokeWidth: DefaultStrokeWidth,
|
StrokeWidth: DefaultStrokeWidth,
|
||||||
|
|
|
@ -159,16 +159,16 @@ var (
|
||||||
DashArrayDashesLarge = []int{10, 10}
|
DashArrayDashesLarge = []int{10, 10}
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetDefaultSeriesStrokeColor returns a color from the default list by index.
|
// GetDefaultColor returns a color from the default list by index.
|
||||||
// NOTE: the index will wrap around (using a modulo).
|
// NOTE: the index will wrap around (using a modulo).
|
||||||
func GetDefaultSeriesStrokeColor(index int) drawing.Color {
|
func GetDefaultColor(index int) drawing.Color {
|
||||||
finalIndex := index % len(DefaultColors)
|
finalIndex := index % len(DefaultColors)
|
||||||
return DefaultColors[finalIndex]
|
return DefaultColors[finalIndex]
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultPieChartValueColor returns a color from the default list by index.
|
// GetAlternateColor returns a color from the default list by index.
|
||||||
// NOTE: the index will wrap around (using a modulo).
|
// NOTE: the index will wrap around (using a modulo).
|
||||||
func GetDefaultPieChartValueColor(index int) drawing.Color {
|
func GetAlternateColor(index int) drawing.Color {
|
||||||
finalIndex := index % len(DefaultAlternateColors)
|
finalIndex := index % len(DefaultAlternateColors)
|
||||||
return DefaultAlternateColors[finalIndex]
|
return DefaultAlternateColors[finalIndex]
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,13 @@ func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
FillColor: chart.ColorLightGray,
|
FillColor: chart.ColorLightGray,
|
||||||
},
|
},
|
||||||
Values: []chart.Value{
|
Values: []chart.Value{
|
||||||
{Value: 10, Label: "Blue"},
|
{Value: 5, Label: "Blue"},
|
||||||
{Value: 9, Label: "Green"},
|
{Value: 5, Label: "Green"},
|
||||||
{Value: 8, Label: "Gray"},
|
{Value: 4, Label: "Gray"},
|
||||||
{Value: 7, Label: "Orange"},
|
{Value: 4, Label: "Orange"},
|
||||||
{Value: 6, Label: "HEANG"},
|
{Value: 3, Label: "Test"},
|
||||||
{Value: 5, Label: "??"},
|
{Value: 3, Label: "??"},
|
||||||
{Value: 2, Label: "!!"},
|
{Value: 1, Label: "!!"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
48
examples/stacked_bar/main.go
Normal file
48
examples/stacked_bar/main.go
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/wcharczuk/go-chart"
|
||||||
|
)
|
||||||
|
|
||||||
|
func drawChart(res http.ResponseWriter, req *http.Request) {
|
||||||
|
sbc := chart.StackedBarChart{
|
||||||
|
Background: chart.Style{
|
||||||
|
Padding: chart.Box{Top: 50, Left: 50, Right: 50, Bottom: 50},
|
||||||
|
},
|
||||||
|
Bars: []chart.StackedBar{
|
||||||
|
{
|
||||||
|
Values: []chart.Value{
|
||||||
|
{Value: 5, Label: "Blue"},
|
||||||
|
{Value: 5, Label: "Green"},
|
||||||
|
{Value: 4, Label: "Gray"},
|
||||||
|
{Value: 4, Label: "Orange"},
|
||||||
|
{Value: 3, Label: "Test"},
|
||||||
|
{Value: 3, Label: "??"},
|
||||||
|
{Value: 1, Label: "!!"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Values: []chart.Value{
|
||||||
|
{Value: 10, Label: "Blue"},
|
||||||
|
{Value: 5, Label: "Green"},
|
||||||
|
{Value: 1, Label: "Gray"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
res.Header().Set("Content-Type", "image/svg+xml")
|
||||||
|
err := sbc.Render(chart.SVG, res)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Error rendering chart: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
http.HandleFunc("/", drawChart)
|
||||||
|
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||||
|
}
|
12
pie_chart.go
12
pie_chart.go
|
@ -217,7 +217,7 @@ func (pc PieChart) stylePieChartValue(index int) Style {
|
||||||
return Style{
|
return Style{
|
||||||
StrokeColor: ColorWhite,
|
StrokeColor: ColorWhite,
|
||||||
StrokeWidth: 5.0,
|
StrokeWidth: 5.0,
|
||||||
FillColor: GetDefaultPieChartValueColor(index),
|
FillColor: GetAlternateColor(index),
|
||||||
FontSize: 24.0,
|
FontSize: 24.0,
|
||||||
FontColor: ColorWhite,
|
FontColor: ColorWhite,
|
||||||
Font: pc.GetFont(),
|
Font: pc.GetFont(),
|
||||||
|
@ -232,16 +232,6 @@ func (pc PieChart) styleDefaultsBackground() Style {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pc PieChart) styleDefaultsSeries(seriesIndex int) Style {
|
|
||||||
strokeColor := GetDefaultSeriesStrokeColor(seriesIndex)
|
|
||||||
return Style{
|
|
||||||
StrokeColor: strokeColor,
|
|
||||||
StrokeWidth: DefaultStrokeWidth,
|
|
||||||
Font: pc.GetFont(),
|
|
||||||
FontSize: DefaultFontSize,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pc PieChart) styleDefaultsElements() Style {
|
func (pc PieChart) styleDefaultsElements() Style {
|
||||||
return Style{
|
return Style{
|
||||||
Font: pc.GetFont(),
|
Font: pc.GetFont(),
|
||||||
|
|
209
stacked_bar_chart.go
Normal file
209
stacked_bar_chart.go
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
package chart
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/golang/freetype/truetype"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StackedBar is a bar within a StackedBarChart.
|
||||||
|
type StackedBar struct {
|
||||||
|
Name string
|
||||||
|
Width int
|
||||||
|
Values []Value
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWidth returns the width of the bar.
|
||||||
|
func (sb StackedBar) GetWidth() int {
|
||||||
|
if sb.Width == 0 {
|
||||||
|
return 20
|
||||||
|
}
|
||||||
|
return sb.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
// StackedBarChart is a chart that draws sections of a bar based on percentages.
|
||||||
|
type StackedBarChart struct {
|
||||||
|
Title string
|
||||||
|
TitleStyle Style
|
||||||
|
|
||||||
|
Width int
|
||||||
|
Height int
|
||||||
|
DPI float64
|
||||||
|
|
||||||
|
Background Style
|
||||||
|
Canvas Style
|
||||||
|
|
||||||
|
BarSpacing int
|
||||||
|
|
||||||
|
Font *truetype.Font
|
||||||
|
defaultFont *truetype.Font
|
||||||
|
|
||||||
|
Bars []StackedBar
|
||||||
|
Elements []Renderable
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDPI returns the dpi for the chart.
|
||||||
|
func (sbc StackedBarChart) GetDPI(defaults ...float64) float64 {
|
||||||
|
if sbc.DPI == 0 {
|
||||||
|
if len(defaults) > 0 {
|
||||||
|
return defaults[0]
|
||||||
|
}
|
||||||
|
return DefaultDPI
|
||||||
|
}
|
||||||
|
return sbc.DPI
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetFont returns the text font.
|
||||||
|
func (sbc StackedBarChart) GetFont() *truetype.Font {
|
||||||
|
if sbc.Font == nil {
|
||||||
|
return sbc.defaultFont
|
||||||
|
}
|
||||||
|
return sbc.Font
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetWidth returns the chart width or the default value.
|
||||||
|
func (sbc StackedBarChart) GetWidth() int {
|
||||||
|
if sbc.Width == 0 {
|
||||||
|
return DefaultChartWidth
|
||||||
|
}
|
||||||
|
return sbc.Width
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetHeight returns the chart height or the default value.
|
||||||
|
func (sbc StackedBarChart) GetHeight() int {
|
||||||
|
if sbc.Height == 0 {
|
||||||
|
return DefaultChartWidth
|
||||||
|
}
|
||||||
|
return sbc.Height
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetBarSpacing returns the spacing between bars.
|
||||||
|
func (sbc StackedBarChart) GetBarSpacing() int {
|
||||||
|
if sbc.BarSpacing == 0 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
return sbc.BarSpacing
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render renders the chart with the given renderer to the given io.Writer.
|
||||||
|
func (sbc StackedBarChart) Render(rp RendererProvider, w io.Writer) error {
|
||||||
|
if len(sbc.Bars) == 0 {
|
||||||
|
return errors.New("Please provide at least one bar.")
|
||||||
|
}
|
||||||
|
|
||||||
|
r, err := rp(sbc.GetWidth(), sbc.GetHeight())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sbc.Font == nil {
|
||||||
|
defaultFont, err := GetDefaultFont()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
sbc.defaultFont = defaultFont
|
||||||
|
}
|
||||||
|
r.SetDPI(sbc.GetDPI(DefaultDPI))
|
||||||
|
|
||||||
|
canvasBox := sbc.getAdjustedCanvasBox(sbc.getDefaultCanvasBox())
|
||||||
|
sbc.drawBars(r, canvasBox)
|
||||||
|
|
||||||
|
sbc.drawTitle(r)
|
||||||
|
for _, a := range sbc.Elements {
|
||||||
|
a(r, canvasBox, sbc.styleDefaultsElements())
|
||||||
|
}
|
||||||
|
|
||||||
|
return r.Save(w)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) drawBars(r Renderer, canvasBox Box) {
|
||||||
|
xoffset := canvasBox.Left
|
||||||
|
for _, bar := range sbc.Bars {
|
||||||
|
sbc.drawBar(r, canvasBox, xoffset, bar)
|
||||||
|
xoffset += sbc.GetBarSpacing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) drawBar(r Renderer, canvasBox Box, xoffset int, bar StackedBar) int {
|
||||||
|
bxl := xoffset
|
||||||
|
bxr := xoffset + bar.GetWidth()
|
||||||
|
|
||||||
|
normalizedBarComponents := Values(bar.Values).Normalize()
|
||||||
|
yoffset := canvasBox.Top
|
||||||
|
for index, bv := range normalizedBarComponents {
|
||||||
|
barHeight := int(bv.Value * float64(canvasBox.Height()))
|
||||||
|
barBox := Box{Top: yoffset, Left: bxl, Right: bxr, Bottom: yoffset + barHeight}
|
||||||
|
DrawBox(r, barBox, bv.Style.InheritFrom(sbc.styleDefaultsStackedBarValue(index)))
|
||||||
|
yoffset += barHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
return bxr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) drawTitle(r Renderer) {
|
||||||
|
if len(sbc.Title) > 0 && sbc.TitleStyle.Show {
|
||||||
|
r.SetFont(sbc.TitleStyle.GetFont(sbc.GetFont()))
|
||||||
|
r.SetFontColor(sbc.TitleStyle.GetFontColor(DefaultTextColor))
|
||||||
|
titleFontSize := sbc.TitleStyle.GetFontSize(DefaultTitleFontSize)
|
||||||
|
r.SetFontSize(titleFontSize)
|
||||||
|
|
||||||
|
textBox := r.MeasureText(sbc.Title)
|
||||||
|
|
||||||
|
textWidth := textBox.Width()
|
||||||
|
textHeight := textBox.Height()
|
||||||
|
|
||||||
|
titleX := (sbc.GetWidth() >> 1) - (textWidth >> 1)
|
||||||
|
titleY := sbc.TitleStyle.Padding.GetTop(DefaultTitleTop) + textHeight
|
||||||
|
|
||||||
|
r.Text(sbc.Title, titleX, titleY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) getDefaultCanvasBox() Box {
|
||||||
|
return sbc.Box()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) getAdjustedCanvasBox(canvasBox Box) Box {
|
||||||
|
var totalWidth int
|
||||||
|
for index, bar := range sbc.Bars {
|
||||||
|
totalWidth += bar.GetWidth()
|
||||||
|
if index < len(sbc.Bars)-1 {
|
||||||
|
totalWidth += sbc.GetBarSpacing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return canvasBox.OuterConstrain(sbc.Box(), Box{
|
||||||
|
Top: canvasBox.Top,
|
||||||
|
Left: canvasBox.Left,
|
||||||
|
Right: canvasBox.Left + totalWidth,
|
||||||
|
Bottom: canvasBox.Bottom,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Box returns the chart bounds as a box.
|
||||||
|
func (sbc StackedBarChart) Box() Box {
|
||||||
|
dpr := sbc.Background.Padding.GetRight(DefaultBackgroundPadding.Right)
|
||||||
|
dpb := sbc.Background.Padding.GetBottom(DefaultBackgroundPadding.Bottom)
|
||||||
|
|
||||||
|
return Box{
|
||||||
|
Top: sbc.Background.Padding.GetTop(DefaultBackgroundPadding.Top),
|
||||||
|
Left: sbc.Background.Padding.GetLeft(DefaultBackgroundPadding.Left),
|
||||||
|
Right: sbc.GetWidth() - dpr,
|
||||||
|
Bottom: sbc.GetHeight() - dpb,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) styleDefaultsStackedBarValue(index int) Style {
|
||||||
|
return Style{
|
||||||
|
StrokeColor: GetAlternateColor(index),
|
||||||
|
StrokeWidth: 3.0,
|
||||||
|
FillColor: GetAlternateColor(index),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sbc StackedBarChart) styleDefaultsElements() Style {
|
||||||
|
return Style{
|
||||||
|
Font: sbc.GetFont(),
|
||||||
|
}
|
||||||
|
}
|
2
util.go
2
util.go
|
@ -109,7 +109,7 @@ func Normalize(values ...float64) []float64 {
|
||||||
}
|
}
|
||||||
output := make([]float64, len(values))
|
output := make([]float64, len(values))
|
||||||
for x, v := range values {
|
for x, v := range values {
|
||||||
output[x] = RoundDown(v/total, 0.001)
|
output[x] = RoundDown(v/total, 0.00001)
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
2
value.go
2
value.go
|
@ -32,7 +32,7 @@ func (vs Values) Normalize() []Value {
|
||||||
output[index] = Value{
|
output[index] = Value{
|
||||||
Style: v.Style,
|
Style: v.Style,
|
||||||
Label: v.Label,
|
Label: v.Label,
|
||||||
Value: RoundDown(v.Value/total, 0.001),
|
Value: RoundDown(v.Value/total, 0.00001),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
|
|
Loading…
Reference in a new issue