示例#1
0
    def draw(self, state, plotCoordinates, plotDefinitions, performanceTable):
        """Draw the plot element.

        This stage consists of creating an SVG image of the
        pre-computed data.

        @type state: ad-hoc Python object
        @param state: State information that persists long enough to use quantities computed in C{prepare} in the C{draw} stage.  This is a work-around of lxml's refusal to let its Python instances maintain C{self} and it is unrelated to DataTableState.
        @type plotCoordinates: PlotCoordinates
        @param plotCoordinates: The coordinate system in which this plot element will be placed.
        @type plotDefinitions: PlotDefinitions
        @type plotDefinitions: The dictionary of key-value pairs that forms the <defs> section of the SVG document.
        @type performanceTable: PerformanceTable
        @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process.
        @rtype: SvgBinding
        @return: An SVG fragment representing the fully drawn plot element.
        """

        svg = SvgBinding.elementMaker
        performanceTable.begin("PlotHistogram draw")

        cumulative = self.get("cumulative",
                              defaultFromXsd=True,
                              convertType=True)
        vertical = self.get("vertical", defaultFromXsd=True, convertType=True)
        visualization = self.get("visualization", defaultFromXsd=True)

        output = svg.g()
        if len(state.count) > 0:
            if state.fieldType is not self.fieldTypeNumeric:
                if vertical:
                    strings = plotCoordinates.xstrings
                else:
                    strings = plotCoordinates.ystrings

                newCount = []
                for string in strings:
                    try:
                        index = state.edges.index(string)
                    except ValueError:
                        newCount.append(0.0)
                    else:
                        newCount.append(state.count[index])

                state.count = newCount
                state.edges = [(i - 0.5, i + 0.5)
                               for i in xrange(len(strings))]

            if vertical:
                Ax = NP("array", [
                    low if low is not None else float("-inf")
                    for low, high in state.edges
                ],
                        dtype=NP.dtype(float))
                Bx = NP(Ax.copy())
                Cx = NP("array", [
                    high if high is not None else float("inf")
                    for low, high in state.edges
                ],
                        dtype=NP.dtype(float))
                Dx = NP(Cx.copy())
                Ay = NP("zeros", len(state.count), dtype=NP.dtype(float))
                if cumulative:
                    Cy = NP("cumsum",
                            NP("array", state.count, dtype=NP.dtype(float)))
                    By = NP("roll", Cy, 1)
                    By[0] = 0.0
                else:
                    By = NP("array", state.count, dtype=NP.dtype(float))
                    Cy = NP(By.copy())
                Dy = NP(Ay.copy())

            else:
                if cumulative:
                    Cx = NP("cumsum",
                            NP("array", state.count, dtype=NP.dtype(float)))
                    Bx = NP("roll", Cx, 1)
                    Bx[0] = 0.0
                else:
                    Bx = NP("array", state.count, dtype=NP.dtype(float))
                    Cx = NP(Bx.copy())
                Ax = NP("zeros", len(state.count), dtype=NP.dtype(float))
                Dx = NP(Ax.copy())
                Ay = NP("array", [
                    low if low is not None else float("-inf")
                    for low, high in state.edges
                ],
                        dtype=NP.dtype(float))
                By = NP(Ay.copy())
                Cy = NP("array", [
                    high if high is not None else float("inf")
                    for low, high in state.edges
                ],
                        dtype=NP.dtype(float))
                Dy = NP(Cy.copy())

            AX, AY = plotCoordinates(Ax, Ay)
            BX, BY = plotCoordinates(Bx, By)
            CX, CY = plotCoordinates(Cx, Cy)
            DX, DY = plotCoordinates(Dx, Dy)

            if visualization == "skyline":
                gap = self.get("gap", defaultFromXsd=True, convertType=True)

                if vertical:
                    if gap > 0.0 and NP(
                            NP(DX - gap / 2.0) -
                            NP(AX + gap / 2.0)).min() > 0.0:
                        AX += gap / 2.0
                        BX += gap / 2.0
                        CX -= gap / 2.0
                        DX -= gap / 2.0
                else:
                    if gap > 0.0 and NP(
                            NP(AY + gap / 2.0) -
                            NP(DY - gap / 2.0)).min() > 0.0:
                        AY -= gap / 2.0
                        BY -= gap / 2.0
                        CY += gap / 2.0
                        DY += gap / 2.0

                pathdata = []
                nextIsMoveto = True
                for i in xrange(len(state.count)):
                    iprev = i - 1
                    inext = i + 1

                    if vertical and By[i] == 0.0 and Cy[i] == 0.0:
                        if i > 0 and not nextIsMoveto:
                            pathdata.append("L %r %r" % (DX[iprev], DY[iprev]))
                        nextIsMoveto = True

                    elif not vertical and Bx[i] == 0.0 and Cx[i] == 0.0:
                        if i > 0 and not nextIsMoveto:
                            pathdata.append("L %r %r" % (DX[iprev], DY[iprev]))
                        nextIsMoveto = True

                    else:
                        if nextIsMoveto or gap > 0.0 or (
                                vertical and DX[iprev] != AX[i]) or (
                                    not vertical and DY[iprev] != AY[i]):
                            pathdata.append("M %r %r" % (AX[i], AY[i]))
                            nextIsMoveto = False

                        pathdata.append("L %r %r" % (BX[i], BY[i]))
                        pathdata.append("L %r %r" % (CX[i], CY[i]))

                        if i == len(state.count) - 1 or gap > 0.0 or (
                                vertical and DX[i] != AX[inext]) or (
                                    not vertical and DY[i] != AY[inext]):
                            pathdata.append("L %r %r" % (DX[i], DY[i]))

                style = self.getStyleState()
                del style["marker-size"]
                del style["marker-outline"]
                output.append(
                    svg.path(d=" ".join(pathdata),
                             style=PlotStyle.toString(style)))

            elif visualization == "polyline":
                pathdata = []
                for i in xrange(len(state.count)):
                    if i == 0:
                        pathdata.append("M %r %r" % (AX[i], AY[i]))

                    pathdata.append("L %r %r" % ((BX[i] + CX[i]) / 2.0,
                                                 (BY[i] + CY[i]) / 2.0))

                    if i == len(state.count) - 1:
                        pathdata.append("L %r %r" % (DX[i], DY[i]))

                style = self.getStyleState()
                del style["marker-size"]
                del style["marker-outline"]
                output.append(
                    svg.path(d=" ".join(pathdata),
                             style=PlotStyle.toString(style)))

            elif visualization == "smooth":
                smoothingSamples = math.ceil(len(state.count) / 2.0)

                BCX = NP(NP(BX + CX) / 2.0)
                BCY = NP(NP(BY + CY) / 2.0)

                xarray = NP("array", [AX[0]] + list(BCX) + [DX[-1]],
                            dtype=NP.dtype(float))
                yarray = NP("array", [AY[0]] + list(BCY) + [DY[-1]],
                            dtype=NP.dtype(float))
                samples = NP("linspace",
                             AX[0],
                             DX[-1],
                             int(smoothingSamples),
                             endpoint=True)
                smoothingScale = abs(DX[-1] - AX[0]) / smoothingSamples

                xlist, ylist, dxlist, dylist = PlotCurve.pointsToSmoothCurve(
                    xarray, yarray, samples, smoothingScale, False)

                pathdata = PlotCurve.formatPathdata(xlist, ylist,
                                                    dxlist, dylist,
                                                    PlotCoordinates(), False,
                                                    True)

                style = self.getStyleState()
                fillStyle = dict(
                    (x, style[x]) for x in style if x.startswith("fill"))
                fillStyle["stroke"] = "none"
                strokeStyle = dict(
                    (x, style[x]) for x in style if x.startswith("stroke"))

                if style["fill"] != "none" and len(pathdata) > 0:
                    if vertical:
                        firstPoint = plotCoordinates(Ax[0], 0.0)
                        lastPoint = plotCoordinates(Dx[-1], 0.0)
                    else:
                        firstPoint = plotCoordinates(0.0, Ay[0])
                        lastPoint = plotCoordinates(0.0, Dy[-1])

                    pathdata2 = [
                        "M %r %r" % firstPoint, pathdata[0].replace("M", "L")
                    ]
                    pathdata2.extend(pathdata[1:])
                    pathdata2.append(pathdata[-1])
                    pathdata2.append("L %r %r" % lastPoint)

                    output.append(
                        svg.path(d=" ".join(pathdata2),
                                 style=PlotStyle.toString(fillStyle)))

                output.append(
                    svg.path(d=" ".join(pathdata),
                             style=PlotStyle.toString(strokeStyle)))

            elif visualization == "points":
                currentStyle = PlotStyle.toDict(self.get("style") or {})
                style = self.getStyleState()
                if "fill" not in currentStyle:
                    style["fill"] = "black"

                BCX = NP(NP(BX + CX) / 2.0)
                BCY = NP(NP(BY + CY) / 2.0)

                svgId = self.get("svgId")
                if svgId is None:
                    svgIdMarker = plotDefinitions.uniqueName()
                else:
                    svgIdMarker = svgId + ".marker"

                marker = PlotScatter.makeMarker(
                    svgIdMarker, self.get("marker", defaultFromXsd=True),
                    style, self.childOfTag("PlotSvgMarker"))
                plotDefinitions[marker.get("id")] = marker

                markerReference = "#" + marker.get("id")
                output.extend(
                    svg.use(
                        **{
                            "x": repr(x),
                            "y": repr(y),
                            defs.XLINK_HREF: markerReference
                        }) for x, y in itertools.izip(BCX, BCY))

            else:
                raise NotImplementedError("TODO: add 'errorbars'")

        svgId = self.get("svgId")
        if svgId is not None:
            output["id"] = svgId

        performanceTable.end("PlotHistogram draw")
        return output
示例#2
0
    def draw(self, state, plotCoordinates, plotDefinitions, performanceTable):
        """Draw the plot element.

        This stage consists of creating an SVG image of the
        pre-computed data.

        @type state: ad-hoc Python object
        @param state: State information that persists long enough to use quantities computed in C{prepare} in the C{draw} stage.  This is a work-around of lxml's refusal to let its Python instances maintain C{self} and it is unrelated to DataTableState.
        @type plotCoordinates: PlotCoordinates
        @param plotCoordinates: The coordinate system in which this plot element will be placed.
        @type plotDefinitions: PlotDefinitions
        @type plotDefinitions: The dictionary of key-value pairs that forms the <defs> section of the SVG document.
        @type performanceTable: PerformanceTable
        @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process.
        @rtype: SvgBinding
        @return: An SVG fragment representing the fully drawn plot element.
        """

        svg = SvgBinding.elementMaker
        performanceTable.begin("PlotHistogram draw")

        cumulative = self.get("cumulative", defaultFromXsd=True, convertType=True)
        vertical = self.get("vertical", defaultFromXsd=True, convertType=True)
        visualization = self.get("visualization", defaultFromXsd=True)

        output = svg.g()
        if len(state.count) > 0:
            if state.fieldType is not self.fieldTypeNumeric:
                if vertical:
                    strings = plotCoordinates.xstrings
                else:
                    strings = plotCoordinates.ystrings

                newCount = []
                for string in strings:
                    try:
                        index = state.edges.index(string)
                    except ValueError:
                        newCount.append(0.0)
                    else:
                        newCount.append(state.count[index])

                state.count = newCount
                state.edges = [(i - 0.5, i + 0.5) for i in xrange(len(strings))]

            if vertical:
                Ax = NP("array", [low if low is not None else float("-inf") for low, high in state.edges], dtype=NP.dtype(float))
                Bx = NP(Ax.copy())
                Cx = NP("array", [high if high is not None else float("inf") for low, high in state.edges], dtype=NP.dtype(float))
                Dx = NP(Cx.copy())
                Ay = NP("zeros", len(state.count), dtype=NP.dtype(float))
                if cumulative:
                    Cy = NP("cumsum", NP("array", state.count, dtype=NP.dtype(float)))
                    By = NP("roll", Cy, 1)
                    By[0] = 0.0
                else:
                    By = NP("array", state.count, dtype=NP.dtype(float))
                    Cy = NP(By.copy())
                Dy = NP(Ay.copy())

            else:
                if cumulative:
                    Cx = NP("cumsum", NP("array", state.count, dtype=NP.dtype(float)))
                    Bx = NP("roll", Cx, 1)
                    Bx[0] = 0.0
                else:
                    Bx = NP("array", state.count, dtype=NP.dtype(float))
                    Cx = NP(Bx.copy())
                Ax = NP("zeros", len(state.count), dtype=NP.dtype(float))
                Dx = NP(Ax.copy())
                Ay = NP("array", [low if low is not None else float("-inf") for low, high in state.edges], dtype=NP.dtype(float))
                By = NP(Ay.copy())
                Cy = NP("array", [high if high is not None else float("inf") for low, high in state.edges], dtype=NP.dtype(float))
                Dy = NP(Cy.copy())

            AX, AY = plotCoordinates(Ax, Ay)
            BX, BY = plotCoordinates(Bx, By)
            CX, CY = plotCoordinates(Cx, Cy)
            DX, DY = plotCoordinates(Dx, Dy)

            if visualization == "skyline":
                gap = self.get("gap", defaultFromXsd=True, convertType=True)

                if vertical:
                    if gap > 0.0 and NP(NP(DX - gap/2.0) - NP(AX + gap/2.0)).min() > 0.0:
                        AX += gap/2.0
                        BX += gap/2.0
                        CX -= gap/2.0
                        DX -= gap/2.0
                else:
                    if gap > 0.0 and NP(NP(AY + gap/2.0) - NP(DY - gap/2.0)).min() > 0.0:
                        AY -= gap/2.0
                        BY -= gap/2.0
                        CY += gap/2.0
                        DY += gap/2.0

                pathdata = []
                nextIsMoveto = True
                for i in xrange(len(state.count)):
                    iprev = i - 1
                    inext = i + 1

                    if vertical and By[i] == 0.0 and Cy[i] == 0.0:
                        if i > 0 and not nextIsMoveto:
                            pathdata.append("L %r %r" % (DX[iprev], DY[iprev]))
                        nextIsMoveto = True

                    elif not vertical and Bx[i] == 0.0 and Cx[i] == 0.0:
                        if i > 0 and not nextIsMoveto:
                            pathdata.append("L %r %r" % (DX[iprev], DY[iprev]))
                        nextIsMoveto = True

                    else:
                        if nextIsMoveto or gap > 0.0 or (vertical and DX[iprev] != AX[i]) or (not vertical and DY[iprev] != AY[i]):
                            pathdata.append("M %r %r" % (AX[i], AY[i]))
                            nextIsMoveto = False

                        pathdata.append("L %r %r" % (BX[i], BY[i]))
                        pathdata.append("L %r %r" % (CX[i], CY[i]))

                        if i == len(state.count) - 1 or gap > 0.0 or (vertical and DX[i] != AX[inext]) or (not vertical and DY[i] != AY[inext]):
                            pathdata.append("L %r %r" % (DX[i], DY[i]))

                style = self.getStyleState()
                del style["marker-size"]
                del style["marker-outline"]
                output.append(svg.path(d=" ".join(pathdata), style=PlotStyle.toString(style)))

            elif visualization == "polyline":
                pathdata = []
                for i in xrange(len(state.count)):
                    if i == 0:
                        pathdata.append("M %r %r" % (AX[i], AY[i]))

                    pathdata.append("L %r %r" % ((BX[i] + CX[i])/2.0, (BY[i] + CY[i])/2.0))

                    if i == len(state.count) - 1:
                        pathdata.append("L %r %r" % (DX[i], DY[i]))

                style = self.getStyleState()
                del style["marker-size"]
                del style["marker-outline"]
                output.append(svg.path(d=" ".join(pathdata), style=PlotStyle.toString(style)))

            elif visualization == "smooth":
                smoothingSamples = math.ceil(len(state.count) / 2.0)

                BCX = NP(NP(BX + CX) / 2.0)
                BCY = NP(NP(BY + CY) / 2.0)

                xarray = NP("array", [AX[0]] + list(BCX) + [DX[-1]], dtype=NP.dtype(float))
                yarray = NP("array", [AY[0]] + list(BCY) + [DY[-1]], dtype=NP.dtype(float))
                samples = NP("linspace", AX[0], DX[-1], int(smoothingSamples), endpoint=True)
                smoothingScale = abs(DX[-1] - AX[0]) / smoothingSamples

                xlist, ylist, dxlist, dylist = PlotCurve.pointsToSmoothCurve(xarray, yarray, samples, smoothingScale, False)

                pathdata = PlotCurve.formatPathdata(xlist, ylist, dxlist, dylist, PlotCoordinates(), False, True)

                style = self.getStyleState()
                fillStyle = dict((x, style[x]) for x in style if x.startswith("fill"))
                fillStyle["stroke"] = "none"
                strokeStyle = dict((x, style[x]) for x in style if x.startswith("stroke"))

                if style["fill"] != "none" and len(pathdata) > 0:
                    if vertical:
                        firstPoint = plotCoordinates(Ax[0], 0.0)
                        lastPoint = plotCoordinates(Dx[-1], 0.0)
                    else:
                        firstPoint = plotCoordinates(0.0, Ay[0])
                        lastPoint = plotCoordinates(0.0, Dy[-1])
                        
                    pathdata2 = ["M %r %r" % firstPoint, pathdata[0].replace("M", "L")]
                    pathdata2.extend(pathdata[1:])
                    pathdata2.append(pathdata[-1])
                    pathdata2.append("L %r %r" % lastPoint)

                    output.append(svg.path(d=" ".join(pathdata2), style=PlotStyle.toString(fillStyle)))

                output.append(svg.path(d=" ".join(pathdata), style=PlotStyle.toString(strokeStyle)))

            elif visualization == "points":
                currentStyle = PlotStyle.toDict(self.get("style") or {})
                style = self.getStyleState()
                if "fill" not in currentStyle:
                    style["fill"] = "black"

                BCX = NP(NP(BX + CX) / 2.0)
                BCY = NP(NP(BY + CY) / 2.0)

                svgId = self.get("svgId")
                if svgId is None:
                    svgIdMarker = plotDefinitions.uniqueName()
                else:
                    svgIdMarker = svgId + ".marker"

                marker = PlotScatter.makeMarker(svgIdMarker, self.get("marker", defaultFromXsd=True), style, self.childOfTag("PlotSvgMarker"))
                plotDefinitions[marker.get("id")] = marker

                markerReference = "#" + marker.get("id")
                output.extend(svg.use(**{"x": repr(x), "y": repr(y), defs.XLINK_HREF: markerReference}) for x, y in itertools.izip(BCX, BCY))
                
            else:
                raise NotImplementedError("TODO: add 'errorbars'")

        svgId = self.get("svgId")
        if svgId is not None:
            output["id"] = svgId

        performanceTable.end("PlotHistogram draw")
        return output