[reportlab-users] Area chart with values above/below 0?

O'brien, Jim jim.obrien at zebra.com
Tue Feb 24 09:54:59 EST 2015


>If this works for you let me know and I'll check this in.

Appears to work well.

Thanks

Jim


----------------------------------------------------------------------

Message: 1
Date: Tue, 24 Feb 2015 12:53:02 +0000
From: Robin Becker <robin at reportlab.com>
To: reportlab-users <reportlab-users at lists2.reportlab.com>
Subject: Re: [reportlab-users] Area chart with values above/below 0?
Message-ID: <54EC742E.9050505 at chamonix.reportlab.co.uk>
Content-Type: text/plain; charset="utf-8"; Format="flowed"

On 24/02/2015 11:25, O'brien, Jim wrote:
> Is it possible to build an area chart with inFill = 1 where the chart values (2 series) are above and below zero?
>

Currently the inFill is between the plot value and the x - axis, but logically
it should be possible to use a different Y value.

I did a hack to the lineplots.py file to allow the _inFill value of AreaLinePlot
to have an additional value to specify the user yValue where things should be
filled to.

> C:\code\hg-repos\reportlab>hg diff
> diff -r 5214fe281f6d src/reportlab/graphics/charts/lineplots.py
> --- a/src/reportlab/graphics/charts/lineplots.py        Mon Feb 09 11:04:24 2015 +0000
> +++ b/src/reportlab/graphics/charts/lineplots.py        Tue Feb 24 12:49:26 2015 +0000
> @@ -33,6 +33,12 @@
>          inFill = AttrMapValue(isBoolean, desc='If true flood fill to x axis',advancedUsage=1),
>          )
>
> +class InFillValue(int):
> +    def __new__(cls,v,yValue=None):
> +        self = int.__new__(cls,v)
> +        self.yValue = yValue
> +        return self
> +
>  class Shader(_SetKeyWordArgs):
>      _attrMap = AttrMap(BASE=PlotArea,
>          vertical = AttrMapValue(isBoolean, desc='If true shade to x axis'),
> @@ -240,10 +246,10 @@
>
>      def makeLines(self):
>          g = Group()
> +        yA = self.yValueAxis
> +        xA = self.xValueAxis
>          bubblePlot = getattr(self,'_bubblePlot',None)
>          if bubblePlot:
> -            yA = self.yValueAxis
> -            xA = self.xValueAxis
>              bubbleR = min(yA._bubbleRadius,xA._bubbleRadius)
>              bubbleMax = xA._bubbleMax
>
> @@ -254,9 +260,13 @@
>          inFill = getattr(self,'_inFill',None)
>          styleCount = len(self.lines)
>          if inFill or [rowNo for rowNo in P if getattr(self.lines[rowNo%styleCount],'inFill',False)]:
> -            inFillY = self.xValueAxis._y
> -            inFillX0 = self.yValueAxis._x
> -            inFillX1 = inFillX0 + self.xValueAxis._length
> +            inFillY = getattr(inFill,'yValue',None)
> +            if inFillY is None:
> +                inFillY = xA._y
> +            else:
> +                inFillY = yA.scale(inFillY)
> +            inFillX0 = yA._x
> +            inFillX1 = inFillX0 + xA._length
>              inFillG = getattr(self,'_inFillG',g)
>          lG = getattr(self,'_lineG',g)
>          # Iterate over data rows.



then this drawing works

> from reportlab.graphics.shapes import Drawing
> from reportlab.graphics.charts.lineplots import AreaLinePlot, InFillValue
>
> class MyAreaPlot(Drawing):
>       def __init__(self,width=400,height=200,*args,**kw):
>               Drawing.__init__(self,width,height,*args,**kw)
>               self.add(AreaLinePlot(),name='chart')
>               self.chart.y                = 20
>               self.chart._inFill          = InFillValue(1,0)
>               self.chart.data             = [(1, -20, 100), (2, -11, 50), (3, -15, 70)]
>
> if __name__=="__main__":
>       MyAreaPlot().save(formats=['pdf'],outDir='.',fnRoot=None)


If this works for you let me know and I'll check this in.
--
Robin Becker
>
> See attached example.  I've come up with a line chart that plots 2 lines, one above and one below zero (on the yAxis) but can't quit figure out how to get inFill to work since it seems to be tied to the xAxis position.
>
>
> Goal is something like this:
>
> [cid:2bc56807-842e-4d77-890a-287e3413bd55]
>
>
> Thanks
>
>
> Jim
>
.......


-------------- next part --------------
#Copyright ReportLab Europe Ltd. 2000-2012
#see license.txt for license details
#history http://www.reportlab.co.uk/cgi-bin/viewcvs.cgi/public/reportlab/trunk/reportlab/graphics/charts/lineplots.py

__version__=''' $Id$ '''
__doc__="""This module defines a very preliminary Line Plot example."""

import string, time

from reportlab.lib import colors
from reportlab.lib.validators import *
from reportlab.lib.attrmap import *
from reportlab.graphics.shapes import Drawing, Group, Rect, Line, PolyLine, Polygon, _SetKeyWordArgs
from reportlab.graphics.widgetbase import Widget, TypedPropertyCollection, PropHolder
from reportlab.graphics.charts.textlabels import Label
from reportlab.graphics.charts.axes import XValueAxis, YValueAxis, AdjYValueAxis, NormalDateXValueAxis
from reportlab.graphics.charts.utils import *
from reportlab.graphics.widgets.markers import uSymbol2Symbol, isSymbol, makeMarker
from reportlab.graphics.widgets.grids import Grid, DoubleGrid, ShadedRect, ShadedPolygon
from reportlab.pdfbase.pdfmetrics import stringWidth, getFont
from reportlab.graphics.charts.areas import PlotArea

# This might be moved again from here...
class LinePlotProperties(PropHolder):
    _attrMap = AttrMap(
        strokeWidth = AttrMapValue(isNumber, desc='Width of a line.'),
        strokeColor = AttrMapValue(isColorOrNone, desc='Color of a line.'),
        strokeDashArray = AttrMapValue(isListOfNumbersOrNone, desc='Dash array of a line.'),
        symbol = AttrMapValue(None, desc='Widget placed at data points.',advancedUsage=1),
        shader = AttrMapValue(None, desc='Shader Class.',advancedUsage=1),
        filler = AttrMapValue(None, desc='Filler Class.',advancedUsage=1),
        name = AttrMapValue(isStringOrNone, desc='Name of the line.'),
        inFill = AttrMapValue(isBoolean, desc='If true flood fill to x axis',advancedUsage=1),
        )

class InFillValue(int):
    def __new__(cls,v,yValue=None):
        self = int.__new__(cls,v)
        self.yValue = yValue
        return self

class Shader(_SetKeyWordArgs):
    _attrMap = AttrMap(BASE=PlotArea,
        vertical = AttrMapValue(isBoolean, desc='If true shade to x axis'),
        colors = AttrMapValue(SequenceOf(isColorOrNone,lo=2,hi=2), desc='(AxisColor, LineColor)'),
        )

    def shade(self, lp, g, rowNo, rowColor, row):
        c = [None,None]
        c = getattr(self,'colors',c) or c
        if not c[0]: c[0] = getattr(lp,'fillColor',colors.white)
        if not c[1]: c[1] = rowColor

class NoFiller:
    def fill(self, lp, g, rowNo, rowColor, points):
        pass

class Filler:
    '''mixin providing simple polygon fill'''
    _attrMap = AttrMap(
        fillColor = AttrMapValue(isColorOrNone, desc='filler interior color'),
        strokeColor = AttrMapValue(isColorOrNone, desc='filler edge color'),
        strokeWidth = AttrMapValue(isNumberOrNone, desc='filler edge width'),
        )
    def __init__(self,**kw):
        self.__dict__ = kw

    def fill(self, lp, g, rowNo, rowColor, points):
        g.add(Polygon(points,
            fillColor=getattr(self,'fillColor',rowColor),
            strokeColor=getattr(self,'strokeColor',rowColor),
            strokeWidth=getattr(self,'strokeWidth',0.1)))

class ShadedPolyFiller(Filler,ShadedPolygon):
    pass

class PolyFiller(Filler,Polygon):
    pass

from reportlab.graphics.charts.linecharts import AbstractLineChart
class LinePlot(AbstractLineChart):
    """Line plot with multiple lines.

    Both x- and y-axis are value axis (so there are no seperate
    X and Y versions of this class).
    """
    _attrMap = AttrMap(BASE=PlotArea,
        reversePlotOrder = AttrMapValue(isBoolean, desc='If true reverse plot order.',advancedUsage=1),
        lineLabelNudge = AttrMapValue(isNumber, desc='Distance between a data point and its label.',advancedUsage=1),
        lineLabels = AttrMapValue(None, desc='Handle to the list of data point labels.'),
        lineLabelFormat = AttrMapValue(None, desc='Formatting string or function used for data point labels.'),
        lineLabelArray = AttrMapValue(None, desc='explicit array of line label values, must match size of data if present.'),
        joinedLines = AttrMapValue(isNumber, desc='Display data points joined with lines if true.'),
        strokeColor = AttrMapValue(isColorOrNone, desc='Color used for background border of plot area.'),
        fillColor = AttrMapValue(isColorOrNone, desc='Color used for background interior of plot area.'),
        lines = AttrMapValue(None, desc='Handle of the lines.'),
        xValueAxis = AttrMapValue(None, desc='Handle of the x axis.'),
        yValueAxis = AttrMapValue(None, desc='Handle of the y axis.'),
        data = AttrMapValue(None, desc='Data to be plotted, list of (lists of) x/y tuples.'),
        annotations = AttrMapValue(None, desc='list of callables, will be called with self, xscale, yscale.',advancedUsage=1),
        behindAxes = AttrMapValue(isBoolean, desc='If true use separate line group.',advancedUsage=1),
        gridFirst = AttrMapValue(isBoolean, desc='If true use draw grids before axes.',advancedUsage=1),
        )

    def __init__(self):
        PlotArea.__init__(self)
        self.reversePlotOrder = 0

        self.xValueAxis = XValueAxis()
        self.yValueAxis = YValueAxis()

        # this defines two series of 3 points.  Just an example.
        self.data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3,4), (4,6))
            ]

        self.lines = TypedPropertyCollection(LinePlotProperties)
        self.lines.strokeWidth = 1
        self.lines[0].strokeColor = colors.red
        self.lines[1].strokeColor = colors.blue

        self.lineLabels = TypedPropertyCollection(Label)
        self.lineLabelFormat = None
        self.lineLabelArray = None

        # this says whether the origin is inside or outside
        # the bar - +10 means put the origin ten points
        # above the tip of the bar if value > 0, or ten
        # points inside if bar value < 0.  This is different
        # to label dx/dy which are not dependent on the
        # sign of the data.
        self.lineLabelNudge = 10
        # if you have multiple series, by default they butt
        # together.

        # New line chart attributes.
        self.joinedLines = 1 # Connect items with straight lines.

        #private attributes
        self._inFill = None
        self.annotations = []
        self.behindAxes = 0
        self.gridFirst = 0

    def demo(self):
        """Shows basic use of a line chart."""

        drawing = Drawing(400, 200)

        data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3.5,5), (4,6))
            ]

        lp = LinePlot()

        lp.x = 50
        lp.y = 50
        lp.height = 125
        lp.width = 300
        lp.data = data
        lp.joinedLines = 1
        lp.lineLabelFormat = '%2.0f'
        lp.strokeColor = colors.black

        lp.lines[0].strokeColor = colors.red
        lp.lines[0].symbol = makeMarker('FilledCircle')
        lp.lines[1].strokeColor = colors.blue
        lp.lines[1].symbol = makeMarker('FilledDiamond')

        lp.xValueAxis.valueMin = 0
        lp.xValueAxis.valueMax = 5
        lp.xValueAxis.valueStep = 1

        lp.yValueAxis.valueMin = 0
        lp.yValueAxis.valueMax = 7
        lp.yValueAxis.valueStep = 1

        drawing.add(lp)

        return drawing


    def calcPositions(self):
        """Works out where they go.

        Sets an attribute _positions which is a list of
        lists of (x, y) matching the data.
        """

        self._seriesCount = len(self.data)
        self._rowLength = max(list(map(len,self.data)))

        self._positions = []
        for rowNo in range(len(self.data)):
            line = []
            for colNo in range(len(self.data[rowNo])):
                datum = self.data[rowNo][colNo] # x,y value
                if isinstance(datum[0],str):
                    x = self.xValueAxis.scale(mktime(mkTimeTuple(datum[0])))
                else:
                    x = self.xValueAxis.scale(datum[0])
                y = self.yValueAxis.scale(datum[1])
                line.append((x, y))
            self._positions.append(line)

    def _innerDrawLabel(self, rowNo, colNo, x, y):
        "Draw a label for a given item in the list."

        labelFmt = self.lineLabelFormat
        labelValue = self.data[rowNo][colNo][1] ###

        if labelFmt is None:
            labelText = None
        elif isinstance(labelFmt,str):
            if labelFmt == 'values':
                labelText = self.lineLabelArray[rowNo][colNo]
            else:
                labelText = labelFmt % labelValue
        elif hasattr(labelFmt,'__call__'):
            if not hasattr(labelFmt,'__labelFmtEX__'):
                labelText = labelFmt(labelValue)
            else:
                labelText = labelFmt(self,rowNo,colNo,x,y)
        else:
            raise ValueError("Unknown formatter type %s, expected string or function"% labelFmt)

        if labelText:
            label = self.lineLabels[(rowNo, colNo)]
            if not label.visible: return
            #hack to make sure labels are outside the bar
            if y > 0:
                label.setOrigin(x, y + self.lineLabelNudge)
            else:
                label.setOrigin(x, y - self.lineLabelNudge)
            label.setText(labelText)
        else:
            label = None
        return label

    def drawLabel(self, G, rowNo, colNo, x, y):
        '''Draw a label for a given item in the list.
        G must have an add method'''
        G.add(self._innerDrawLabel(rowNo,colNo,x,y))

    def makeLines(self):
        g = Group()
        yA = self.yValueAxis
        xA = self.xValueAxis
        bubblePlot = getattr(self,'_bubblePlot',None)
        if bubblePlot:
            bubbleR = min(yA._bubbleRadius,xA._bubbleRadius)
            bubbleMax = xA._bubbleMax

        labelFmt = self.lineLabelFormat

        P = list(range(len(self._positions)))
        if self.reversePlotOrder: P.reverse()
        inFill = getattr(self,'_inFill',None)
        styleCount = len(self.lines)
        if inFill or [rowNo for rowNo in P if getattr(self.lines[rowNo%styleCount],'inFill',False)]:
            inFillY = getattr(inFill,'yValue',None)
            if inFillY is None:
                inFillY = xA._y
            else:
                inFillY = yA.scale(inFillY)
            inFillX0 = yA._x
            inFillX1 = inFillX0 + xA._length
            inFillG = getattr(self,'_inFillG',g)
        lG = getattr(self,'_lineG',g)
        # Iterate over data rows.
        for rowNo in P:
            row = self._positions[rowNo]
            rowStyle = self.lines[rowNo % styleCount]
            rowColor = getattr(rowStyle,'strokeColor',None)
            dash = getattr(rowStyle, 'strokeDashArray', None)

            if hasattr(rowStyle, 'strokeWidth'):
                width = rowStyle.strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                width = self.lines.strokeWidth
            else:
                width = None

            # Iterate over data columns.
            if self.joinedLines:
                points = []
                for xy in row:
                    points += [xy[0], xy[1]]
                if inFill or getattr(rowStyle,'inFill',False):
                    fpoints = [inFillX0,inFillY] + points + [inFillX1,inFillY]
                    filler = getattr(rowStyle, 'filler', None)
                    if filler:
                        filler.fill(self,inFillG,rowNo,rowColor,fpoints)
                    else:
                        inFillG.add(Polygon(fpoints,fillColor=rowColor,strokeColor=rowColor,strokeWidth=width or 0.1))
                if inFill in (None,0,2):
                    line = PolyLine(points,strokeColor=rowColor,strokeLineCap=0,strokeLineJoin=1)
                    if width:
                        line.strokeWidth = width
                    if dash:
                        line.strokeDashArray = dash
                    lG.add(line)

            if hasattr(rowStyle, 'symbol'):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                if bubblePlot: drow = self.data[rowNo]
                for j,xy in enumerate(row):
                    symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor)
                    if symbol:
                        if bubblePlot:
                            symbol.size = bubbleR*(drow[j][2]/bubbleMax)**0.5
                        g.add(symbol)
            else:
                if bubblePlot: drow = self.data[rowNo]
                for j,xy in enumerate(row):
                    usymbol = getattr(self.lines[rowNo,j],'symbol',None)
                    if not usymbol: continue
                    symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor)
                    if symbol:
                        if bubblePlot:
                            symbol.size = bubbleR*(drow[j][2]/bubbleMax)**0.5
                        g.add(symbol)

            # Draw data labels.
            for colNo in range(len(row)):
                x1, y1 = row[colNo]
                self.drawLabel(g, rowNo, colNo, x1, y1)

            shader = getattr(rowStyle, 'shader', None)
            if shader: shader.shade(self,g,rowNo,rowColor,row)

        return g

    def draw(self):
        yA = self.yValueAxis
        xA = self.xValueAxis
        if getattr(self,'_bubblePlot',None):
            yA._bubblePlot = xA._bubblePlot = 1
        yA.setPosition(self.x, self.y, self.height)
        if yA: yA.joinAxis = xA
        if xA: xA.joinAxis = yA
        yA.configure(self.data)

        # if zero is in chart, put x axis there, otherwise use bottom.
        xAxisCrossesAt = yA.scale(0)
        if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        xA.setPosition(self.x, y, self.width)
        xA.configure(self.data)
        self.calcPositions()
        g = Group()
        g.add(self.makeBackground())
        if self._inFill or self.behindAxes:
            xA._joinToAxis()
            if self._inFill:
                self._inFillG = Group()
                g.add(self._inFillG)
            if self.behindAxes:
                self._lineG = Group()
                g.add(self._lineG)
        xA._joinToAxis()
        yA._joinToAxis()
        xAex = xA.visibleAxis and [xA._y] or []
        yAex = yA.visibleAxis and [yA._x] or []
        skipGrid = getattr(xA,'skipGrid','none')
        if skipGrid!=None:
            if skipGrid in ('both','top'):
                yAex.append(xA._x+xA._length)
            if skipGrid in ('both','bottom'):
                yAex.append(xA._x)
        skipGrid = getattr(yA,'skipGrid','none')
        if skipGrid!=None:
            if skipGrid in ('both','top'):
                xAex.append(yA._y+yA._length)
            if skipGrid in ('both','bottom'):
                xAex.append(yA._y)
        if self.gridFirst:
            xA.makeGrid(g,parent=self,dim=yA.getGridDims,exclude=yAex)
            yA.makeGrid(g,parent=self,dim=xA.getGridDims,exclude=xAex)
        g.add(xA.draw())
        g.add(yA.draw())
        if not self.gridFirst:
            xAdgl = getattr(xA,'drawGridLast',False)
            yAdgl = getattr(yA,'drawGridLast',False)
            if not xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims,exclude=yAex)
            if not yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims,exclude=xAex)
        annotations = getattr(self,'annotations',[])
        for a in annotations:
            if getattr(a,'beforeLines',None):
                g.add(a(self,xA.scale,yA.scale))
        g.add(self.makeLines())
        if not self.gridFirst:
            if xAdgl: xA.makeGrid(g,parent=self,dim=yA.getGridDims,exclude=yAex)
            if yAdgl: yA.makeGrid(g,parent=self,dim=xA.getGridDims,exclude=xAex)
        for a in annotations:
            if not getattr(a,'beforeLines',None):
                g.add(a(self,xA.scale,yA.scale))
        return g

    def addCrossHair(self,name,xv,yv,strokeColor=colors.black,strokeWidth=1,beforeLines=True):
        from reportlab.graphics.shapes import Group, Line
        annotations = [a for a in getattr(self,'annotations',[]) if getattr(a,'name',None)!=name]
        def annotation(self,xScale,yScale):
            x = xScale(xv)
            y = yScale(yv)
            g = Group()
            xA = xScale.__self__ #the x axis
            g.add(Line(xA._x,y,xA._x+xA._length,y,strokeColor=strokeColor,strokeWidth=strokeWidth))
            yA = yScale.__self__ #the y axis
            g.add(Line(x,yA._y,x,yA._y+yA._length,strokeColor=strokeColor,strokeWidth=strokeWidth))
            return g
        annotation.beforeLines = beforeLines
        annotations.append(annotation)
        self.annotations = annotations

class LinePlot3D(LinePlot):
    _attrMap = AttrMap(BASE=LinePlot,
        theta_x = AttrMapValue(isNumber, desc='dx/dz'),
        theta_y = AttrMapValue(isNumber, desc='dy/dz'),
        zDepth = AttrMapValue(isNumber, desc='depth of an individual series'),
        zSpace = AttrMapValue(isNumber, desc='z gap around series'),
        )
    theta_x = .5
    theta_y = .5
    zDepth = 10
    zSpace = 3

    def calcPositions(self):
        LinePlot.calcPositions(self)
        nSeries = self._seriesCount
        zSpace = self.zSpace
        zDepth = self.zDepth
        if self.xValueAxis.style=='parallel_3d':
            _3d_depth = nSeries*zDepth+(nSeries+1)*zSpace
        else:
            _3d_depth = zDepth + 2*zSpace
        self._3d_dx = self.theta_x*_3d_depth
        self._3d_dy = self.theta_y*_3d_depth

    def _calc_z0(self,rowNo):
        zSpace = self.zSpace
        if self.xValueAxis.style=='parallel_3d':
            z0 = rowNo*(self.zDepth+zSpace)+zSpace
        else:
            z0 = zSpace
        return z0

    def _zadjust(self,x,y,z):
        return x+z*self.theta_x, y+z*self.theta_y

    def makeLines(self):
        bubblePlot = getattr(self,'_bubblePlot',None)
        assert not bubblePlot, "_bubblePlot not supported for 3d yet"
        #if bubblePlot:
        #   yA = self.yValueAxis
        #   xA = self.xValueAxis
        #   bubbleR = min(yA._bubbleRadius,xA._bubbleRadius)
        #   bubbleMax = xA._bubbleMax

        labelFmt = self.lineLabelFormat
        positions = self._positions

        P = list(range(len(positions)))
        if self.reversePlotOrder: P.reverse()
        inFill = getattr(self,'_inFill',None)
        assert not inFill, "inFill not supported for 3d yet"
        #if inFill:
        #   inFillY = self.xValueAxis._y
        #   inFillX0 = self.yValueAxis._x
        #   inFillX1 = inFillX0 + self.xValueAxis._length
        #   inFillG = getattr(self,'_inFillG',g)
        zDepth = self.zDepth
        _zadjust = self._zadjust
        theta_x = self.theta_x
        theta_y = self.theta_y
        from reportlab.graphics.charts.linecharts import _FakeGroup
        F = _FakeGroup()

        from reportlab.graphics.charts.utils3d import _make_3d_line_info, find_intersections
        if self.xValueAxis.style!='parallel_3d':
            tileWidth = getattr(self,'_3d_tilewidth',1)
            if getattr(self,'_find_intersections',None):
                from copy import copy
                fpositions = list(map(copy,positions))
                I = find_intersections(fpositions,small=tileWidth)
                ic = None
                for i,j,x,y in I:
                    if ic!=i:
                        ic = i
                        jc = 0
                    else:
                        jc+=1
                    fpositions[i].insert(j+jc,(x,y))
                tileWidth = None
            else:
                fpositions = positions
        else:
            tileWidth = None
            fpositions = positions

        # Iterate over data rows.
        styleCount = len(self.lines)
        for rowNo in P:
            row = positions[rowNo]
            n = len(row)
            rowStyle = self.lines[rowNo % styleCount]
            rowColor = rowStyle.strokeColor
            dash = getattr(rowStyle, 'strokeDashArray', None)
            z0 = self._calc_z0(rowNo)
            z1 = z0 + zDepth

            if hasattr(rowStyle, 'strokeWidth'):
                width = rowStyle.strokeWidth
            elif hasattr(self.lines, 'strokeWidth'):
                width = self.lines.strokeWidth
            else:
                width = None

            # Iterate over data columns.
            if self.joinedLines:
                if n:
                    frow = fpositions[rowNo]
                    x0, y0 = frow[0]
                    for colNo in range(1,len(frow)):
                        x1, y1 = frow[colNo]
                        _make_3d_line_info( F, x0, x1, y0, y1, z0, z1,
                                theta_x, theta_y,
                                rowColor, fillColorShaded=None, tileWidth=tileWidth,
                                strokeColor=None, strokeWidth=None, strokeDashArray=None,
                                shading=0.1)
                        x0, y0 = x1, y1

            if hasattr(rowStyle, 'symbol'):
                uSymbol = rowStyle.symbol
            elif hasattr(self.lines, 'symbol'):
                uSymbol = self.lines.symbol
            else:
                uSymbol = None

            if uSymbol:
                for xy in row:
                    x1, y1 = row[colNo]
                    x1, y1 = _zadjust(x1,y1,z0)
                    symbol = uSymbol2Symbol(uSymbol,xy[0],xy[1],rowColor)
                    if symbol: F.add((1,z0,z0,x1,y1,symbol))

            # Draw data labels.
            for colNo in range(n):
                x1, y1 = row[colNo]
                x1, y1 = _zadjust(x1,y1,z0)
                L = self._innerDrawLabel(rowNo, colNo, x1, y1)
                if L: F.add((2,z0,z0,x1,y1,L))

        F.sort()
        g = Group()
        for v in F.value(): g.add(v[-1])
        return g

_monthlyIndexData = [[(19971202, 100.0),
  (19971231, 100.1704367),
  (19980131, 101.5639577),
  (19980228, 102.1879927),
  (19980331, 101.6337257),
  (19980430, 102.7640446),
  (19980531, 102.9198038),
  (19980630, 103.25938789999999),
  (19980731, 103.2516421),
  (19980831, 105.4744329),
  (19980930, 109.3242705),
  (19981031, 111.9859291),
  (19981130, 110.9184642),
  (19981231, 110.9184642),
  (19990131, 111.9882532),
  (19990228, 109.7912614),
  (19990331, 110.24189629999999),
  (19990430, 110.4279321),
  (19990531, 109.33955469999999),
  (19990630, 108.2341748),
  (19990731, 110.21294469999999),
  (19990831, 110.9683062),
  (19990930, 112.4425371),
  (19991031, 112.7314032),
  (19991130, 112.3509645),
  (19991231, 112.3660659),
  (20000131, 110.9255248),
  (20000229, 110.5266306),
  (20000331, 113.3116101),
  (20000430, 111.0449133),
  (20000531, 111.702717),
  (20000630, 113.5832178)],
 [(19971202, 100.0),
  (19971231, 100.0),
  (19980131, 100.8),
  (19980228, 102.0),
  (19980331, 101.9),
  (19980430, 103.0),
  (19980531, 103.0),
  (19980630, 103.1),
  (19980731, 103.1),
  (19980831, 102.8),
  (19980930, 105.6),
  (19981031, 108.3),
  (19981130, 108.1),
  (19981231, 111.9),
  (19990131, 113.1),
  (19990228, 110.2),
  (19990331, 111.8),
  (19990430, 112.3),
  (19990531, 110.1),
  (19990630, 109.3),
  (19990731, 111.2),
  (19990831, 111.7),
  (19990930, 112.6),
  (19991031, 113.2),
  (19991130, 113.9),
  (19991231, 115.4),
  (20000131, 112.7),
  (20000229, 113.9),
  (20000331, 115.8),
  (20000430, 112.2),
  (20000531, 112.6),
  (20000630, 114.6)]]

class SimpleTimeSeriesPlot(LinePlot):
    """A customized version of LinePlot.
    It uses NormalDateXValueAxis() and AdjYValueAxis() for the X and Y axes.
    """
    def __init__(self):
        LinePlot.__init__(self)
        self.xValueAxis = NormalDateXValueAxis()
        self.yValueAxis = YValueAxis()
        self.data = _monthlyIndexData

class GridLinePlot(SimpleTimeSeriesPlot):
    """A customized version of SimpleTimeSeriesSPlot.
    It uses NormalDateXValueAxis() and AdjYValueAxis() for the X and Y axes.
    The chart has a default grid background with thin horizontal lines
    aligned with the tickmarks (and labels). You can change the back-
    ground to be any Grid or ShadedRect, or scale the whole chart.
    If you do provide a background, you can specify the colours of the
    stripes with 'background.stripeColors'.
    """

    _attrMap = AttrMap(BASE=LinePlot,
        background = AttrMapValue(None, desc='Background for chart area (now Grid or ShadedRect).'),
        scaleFactor = AttrMapValue(isNumberOrNone, desc='Scalefactor to apply to whole drawing.'),
        )

    def __init__(self):
        from reportlab.lib import colors
        SimpleTimeSeriesPlot.__init__(self)
        self.scaleFactor = None
        self.background = Grid()
        self.background.orientation = 'horizontal'
        self.background.useRects = 0
        self.background.useLines = 1
        self.background.strokeWidth = 0.5
        self.background.strokeColor = colors.black

    def demo(self,drawing=None):
        from reportlab.lib import colors
        if not drawing:
            drawing = Drawing(400, 200)
        lp = GridLinePlot()
        lp.x = 50
        lp.y = 50
        lp.height = 125
        lp.width = 300
        lp.data = _monthlyIndexData
        lp.joinedLines = 1
        lp.strokeColor = colors.black
        c0 = colors.PCMYKColor(100,65,0,30, spotName='PANTONE 288 CV', density=100)
        lp.lines[0].strokeColor = c0
        lp.lines[0].strokeWidth = 2
        lp.lines[0].strokeDashArray = None
        c1 = colors.PCMYKColor(0,79,91,0, spotName='PANTONE Wm Red CV', density=100)
        lp.lines[1].strokeColor = c1
        lp.lines[1].strokeWidth = 1
        lp.lines[1].strokeDashArray = [3,1]
        lp.xValueAxis.labels.fontSize = 10
        lp.xValueAxis.labels.textAnchor = 'start'
        lp.xValueAxis.labels.boxAnchor = 'w'
        lp.xValueAxis.labels.angle = -45
        lp.xValueAxis.labels.dx = 0
        lp.xValueAxis.labels.dy = -8
        lp.xValueAxis.xLabelFormat = '{mm}/{yy}'
        lp.yValueAxis.labelTextFormat = '%5d%% '
        lp.yValueAxis.tickLeft = 5
        lp.yValueAxis.labels.fontSize = 10
        lp.background = Grid()
        lp.background.stripeColors = [colors.pink, colors.lightblue]
        lp.background.orientation = 'vertical'
        drawing.add(lp,'plot')
        return drawing

    def draw(self):
        xva, yva = self.xValueAxis, self.yValueAxis
        if xva: xva.joinAxis = yva
        if yva: yva.joinAxis = xva

        yva.setPosition(self.x, self.y, self.height)
        yva.configure(self.data)

        # if zero is in chart, put x axis there, otherwise
        # use bottom.
        xAxisCrossesAt = yva.scale(0)
        if ((xAxisCrossesAt > self.y + self.height) or (xAxisCrossesAt < self.y)):
            y = self.y
        else:
            y = xAxisCrossesAt

        xva.setPosition(self.x, y, self.width)
        xva.configure(self.data)

        back = self.background
        if isinstance(back, Grid):
            if back.orientation == 'vertical' and xva._tickValues:
                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                steps = []
                for i in range(len(xpos)-1):
                    steps.append(xpos[i+1] - xpos[i])
                back.deltaSteps = steps
            elif back.orientation == 'horizontal' and yva._tickValues:
                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                steps = []
                for i in range(len(ypos)-1):
                    steps.append(ypos[i+1] - ypos[i])
                back.deltaSteps = steps
        elif isinstance(back, DoubleGrid):
            # Ideally, these lines would not be needed...
            back.grid0.x = self.x
            back.grid0.y = self.y
            back.grid0.width = self.width
            back.grid0.height = self.height
            back.grid1.x = self.x
            back.grid1.y = self.y
            back.grid1.width = self.width
            back.grid1.height = self.height

            # some room left for optimization...
            if back.grid0.orientation == 'vertical' and xva._tickValues:
                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                steps = []
                for i in range(len(xpos)-1):
                    steps.append(xpos[i+1] - xpos[i])
                back.grid0.deltaSteps = steps
            elif back.grid0.orientation == 'horizontal' and yva._tickValues:
                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                steps = []
                for i in range(len(ypos)-1):
                    steps.append(ypos[i+1] - ypos[i])
                back.grid0.deltaSteps = steps
            if back.grid1.orientation == 'vertical' and xva._tickValues:
                xpos = list(map(xva.scale, [xva._valueMin] + xva._tickValues))
                steps = []
                for i in range(len(xpos)-1):
                    steps.append(xpos[i+1] - xpos[i])
                back.grid1.deltaSteps = steps
            elif back.grid1.orientation == 'horizontal' and yva._tickValues:
                ypos = list(map(yva.scale, [yva._valueMin] + yva._tickValues))
                steps = []
                for i in range(len(ypos)-1):
                    steps.append(ypos[i+1] - ypos[i])
                back.grid1.deltaSteps = steps

        self.calcPositions()

        width, height, scaleFactor = self.width, self.height, self.scaleFactor
        if scaleFactor and scaleFactor!=1:
            #g = Drawing(scaleFactor*width, scaleFactor*height)
            g.transform = (scaleFactor, 0, 0, scaleFactor,0,0)
        else:
            g = Group()

        g.add(self.makeBackground())
        g.add(self.xValueAxis)
        g.add(self.yValueAxis)
        g.add(self.makeLines())

        return g

class AreaLinePlot(LinePlot):
    '''we're given data in the form [(X1,Y11,..Y1M)....(Xn,Yn1,...YnM)]'''#'
    def __init__(self):
        LinePlot.__init__(self)
        self._inFill = 1
        self.reversePlotOrder = 1
        self.data = [(1,20,100,30),(2,11,50,15),(3,15,70,40)]

    def draw(self):
        try:
            odata = self.data
            n = len(odata)
            m = len(odata[0])
            S = n*[0]
            self.data = []
            for i in range(1,m):
                D = []
                for j in range(n):
                    S[j] = S[j] + odata[j][i]
                    D.append((odata[j][0],S[j]))
                self.data.append(D)
            return LinePlot.draw(self)
        finally:
            self.data = odata

class SplitLinePlot(AreaLinePlot):
    def __init__(self):
        AreaLinePlot.__init__(self)
        self.xValueAxis = NormalDateXValueAxis()
        self.yValueAxis = AdjYValueAxis()
        self.data=[(20030601,0.95,0.05,0.0),(20030701,0.95,0.05,0.0),(20030801,0.95,0.05,0.0),(20030901,0.95,0.05,0.0),(20031001,0.95,0.05,0.0),(20031101,0.95,0.05,0.0),(20031201,0.95,0.05,0.0),(20040101,0.95,0.05,0.0),(20040201,0.95,0.05,0.0),(20040301,0.95,0.05,0.0),(20040401,0.95,0.05,0.0),(20040501,0.95,0.05,0.0),(20040601,0.95,0.05,0.0),(20040701,0.95,0.05,0.0),(20040801,0.95,0.05,0.0),(20040901,0.95,0.05,0.0),(20041001,0.95,0.05,0.0),(20041101,0.95,0.05,0.0),(20041201,0.95,0.05,0.0),(20050101,0.95,0.05,0.0),(20050201,0.95,0.05,0.0),(20050301,0.95,0.05,0.0),(20050401,0.95,0.05,0.0),(20050501,0.95,0.05,0.0),(20050601,0.95,0.05,0.0),(20050701,0.95,0.05,0.0),(20050801,0.95,0.05,0.0),(20050901,0.95,0.05,0.0),(20051001,0.95,0.05,0.0),(20051101,0.95,0.05,0.0),(20051201,0.95,0.05,0.0),(20060101,0.95,0.05,0.0),(20060201,0.95,0.05,0.0),(20060301,0.95,0.05,0.0),(20060401,0.95,0.05,0.0),(20060501,0.95,0.05,0.0),(20060601,0.95,0.05,0.0),(20060701,0.95,0.05,0.0),(20060801,0.95,0.05,0.0),(200
 60901,0.95,0.05,0.0),(20061001,0.95,0.05,0.0),(20061101,0.95,0.05,0.0),(20061201,0.95,0.05,0.0),(20070101,0.95,0.05,0.0),(20070201,0.95,0.05,0.0),(20070301,0.95,0.05,0.0),(20070401,0.95,0.05,0.0),(20070501,0.95,0.05,0.0),(20070601,0.95,0.05,0.0),(20070701,0.95,0.05,0.0),(20070801,0.95,0.05,0.0),(20070901,0.95,0.05,0.0),(20071001,0.95,0.05,0.0),(20071101,0.95,0.05,0.0),(20071201,0.95,0.05,0.0),(20080101,0.95,0.05,0.0),(20080201,0.95,0.05,0.0),(20080301,0.95,0.05,0.0),(20080401,0.95,0.05,0.0),(20080501,0.95,0.05,0.0),(20080601,0.95,0.05,0.0),(20080701,0.95,0.05,0.0),(20080801,0.95,0.05,0.0),(20080901,0.95,0.05,0.0),(20081001,0.95,0.05,0.0),(20081101,0.95,0.05,0.0),(20081201,0.95,0.05,0.0),(20090101,0.95,0.05,0.0),(20090201,0.91,0.09,0.0),(20090301,0.91,0.09,0.0),(20090401,0.91,0.09,0.0),(20090501,0.91,0.09,0.0),(20090601,0.91,0.09,0.0),(20090701,0.91,0.09,0.0),(20090801,0.91,0.09,0.0),(20090901,0.91,0.09,0.0),(20091001,0.91,0.09,0.0),(20091101,0.91,0.09,0.0),(20091201,0.91,0.09,0.0),(
 20100101,0.91,0.09,0.0),(20100201,0.81,0.19,0.0),(20100301,0.81,0.19,0.0),(20100401,0.81,0.19,0.0),(20100501,0.81,0.19,0.0),(20100601,0.81,0.19,0.0),(20100701,0.81,0.19,0.0),(20100801,0.81,0.19,0.0),(20100901,0.81,0.19,0.0),(20101001,0.81,0.19,0.0),(20101101,0.81,0.19,0.0),(20101201,0.81,0.19,0.0),(20110101,0.81,0.19,0.0),(20110201,0.72,0.28,0.0),(20110301,0.72,0.28,0.0),(20110401,0.72,0.28,0.0),(20110501,0.72,0.28,0.0),(20110601,0.72,0.28,0.0),(20110701,0.72,0.28,0.0),(20110801,0.72,0.28,0.0),(20110901,0.72,0.28,0.0),(20111001,0.72,0.28,0.0),(20111101,0.72,0.28,0.0),(20111201,0.72,0.28,0.0),(20120101,0.72,0.28,0.0),(20120201,0.53,0.47,0.0),(20120301,0.53,0.47,0.0),(20120401,0.53,0.47,0.0),(20120501,0.53,0.47,0.0),(20120601,0.53,0.47,0.0),(20120701,0.53,0.47,0.0),(20120801,0.53,0.47,0.0),(20120901,0.53,0.47,0.0),(20121001,0.53,0.47,0.0),(20121101,0.53,0.47,0.0),(20121201,0.53,0.47,0.0),(20130101,0.53,0.47,0.0),(20130201,0.44,0.56,0.0),(20130301,0.44,0.56,0.0),(20130401,0.44,0.56,0.0
 ),(20130501,0.44,0.56,0.0),(20130601,0.44,0.56,0.0),(20130701,0.44,0.56,0.0),(20130801,0.44,0.56,0.0),(20130901,0.44,0.56,0.0),(20131001,0.44,0.56,0.0),(20131101,0.44,0.56,0.0),(20131201,0.44,0.56,0.0),(20140101,0.44,0.56,0.0),(20140201,0.36,0.5,0.14),(20140301,0.36,0.5,0.14),(20140401,0.36,0.5,0.14),(20140501,0.36,0.5,0.14),(20140601,0.36,0.5,0.14),(20140701,0.36,0.5,0.14),(20140801,0.36,0.5,0.14),(20140901,0.36,0.5,0.14),(20141001,0.36,0.5,0.14),(20141101,0.36,0.5,0.14),(20141201,0.36,0.5,0.14),(20150101,0.36,0.5,0.14),(20150201,0.3,0.41,0.29),(20150301,0.3,0.41,0.29),(20150401,0.3,0.41,0.29),(20150501,0.3,0.41,0.29),(20150601,0.3,0.41,0.29),(20150701,0.3,0.41,0.29),(20150801,0.3,0.41,0.29),(20150901,0.3,0.41,0.29),(20151001,0.3,0.41,0.29),(20151101,0.3,0.41,0.29),(20151201,0.3,0.41,0.29),(20160101,0.3,0.41,0.29),(20160201,0.26,0.36,0.38),(20160301,0.26,0.36,0.38),(20160401,0.26,0.36,0.38),(20160501,0.26,0.36,0.38),(20160601,0.26,0.36,0.38),(20160701,0.26,0.36,0.38),(20160801,0.26
 ,0.36,0.38),(20160901,0.26,0.36,0.38),(20161001,0.26,0.36,0.38),(20161101,0.26,0.36,0.38),(20161201,0.26,0.36,0.38),(20170101,0.26,0.36,0.38),(20170201,0.2,0.3,0.5),(20170301,0.2,0.3,0.5),(20170401,0.2,0.3,0.5),(20170501,0.2,0.3,0.5),(20170601,0.2,0.3,0.5),(20170701,0.2,0.3,0.5),(20170801,0.2,0.3,0.5),(20170901,0.2,0.3,0.5),(20171001,0.2,0.3,0.5),(20171101,0.2,0.3,0.5),(20171201,0.2,0.3,0.5),(20180101,0.2,0.3,0.5),(20180201,0.13,0.37,0.5),(20180301,0.13,0.37,0.5),(20180401,0.13,0.37,0.5),(20180501,0.13,0.37,0.5),(20180601,0.13,0.37,0.5),(20180701,0.13,0.37,0.5),(20180801,0.13,0.37,0.5),(20180901,0.13,0.37,0.5),(20181001,0.13,0.37,0.5),(20181101,0.13,0.37,0.5),(20181201,0.13,0.37,0.5),(20190101,0.13,0.37,0.5),(20190201,0.1,0.4,0.5),(20190301,0.1,0.4,0.5),(20190401,0.1,0.4,0.5),(20190501,0.1,0.4,0.5),(20190601,0.1,0.4,0.5),(20190701,0.1,0.4,0.5),(20190801,0.1,0.4,0.5),(20190901,0.1,0.4,0.5),(20191001,0.1,0.4,0.5),(20191101,0.1,0.4,0.5),(20191201,0.1,0.4,0.5),(20200101,0.1,0.4,0.5)]
        self.yValueAxis.requiredRange = None
        self.yValueAxis.leftAxisPercent = 0
        self.yValueAxis.leftAxisOrigShiftMin = 0
        self.yValueAxis.leftAxisOrigShiftIPC = 0
        self.lines[0].strokeColor = colors.toColor(0x0033cc)
        self.lines[1].strokeColor = colors.toColor(0x99c3ff)
        self.lines[2].strokeColor = colors.toColor(0xCC0033)

def _maxWidth(T, fontName, fontSize):
    '''return max stringWidth for the list of strings T'''
    if not isinstance(T,(tuple,list)): T = (T,)
    T = [_f for _f in T if _f]
    return T and max(list(map(lambda t,sW=stringWidth,fN=fontName, fS=fontSize: sW(t,fN,fS),T))) or 0

class ScatterPlot(LinePlot):
    """A scatter plot widget"""

    _attrMap = AttrMap(BASE=LinePlot,
                    width = AttrMapValue(isNumber, desc="Width of the area inside the axes"),
                    height = AttrMapValue(isNumber, desc="Height of the area inside the axes"),
                    outerBorderOn = AttrMapValue(isBoolean, desc="Is there an outer border (continuation of axes)"),
                    outerBorderColor = AttrMapValue(isColorOrNone, desc="Color of outer border (if any)"),
                    labelOffset = AttrMapValue(isNumber, desc="Space between label and Axis (or other labels)",advancedUsage=1),
                    axisTickLengths = AttrMapValue(isNumber, desc="Lenth of the ticks on both axes"),
                    axisStrokeWidth = AttrMapValue(isNumber, desc="Stroke width for both axes"),
                    xLabel = AttrMapValue(isString, desc="Label for the whole X-Axis"),
                    yLabel = AttrMapValue(isString, desc="Label for the whole Y-Axis"),
                    data = AttrMapValue(isAnything, desc='Data points - a list of x/y tuples.'),
                    strokeColor = AttrMapValue(isColorOrNone, desc='Color used for border of plot area.'),
                    fillColor = AttrMapValue(isColorOrNone, desc='Color used for background interior of plot area.'),
                    leftPadding = AttrMapValue(isNumber, desc='Padding on left of drawing'),
                    rightPadding = AttrMapValue(isNumber, desc='Padding on right of drawing'),
                    topPadding = AttrMapValue(isNumber, desc='Padding at top of drawing'),
                    bottomPadding = AttrMapValue(isNumber, desc='Padding at bottom of drawing'),
                    )

    def __init__(self):
        LinePlot.__init__(self)
        self.width = 142
        self.height = 77
        self.outerBorderOn = 1
        self.outerBorderColor = colors.black
        self.background = None

        _labelOffset = 3
        _axisTickLengths = 2
        _axisStrokeWidth = 0.5

        self.yValueAxis.valueMin = None
        self.yValueAxis.valueMax = None
        self.yValueAxis.valueStep = None
        self.yValueAxis.labelTextFormat  = '%s'

        self.xLabel="X Lable"
        self.xValueAxis.labels.fontSize = 6

        self.yLabel="Y Lable"
        self.yValueAxis.labels.fontSize = 6

        self.data =[((0.030, 62.73),
                     (0.074, 54.363),
                     (1.216, 17.964)),

                     ((1.360, 11.621),
                     (1.387, 50.011),
                     (1.428, 68.953)),

                     ((1.444, 86.888),
                     (1.754, 35.58),
                     (1.766, 36.05))]

        #values for lineplot
        self.joinedLines = 0

        self.leftPadding=5
        self.rightPadding=10
        self.topPadding=5
        self.bottomPadding=5

        self.x = self.leftPadding+_axisTickLengths+(_labelOffset*2)
        self.x=self.x+_maxWidth(str(self.yValueAxis.valueMax), self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize)
        self.y = self.bottomPadding+_axisTickLengths+_labelOffset+self.xValueAxis.labels.fontSize

        self.xValueAxis.labels.dy = -_labelOffset
        self.xValueAxis.tickDown = _axisTickLengths
        self.xValueAxis.strokeWidth = _axisStrokeWidth
        self.xValueAxis.rangeRound='both'
        self.yValueAxis.labels.dx = -_labelOffset
        self.yValueAxis.tickLeft = _axisTickLengths
        self.yValueAxis.strokeWidth = _axisStrokeWidth
        self.yValueAxis.rangeRound='both'

        self.lineLabelFormat="%.2f"
        self.lineLabels.fontSize = 5
        self.lineLabels.boxAnchor = 'e'
        self.lineLabels.dx             = -2
        self.lineLabelNudge = 0
        self.lines.symbol=makeMarker('FilledCircle',size=3)
        self.lines[1].symbol=makeMarker('FilledDiamond',size=3)
        self.lines[2].symbol=makeMarker('FilledSquare',size=3)
        self.lines[2].strokeColor = colors.green

    def _getDrawingDimensions(self):
        tx = self.leftPadding+self.yValueAxis.tickLeft+(self.yValueAxis.labels.dx*2)+self.xValueAxis.labels.fontSize
        tx=tx+(5*_maxWidth(str(self.yValueAxis.valueMax), self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize))
        tx=tx+self.width+self.rightPadding
        t=('%.2f%%'%self.xValueAxis.valueMax)
        tx=tx+(_maxWidth(t, self.yValueAxis.labels.fontName, self.yValueAxis.labels.fontSize))
        ty = self.bottomPadding+self.xValueAxis.tickDown+(self.xValueAxis.labels.dy*2)+(self.xValueAxis.labels.fontSize*2)
        ty=ty+self.yValueAxis.labels.fontSize+self.height+self.topPadding
        #print (tx, ty)
        return (tx,ty)

    def demo(self,drawing=None):
        if not drawing:
            tx,ty=self._getDrawingDimensions()
            drawing = Drawing(tx,ty)
        drawing.add(self.draw())
        return drawing

    def draw(self):
        ascent=getFont(self.xValueAxis.labels.fontName).face.ascent
        if ascent==0:
            ascent=0.718 # default (from helvetica)
        ascent=ascent*self.xValueAxis.labels.fontSize # normalize

        #basic LinePlot - does the Axes, Ticks etc
        lp = LinePlot.draw(self)

        xLabel = self.xLabel
        if xLabel: #Overall label for the X-axis
            xl=Label()
            xl.x = (self.x+self.width)/2.0
            xl.y = 0
            xl.fontName = self.xValueAxis.labels.fontName
            xl.fontSize = self.xValueAxis.labels.fontSize
            xl.setText(xLabel)
            lp.add(xl)

        yLabel = self.yLabel
        if yLabel: #Overall label for the Y-axis
            yl=Label()
            yl.angle = 90
            yl.x = 0
            yl.y = (self.y+self.height/2.0)
            yl.fontName = self.yValueAxis.labels.fontName
            yl.fontSize = self.yValueAxis.labels.fontSize
            yl.setText(yLabel)
            lp.add(yl)

        # do a bounding box - in the same style as the axes
        if self.outerBorderOn:
            lp.add(Rect(self.x, self.y, self.width, self.height,
                       strokeColor = self.outerBorderColor,
                       strokeWidth = self.yValueAxis.strokeWidth,
                       fillColor = None))

        lp.shift(self.leftPadding, self.bottomPadding)

        return lp

def sample1a():
    "A line plot with non-equidistant points in x-axis."

    drawing = Drawing(400, 200)

    data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3.5,5), (4,6))
        ]

    lp = LinePlot()

    lp.x = 50
    lp.y = 50
    lp.height = 125
    lp.width = 300
    lp.data = data
    lp.joinedLines = 1
    lp.strokeColor = colors.black

    lp.lines.symbol = makeMarker('UK_Flag')

    lp.lines[0].strokeWidth = 2
    lp.lines[1].strokeWidth = 4

    lp.xValueAxis.valueMin = 0
    lp.xValueAxis.valueMax = 5
    lp.xValueAxis.valueStep = 1

    lp.yValueAxis.valueMin = 0
    lp.yValueAxis.valueMax = 7
    lp.yValueAxis.valueStep = 1

    drawing.add(lp)

    return drawing


def sample1b():
    "A line plot with non-equidistant points in x-axis."

    drawing = Drawing(400, 200)

    data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3.5,5), (4,6))
        ]

    lp = LinePlot()

    lp.x = 50
    lp.y = 50
    lp.height = 125
    lp.width = 300
    lp.data = data
    lp.joinedLines = 1
    lp.lines.symbol = makeMarker('Circle')
    lp.lineLabelFormat = '%2.0f'
    lp.strokeColor = colors.black

    lp.xValueAxis.valueMin = 0
    lp.xValueAxis.valueMax = 5
    lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5]
    lp.xValueAxis.labelTextFormat = '%2.1f'

    lp.yValueAxis.valueMin = 0
    lp.yValueAxis.valueMax = 7
    lp.yValueAxis.valueStep = 1

    drawing.add(lp)

    return drawing


def sample1c():
    "A line plot with non-equidistant points in x-axis."

    drawing = Drawing(400, 200)

    data = [
            ((1,1), (2,2), (2.5,1), (3,3), (4,5)),
            ((1,2), (2,3), (2.5,2), (3.5,5), (4,6))
        ]

    lp = LinePlot()

    lp.x = 50
    lp.y = 50
    lp.height = 125
    lp.width = 300
    lp.data = data
    lp.joinedLines = 1
    lp.lines[0].symbol = makeMarker('FilledCircle')
    lp.lines[1].symbol = makeMarker('Circle')
    lp.lineLabelFormat = '%2.0f'
    lp.strokeColor = colors.black

    lp.xValueAxis.valueMin = 0
    lp.xValueAxis.valueMax = 5
    lp.xValueAxis.valueSteps = [1, 2, 2.5, 3, 4, 5]
    lp.xValueAxis.labelTextFormat = '%2.1f'

    lp.yValueAxis.valueMin = 0
    lp.yValueAxis.valueMax = 7
    lp.yValueAxis.valueSteps = [1, 2, 3, 5, 6]

    drawing.add(lp)

    return drawing


def preprocessData(series):
    "Convert date strings into seconds and multiply values by 100."

    return [(str2seconds(x[0]), x[1]*100) for x in series]


def sample2():
    "A line plot with non-equidistant points in x-axis."

    drawing = Drawing(400, 200)

    data = [
        (('25/11/1991',1),
         ('30/11/1991',1.000933333),
         ('31/12/1991',1.0062),
         ('31/01/1992',1.0112),
         ('29/02/1992',1.0158),
         ('31/03/1992',1.020733333),
         ('30/04/1992',1.026133333),
         ('31/05/1992',1.030266667),
         ('30/06/1992',1.034466667),
         ('31/07/1992',1.038733333),
         ('31/08/1992',1.0422),
         ('30/09/1992',1.045533333),
         ('31/10/1992',1.049866667),
         ('30/11/1992',1.054733333),
         ('31/12/1992',1.061),
         ),
        ]

    data[0] = preprocessData(data[0])

    lp = LinePlot()

    lp.x = 50
    lp.y = 50
    lp.height = 125
    lp.width = 300
    lp.data = data
    lp.joinedLines = 1
    lp.lines.symbol = makeMarker('FilledDiamond')
    lp.strokeColor = colors.black

    start = mktime(mkTimeTuple('25/11/1991'))
    t0 = mktime(mkTimeTuple('30/11/1991'))
    t1 = mktime(mkTimeTuple('31/12/1991'))
    t2 = mktime(mkTimeTuple('31/03/1992'))
    t3 = mktime(mkTimeTuple('30/06/1992'))
    t4 = mktime(mkTimeTuple('30/09/1992'))
    end = mktime(mkTimeTuple('31/12/1992'))
    lp.xValueAxis.valueMin = start
    lp.xValueAxis.valueMax = end
    lp.xValueAxis.valueSteps = [start, t0, t1, t2, t3, t4, end]
    lp.xValueAxis.labelTextFormat = seconds2str
    lp.xValueAxis.labels[1].dy = -20
    lp.xValueAxis.labels[2].dy = -35

    lp.yValueAxis.labelTextFormat = '%4.2f'
    lp.yValueAxis.valueMin = 100
    lp.yValueAxis.valueMax = 110
    lp.yValueAxis.valueStep = 2

    drawing.add(lp)

    return drawing
-------------- next part --------------
from reportlab.graphics.shapes import Drawing
from reportlab.graphics.charts.lineplots import AreaLinePlot, InFillValue

class MyAreaPlot(Drawing):
        def __init__(self,width=400,height=200,*args,**kw):
                Drawing.__init__(self,width,height,*args,**kw)
                self.add(AreaLinePlot(),name='chart')
                self.chart.y                = 20
                self.chart._inFill          = InFillValue(1,0)
                self.chart.data             = [(1, -20, 100), (2, -11, 50), (3, -15, 70)]

if __name__=="__main__":
        MyAreaPlot().save(formats=['pdf'],outDir='.',fnRoot=None)

------------------------------

Subject: Digest Footer

_______________________________________________
reportlab-users mailing list
reportlab-users at lists2.reportlab.com
https://pairlist2.pair.net/mailman/listinfo/reportlab-users


------------------------------

End of reportlab-users Digest, Vol 135, Issue 5
***********************************************


________________________________
- CONFIDENTIAL-

This email and any files transmitted with it are confidential, and may also be legally privileged. If you are not the intended recipient, you may not review, use, copy, or distribute this message. If you receive this email in error, please notify the sender immediately by reply email and then delete this email.


More information about the reportlab-users mailing list