{-# LANGUAGE ExistentialQuantification #-} {-| This module contains the Haskell data model for the Google Chart API. More details about the API and parameters can be found at : Some chart types are not supported yet: - Box Charts - Candlestick Charts - Compound Charts - Dynamic Icons - Map Charts Some parameters are not supported yet: - Text and Data Value Markers - Shape offset feature for shape markers - Bug in 'BarChartWidthSpacing'. Not fully accurate - Modfy FillType to conform to new API - Dynamic icon type - Geographic area - Vertical slice filling - Bar chart zero line -} module Graphics.GChart.Types ( -- * Typeclasses -- | Typeclasses for abstraction ChartM, ChartItem(set,encode), ChartDataEncodable(addEncodedChartData), -- * Chart -- | Data type to represent the chart Chart(..), -- * Chart Parameters {-| Some of these parameters behave differently depending on the chart type; More details : -} -- ** Bar Width and Spacing BarChartWidthSpacing(..), BarWidth(..), BarGroupSpacing(..), -- ** Series Colors ChartColors(..), Color, -- ** Chart Data ChartData(..), ChartDataScales(..), DataScale(..), -- ** Chart Legend Text and Style ChartLegend(..), LegendPosition(..), -- ** Solid, Gradient and Striped Fills ChartFills, Fill(..), FillKind(..), FillType(..), Offset, Width, Angle, -- ** Grid Lines ChartGrid(..), -- ** Pie chart labels, Google-o-meter label (TODO: QR code data, Formula TeX data) ChartLabels(..), -- ** Chart Label Data ChartLabelData(..), ErrorCorrectionLevel(..), -- ** Line Styles ChartLineStyles(..), LineStyle(..), -- ** Line, Shape, Range and Financial Markers AnyChartMarker(..), ChartMarker(..), ChartMarkers, LineWhichPoints(..),LineMarker(..), ShapeType(..), MarkerDataPoint(..), ShapeMarker(..), RangeMarkerType(..), RangeMarker(..), FinancialMarker(..), -- ** Line Fills LineFillType(..), LineFillMarker(..), -- ** Chart Margins ChartMargins(..), -- ** QR code output encoding QREncoding(..), -- ** Pie Chart Orientation PieChartOrientation(..), -- ** Chart Size ChartSize(..), -- ** Chart Type ChartType(..), -- ** Chart Title and Style ChartTitle(..), -- ** Visible Axis Axis styles and labels ChartAxes, Axis(..), AxisType(..), AxisLabel, AxisPosition, FontSize, AxisRange(..), AxisStyle(..), DrawingControl(..), AxisStyleAlignment(..), -- * Default Values {-| These functions return default values for complex parameters, which can be used as starting points to construct parameters when creating charts. -} defaultChart, defaultAxis, defaultGrid, defaultSpacing, defaultShapeMarker, defaultRangeMarker, defaultFinancialMarker, defaultLineStyle, defaultLineMarker, defaultQREncodingLabelData ) where import Control.Monad.State -- | Size of the chart. width and height specified in pixels data ChartSize = Size Int Int deriving Show -- | Chart type data ChartType = Line -- ^ Line Chart | Sparklines -- ^ Sparklines | LineXY -- ^ Line Chart w/ XY co-ordinates | BarHorizontalStacked -- ^ Horizontal bar chart w/ stacked bars | BarVerticalStacked -- ^ Vertical bar chart w/ stacked bars | BarHorizontalGrouped -- ^ Horizontal bar chart w/ grouped bars | BarVerticalGrouped -- ^ Vertical bar chart w/ grouped bars | Pie -- ^ Two dimensional pie chart | Pie3D -- ^ Three dimensional pie chart | PieConcentric -- ^ Concentric pie chart | Venn -- ^ Venn Diagram | ScatterPlot -- ^ Scatter Plot | Radar -- ^ Radar Chart | RadarCurvedLines -- ^ Radar Chart, connects points with curved lines | GoogleOMeter -- ^ Google-o-meter | Formula -- ^ Formula Chart | QRCode -- ^ QR Codes deriving Show -- | Title of the chart data ChartTitle = ChartTitle { titleStr ::String -- ^ Title , titleColor :: Maybe Color -- ^ Title Color , titleFontSize :: Maybe FontSize -- ^ Title Font Size } deriving Show -- | Data scaling expressed as (@series_min@,@series_max@). Applies to text encoding only type DataScale = (Float,Float) -- | List of Data scaling values data ChartDataScales = CDS [DataScale] deriving Show -- | Chart data along with encoding. XY data for is encoded a pair of -- consecutive data sets data ChartData = Simple [[Int]] -- ^ lets you specify integer values from 0-61, inclusive | Text [[Float]] -- ^ supports floating point numbers from 0-100, inclusive | Extended [[Int]] -- ^ lets you specify integer values from 0-4095, inclusive deriving Show -- | Color data specified as a hex string type Color = String -- | Chart colors specified as a list of 'Color' values for each data point. data ChartColors = ChartColors [Color] deriving Show -- | Angle of the gradient between 0 (horizontal) and 90 -- (vertical). Applicable to 'LinearGradient' and 'LinearStripes' type Angle = Float -- | Specifies at what point the color is pure. In this parameter, 0 specifies -- the right-most chart position and 1 specifies the left-most chart -- position. Applicable to 'LinearGradient' type Offset = Float -- | Width of the stripe. must be between 0 and 1, where 1 is the full width of -- the chart type Width = Float -- | Specifies the kind of fill data FillKind = Solid Color -- ^ Solid Fill | LinearGradient Angle [(Color,Offset)] -- ^ Linear Gradient | LinearStripes Angle [(Color,Width)] -- ^ Linear Stripes deriving Show -- | Specifies the type of fill data FillType = Background -- ^ Background fill | Area -- ^ Chart area fill | Transparent -- ^ Apply transparency to whole chart (applicable to 'Solid' fill only) deriving Show -- | Constructor for a chart fill data Fill = Fill FillKind FillType deriving Show -- | Chart fills, as a list of 'Fill's type ChartFills = [Fill] -- | Position of legend on chart. Applies to 'ChartLegend' data LegendPosition = LegendBottom -- ^ Bottom of chart, horizontally | LegendTop -- ^ Top of chart, horizontally | LegendVBottom -- ^ Bottom of chart, vertically | LegendVTop -- ^ Bottom of chart, vertically | LegendRight -- ^ Left of chart | LegendLeft -- ^ Right of chart deriving Show -- | Chart legend data ChartLegend = Legend [String] (Maybe LegendPosition) deriving Show -- | Type of 'Axis' data AxisType = AxisBottom -- ^ Bottom x-axis | AxisTop -- ^ Top x-axis | AxisLeft -- ^ Left y-axis | AxisRight -- ^ Right y-axis deriving Show -- | 'Axis' Labels. type AxisLabel = String {-| 'Axis' Label Positions. Labels with a specified position of 0 are placed at the bottom of the y- or r-axis, or at the left of the x- or t-axis. Labels with a specified position of 100 are placed at the top of the y- or r-axis, or at the right of the x- or t-axis. -} type AxisPosition = Float {-| 'Axis' Range The range is specifies with a tuple containing the start and end values. An optional interval value can be specified. -} data AxisRange = Range (Float,Float) (Maybe Float) deriving (Show,Eq) -- | Font size in pixels. Applicable to 'AxisStyle' and 'ChartTitle' type FontSize = Int -- | Alignment of 'Axis' labels. Applies to 'AxisStyle' data AxisStyleAlignment = AxisStyleLeft -- ^ Left aligned labels | AxisStyleCenter -- ^ Centered labels | AxisStyleRight -- ^ Right aligned labels deriving (Show,Eq) -- | Control drawing of 'Axis'. Applicable to 'AxisStyle' data DrawingControl = DrawLines -- ^ Draw axis lines only | DrawTicks -- ^ Draw tick marks only | DrawLinesTicks -- ^ Draw axis lines and tick marks deriving (Show,Eq) -- | 'Axis' style data AxisStyle = Style { axisColor :: Color, axisFontSize :: Maybe FontSize, axisStyleAlign :: Maybe AxisStyleAlignment, axisDrawingControl :: Maybe DrawingControl, tickMarkColor :: Maybe Color } deriving (Show,Eq) -- | Visible axis data Axis = Axis { axisType :: AxisType, axisLabels :: Maybe [AxisLabel], axisPositions :: Maybe [AxisPosition], axisRange :: Maybe AxisRange, axisStyle :: Maybe AxisStyle } deriving Show -- | List of 'Axis' for chart type ChartAxes = [Axis] -- | Grid Lines for Chart data ChartGrid = ChartGrid { xAxisStep :: Float -- ^ x-axis step size (0-100) , yAxisStep :: Float -- ^ y-axis step size (0-100) , lineSegmentLength :: Maybe Float -- ^ length of line segment , blankSegmentLength :: Maybe Float -- ^ length of blank segment , xOffset :: Maybe Float -- ^ x axis offset , yOffset :: Maybe Float -- ^ y axis offset } deriving Show -- | Shape type of 'ShapeMarker' data ShapeType = ShapeArrow -- ^ Arrow | ShapeCross -- ^ Cross | ShapeRectangle -- ^ Rectangle | ShapeDiamond -- ^ Diamond | ShapeErrorBarMarker -- ^ Error Bar Marker | HorizontalLine -- ^ Horizontal line across the chart at specified height | HorizontalLineFull -- ^ Horizontal line through the specified data marker | ShapeCircle -- ^ Circle | ShapeSquare -- ^ Square | VerticalLine -- ^ Vertical line from x-axis to data point | VerticalLineFull -- ^ Vertical line across the chart | ShapeX -- ^ X shape deriving Show -- | Data point value of `ShapeMarker` data MarkerDataPoint = DataPoint Float -- ^ A specific data point in the dataset. Use a -- decimal value to interpolate between two points | DataPointEvery -- ^ Draw a marker on each data point | DataPointEveryN Int -- ^ Draw a marker on every n-th data point | DataPointEveryNRange (Int,Int) Int -- ^ @(x,y), n@ draw a marker on every n-th -- data point in a range, where x is the -- first data point in the range, and y is -- the last data point in the range | DataPointXY (Float,Float) -- ^ draw a marker at a specific point -- (x,y). Specify the coordinates as floating -- point values, where 0:0 is the bottom left -- corner of the chart, 0.5:0.5 is the center of -- the chart, and 1:1 is the top right corner of -- the chart deriving Show -- | Shape Marker data ShapeMarker = SM { shapeType :: ShapeType -- ^ Shape type , shapeColor :: Color -- ^ Shape Marker color , shapeDataSetIdx :: Int -- ^ Data Set Index , shapeDataPoints :: MarkerDataPoint -- ^ Data point value , shapeSize :: Int -- ^ Size in pixels , shapeWidth :: Maybe Int -- ^ Optional width used for certain shapes , shapeZorder :: Float -- ^ The layer on which to draw the -- marker. This is a floating point -- number from -1.0 to 1.0, -- inclusive, where -1.0 is the -- bottom and 1.0 is the top -- TODO Add shape offset feature } deriving Show -- | 'RangeMarker' type data RangeMarkerType = RangeMarkerHorizontal -- ^ horizontal range | RangeMarkerVertical -- ^ vertical range deriving Show -- | Range Marker data RangeMarker = RM { rangeMarkerType :: RangeMarkerType -- ^ Range marker type , rangeMarkerColor :: Color -- ^ Range marker color , rangeMarkerRange :: (Float, Float) -- ^ @(start,end) range. @For -- horizontal range markers, the -- (start,end) value is a position on -- the y-axis, where 0.00 is the -- bottom of the chart, and 1.00 is -- the top of the chart. For vertical -- range markers, the (start,end) -- value is a position on the x-axis, -- where 0.00 is the left of the -- chart, and 1.00 is the right of the -- chart. } deriving Show -- | Financial Marker, for line charts and vertical bar charts data FinancialMarker = FM { financeColor :: Color -- ^ Finance Marker color , financeDataSetIdx :: Int -- ^ Data Set Index , financeDataPoint :: MarkerDataPoint -- ^ Data point value , financeSize :: Int -- ^ Size in pixels , financePriority :: Int -- ^ Priority of drawing. Can be one of -1,0,1 } deriving Show -- | Which points in a series to use to draw the line. data LineWhichPoints = PointsAll -- ^ Use all the points in the series. | Points (Maybe Float, Maybe Float) -- ^ (start,end) indicating a specific range of points deriving Show -- | Line Marker data LineMarker = LM { lineColor :: Color -- ^ Line Marker Color , lineDataSetIdx :: Int -- ^ Data set index , lineWhichPoints :: LineWhichPoints -- ^ Which points to draw the line markers on , lineSize :: Int -- ^ Width of line in pixels , lineZorder :: Float -- ^ Floating point between -1 and 1 indicating -- the layer on which to draw. } deriving Show -- | Line fill type for 'LineFill' data LineFillType = LineFillFrom Int -- ^ Line fill starting from a start index | LineFillBetween Int Int -- ^ Line fill between a start index and end index deriving Show -- | Line Fill Marker data LineFillMarker = LineFillMarker LineFillType Color deriving Show -- | Typeclass to abstract over different chart markers class Show a => ChartMarker a where encodeChartMarker :: a -> String encodeChartMarker a = "" -- | Data type to abstract over all kinds of ChartMarker data AnyChartMarker = forall w. ChartMarker w => AnyChartMarker w instance ChartMarker AnyChartMarker where encodeChartMarker (AnyChartMarker m) = encodeChartMarker m instance Show AnyChartMarker where show (AnyChartMarker m) = show m type ChartMarkers = [AnyChartMarker] -- | Labels for Pie Chart and Google-o-meter. -- Specify a list with a single label for Google-o-meter data ChartLabels = ChartLabels [String] deriving Show -- | Pie Chart Orientation. Applicable only to Pie Charts, data PieChartOrientation = PCO Float deriving Show -- | Chart Margins. All margin values specified are the minimum margins around -- the plot area, in pixels. data ChartMargins = ChartMargins { leftMargin :: Int -- ^ Left margin around plot area , rightMargin :: Int -- ^ Right margin around plot area , topMargin :: Int -- ^ Top margin around plot area , bottomMargin :: Int -- ^ Bottom margin around plot area , legendMargins :: Maybe (Int,Int) -- ^ Minimum width and height of legend } deriving Show -- | Bar Width data BarWidth = Automatic -- ^ Automatic resizing | BarWidth Int -- ^ Bar width in pixels deriving Show -- | Bar and Group Spacing data BarGroupSpacing = Fixed (Int, Int) -- ^ Fixed spacing values in pixels | Relative (Float,Float) -- ^ Relative values as percentages deriving Show -- | Bar Width and Spacing. type BarChartWidthSpacing = (Maybe BarWidth, Maybe BarGroupSpacing) -- | Line Style. Applicable for line charts data LineStyle = LS { lineStyleThickness :: Float -- ^ Thickness , lineStyleLineSegment :: Float -- ^ Length of Line Segment , lineStyleBlankSegment :: Float -- ^ Length of Blank Segment } deriving Show type ChartLineStyles = [LineStyle] -- | QR Code Output Encoding data QREncoding = UTF8 | Shift_JIS | ISO8859_1 deriving Show -- | Error Correction Level for QR Code data ErrorCorrectionLevel = L' -- ^ recovery of up to 7% data loss | M' -- ^ recovery of up to 15% data loss | Q' -- ^ recovery of up to 25% data loss | H' -- ^ recovery of up to 30% data loss instance Show ErrorCorrectionLevel where show L' = "L" show M' = "M" show Q' = "Q" show H' = "H" -- | Chart Label Data. Applies to 'QRCode' data ChartLabelData = QRLabelData ErrorCorrectionLevel Int -- ^ Error Correction Level and Margin (as no. of rows) deriving Show -- | Data type for the chart data Chart = Chart { chartSize :: Maybe ChartSize , chartType :: ChartType , chartData :: Maybe ChartData , chartDataScales :: Maybe ChartDataScales , chartTitle :: Maybe ChartTitle , chartColors :: Maybe ChartColors , chartFills :: Maybe ChartFills , chartLegend :: Maybe ChartLegend , chartAxes :: Maybe ChartAxes , chartMarkers :: Maybe ChartMarkers , chartGrid :: Maybe ChartGrid , chartLabels :: Maybe ChartLabels , chartMargins :: Maybe ChartMargins , barChartWidthSpacing :: Maybe BarChartWidthSpacing , pieChartOrientation :: Maybe PieChartOrientation , chartLineStyles :: Maybe ChartLineStyles , qrEncoding :: Maybe QREncoding , chartLabelData :: Maybe ChartLabelData } deriving Show -- | Chart monad which wraps a 'State' monad in turn -- to keep track of the chart state and make it convenient -- to update it type ChartM a = State Chart a -- | Typeclass abstracting all the fields in a chart class ChartItem c where -- | sets the item in a chart set :: c -> ChartM () -- | encode the field into a list string params that can -- then be converted into a query string URL encode :: c -> [(String,String)] -- | Typeclass abstracting the numeric data that can be encoded. -- This helps in passing Int and Float values as chart data, which -- are then encoded correctly class Num a => ChartDataEncodable a where -- | Adds the array of numeric data to the existing chart data. -- Throws a error if the data passed in doesnt match with the -- current data encoding format. addEncodedChartData :: [a] -> ChartData -> ChartData -- | Default value for a chart defaultChart = Chart { chartSize = Nothing, chartType = Line, chartData = Nothing, chartDataScales = Nothing, chartTitle = Nothing, chartColors = Nothing, chartFills = Nothing, chartLegend = Nothing, chartAxes = Nothing, chartGrid = Nothing, chartLabels = Nothing, chartMargins = Nothing, chartMarkers = Nothing, barChartWidthSpacing = Nothing, pieChartOrientation = Nothing, chartLineStyles = Nothing, qrEncoding = Nothing, chartLabelData = Nothing } -- | Default value for an axis defaultAxis = Axis { axisType = AxisBottom, axisLabels = Nothing, axisPositions = Nothing, axisRange = Nothing, axisStyle = Nothing } -- | Default value for an axis style defaultAxisStyle = Style { axisColor = "0000DD", axisFontSize = Nothing, axisStyleAlign = Nothing, axisDrawingControl = Nothing, tickMarkColor = Nothing } -- | Default value for a chart grid defaultGrid = ChartGrid { xAxisStep = 20, yAxisStep = 20, lineSegmentLength = Nothing, blankSegmentLength = Nothing, xOffset = Nothing, yOffset = Nothing } -- | Default value for bar and group spacing in bar chart defaultSpacing = Fixed (4,8) -- | Default value of a shape marker. Make sure you change the value of @shapeDataSetIdx@ defaultShapeMarker = SM { shapeType = ShapeCircle, shapeColor = "0000DD", shapeDataSetIdx = -1, shapeDataPoints = DataPointEvery, shapeSize = 5, shapeWidth = Nothing, shapeZorder = 0 } -- | Default value of range marker defaultRangeMarker = RM { rangeMarkerType = RangeMarkerHorizontal, rangeMarkerColor = "0000DD", rangeMarkerRange = (0.0,1.0) } -- | Default value of a financial marker. Make sure you change the value of @financeDataSetIdx@ defaultFinancialMarker = FM { financeColor = "0000DD", financeDataSetIdx = -1, financeDataPoint = DataPointEvery, financeSize = 5, financePriority = 0 } -- | Default value of a line marker. Make sure you change the value of @lineDataSetIdx@ defaultLineMarker = LM { lineColor = "0000DD" , lineDataSetIdx = -1 , lineWhichPoints = PointsAll , lineSize = 5 , lineZorder = 0.0 } -- | Default value of a line style defaultLineStyle = LS { lineStyleThickness = 1, lineStyleLineSegment = 1, lineStyleBlankSegment = 0 } -- | Default chart label data for QR Encoding defaultQREncodingLabelData = QRLabelData L' 4