From 3607d732d97d0fa38ab6ac297be82c939e098ee0 Mon Sep 17 00:00:00 2001 From: Will Charczuk Date: Sat, 6 Aug 2016 15:53:14 -0700 Subject: [PATCH] last annotation helper --- examples/min_max/main.go | 61 ++++++++++++++++ last_value_annotation_series.go | 37 ++++++++++ min_max_series.go | 119 ++++++++++++++++++++++++++++++++ 3 files changed, 217 insertions(+) create mode 100644 examples/min_max/main.go create mode 100644 last_value_annotation_series.go create mode 100644 min_max_series.go diff --git a/examples/min_max/main.go b/examples/min_max/main.go new file mode 100644 index 0000000..056b1fd --- /dev/null +++ b/examples/min_max/main.go @@ -0,0 +1,61 @@ +package main + +import ( + "net/http" + + "github.com/wcharczuk/go-chart" +) + +func drawChart(res http.ResponseWriter, req *http.Request) { + mainSeries := chart.ContinuousSeries{ + Name: "A test series", + XValues: chart.Sequence.Float64(1.0, 100.0), + YValues: chart.Sequence.RandomWithAverage(100, 100, 50), + } + + minSeries := &chart.MinSeries{ + Style: chart.Style{ + Show: true, + StrokeColor: chart.ColorAlternateGray, + StrokeDashArray: []float64{5.0, 5.0}, + }, + InnerSeries: mainSeries, + } + + maxSeries := &chart.MaxSeries{ + Style: chart.Style{ + Show: true, + StrokeColor: chart.ColorAlternateGray, + StrokeDashArray: []float64{5.0, 5.0}, + }, + InnerSeries: mainSeries, + } + + graph := chart.Chart{ + YAxis: chart.YAxis{ + Style: chart.StyleShow(), + Range: &chart.ContinuousRange{ + Min: 25, + Max: 175, + }, + }, + XAxis: chart.XAxis{ + Style: chart.StyleShow(), + }, + Series: []chart.Series{ + mainSeries, + minSeries, + maxSeries, + chart.LastValueAnnotation(minSeries), + chart.LastValueAnnotation(maxSeries), + }, + } + + res.Header().Set("Content-Type", "image/png") + graph.Render(chart.PNG, res) +} + +func main() { + http.HandleFunc("/", drawChart) + http.ListenAndServe(":8080", nil) +} diff --git a/last_value_annotation_series.go b/last_value_annotation_series.go new file mode 100644 index 0000000..9006378 --- /dev/null +++ b/last_value_annotation_series.go @@ -0,0 +1,37 @@ +package chart + +import "fmt" + +// LastValueAnnotation returns an annotation series of just the last value of a value provider. +func LastValueAnnotation(innerSeries ValueProvider, vfs ...ValueFormatter) AnnotationSeries { + var vf ValueFormatter + if len(vfs) > 0 { + vf = vfs[0] + } else if typed, isTyped := innerSeries.(ValueFormatterProvider); isTyped { + _, vf = typed.GetValueFormatters() + } else { + vf = FloatValueFormatter + } + + var lastValue Value2 + if typed, isTyped := innerSeries.(LastValueProvider); isTyped { + lastValue.XValue, lastValue.YValue = typed.GetLastValue() + lastValue.Label = vf(lastValue.YValue) + } else { + lastValue.XValue, lastValue.YValue = innerSeries.GetValue(innerSeries.Len() - 1) + lastValue.Label = vf(lastValue.YValue) + } + + var seriesName string + var seriesStyle Style + if typed, isTyped := innerSeries.(Series); isTyped { + seriesName = fmt.Sprintf("%s - Last Value", typed.GetName()) + seriesStyle = typed.GetStyle() + } + + return AnnotationSeries{ + Name: seriesName, + Style: seriesStyle, + Annotations: []Value2{lastValue}, + } +} diff --git a/min_max_series.go b/min_max_series.go new file mode 100644 index 0000000..4900c6d --- /dev/null +++ b/min_max_series.go @@ -0,0 +1,119 @@ +package chart + +import "math" + +// MinSeries draws a horizontal line at the minimum value of the inner series. +type MinSeries struct { + Name string + Style Style + YAxis YAxisType + InnerSeries ValueProvider + + minValue *float64 +} + +// GetName returns the name of the time series. +func (ms MinSeries) GetName() string { + return ms.Name +} + +// GetStyle returns the line style. +func (ms MinSeries) GetStyle() Style { + return ms.Style +} + +// GetYAxis returns which YAxis the series draws on. +func (ms MinSeries) GetYAxis() YAxisType { + return ms.YAxis +} + +// Len returns the number of elements in the series. +func (ms MinSeries) Len() int { + return ms.InnerSeries.Len() +} + +// GetValue gets a value at a given index. +func (ms *MinSeries) GetValue(index int) (x, y float64) { + ms.ensureMinValue() + x, _ = ms.InnerSeries.GetValue(index) + y = *ms.minValue + return +} + +// Render renders the series. +func (ms *MinSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { + style := ms.Style.InheritFrom(defaults) + Draw.LineSeries(r, canvasBox, xrange, yrange, style, ms) +} + +func (ms *MinSeries) ensureMinValue() { + if ms.minValue == nil { + minValue := math.MaxFloat64 + var y float64 + for x := 0; x < ms.InnerSeries.Len(); x++ { + _, y = ms.InnerSeries.GetValue(x) + if y < minValue { + minValue = y + } + } + ms.minValue = &minValue + } +} + +// MaxSeries draws a horizontal line at the maximum value of the inner series. +type MaxSeries struct { + Name string + Style Style + YAxis YAxisType + InnerSeries ValueProvider + + maxValue *float64 +} + +// GetName returns the name of the time series. +func (ms MaxSeries) GetName() string { + return ms.Name +} + +// GetStyle returns the line style. +func (ms MaxSeries) GetStyle() Style { + return ms.Style +} + +// GetYAxis returns which YAxis the series draws on. +func (ms MaxSeries) GetYAxis() YAxisType { + return ms.YAxis +} + +// Len returns the number of elements in the series. +func (ms MaxSeries) Len() int { + return ms.InnerSeries.Len() +} + +// GetValue gets a value at a given index. +func (ms *MaxSeries) GetValue(index int) (x, y float64) { + ms.ensureMaxValue() + x, _ = ms.InnerSeries.GetValue(index) + y = *ms.maxValue + return +} + +// Render renders the series. +func (ms *MaxSeries) Render(r Renderer, canvasBox Box, xrange, yrange Range, defaults Style) { + style := ms.Style.InheritFrom(defaults) + Draw.LineSeries(r, canvasBox, xrange, yrange, style, ms) +} + +func (ms *MaxSeries) ensureMaxValue() { + if ms.maxValue == nil { + maxValue := -math.MaxFloat64 + var y float64 + for x := 0; x < ms.InnerSeries.Len(); x++ { + _, y = ms.InnerSeries.GetValue(x) + if y > maxValue { + maxValue = y + } + } + ms.maxValue = &maxValue + } +}