def draw(self, dataTable, functionTable, performanceTable, rowIndex, colIndex, cellContents, labelAttributes, plotDefinitions): """Draw the plot legend content, which is more often text than graphics. @type dataTable: DataTable @param dataTable: Contains the data to describe, if any. @type functionTable: FunctionTable @param functionTable: Defines functions that may be used to transform data. @type performanceTable: PerformanceTable @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process. @type rowIndex: int @param rowIndex: Row number of the C{cellContents} to fill. @type colIndex: int @param colIndex: Column number of the C{cellContents} to fill. @type cellContents: dict @param cellContents: Dictionary that maps pairs of integers to SVG graphics to draw. @type labelAttributes: CSS style dict @param labelAttributes: Style properties that are defined at the level of the legend and must percolate down to all drawables within the legend. @type plotDefinitions: PlotDefinitions @type plotDefinitions: The dictionary of key-value pairs that forms the <defs> section of the SVG document. @rtype: 2-tuple @return: The next C{rowIndex} and C{colIndex} in the sequence. """ svg = SvgBinding.elementMaker svgId = self.get("svgId") if svgId is None: output = svg.g() else: output = svg.g(**{"id": svgId}) inlineSvg = self.getchildren() fileName = self.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = copy.deepcopy(inlineSvg[0]) elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError("PlotLegendSvg should specify an inline SVG or a fileName but not both or neither") sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) nominalHeight = sy2 - sy1 nominalWidth = sx2 - sx1 # TODO: set this correctly from the text height rowHeight = 30.0 # output["transform"] = "translate(%r, %r) scale(%r, %r)" % (-sx1, -sy1, rowHeight/float(sx2 - sx1), rowHeight/float(sy2 - sy1)) output["transform"] = "translate(%r, %r) scale(%r, %r)" % (-sx1 - 0.5*nominalWidth*rowHeight/nominalHeight, -sy1 - 0.75*rowHeight, rowHeight/nominalHeight, rowHeight/nominalHeight) output.append(svgBinding) cellContents[rowIndex, colIndex] = svg.g(output) cellContents[rowIndex, colIndex].text = " " # TODO: set the width correctly, too colIndex += 1 return rowIndex, colIndex
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 x1 = float(self["x1"]) y1 = float(self["y1"]) x2 = float(self["x2"]) y2 = float(self["y2"]) inlineSvg = self.getchildren() fileName = self.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = inlineSvg[0] elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError( "PlotSvgContent should specify an inline SVG or a fileName but not both or neither" ) sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) subCoordinates = PlotCoordinatesWindow(plotCoordinates, sx1, sy1, sx2, sy2, x1, y1, x2 - x1, y2 - y1) tx0, ty0 = subCoordinates(0.0, 0.0) tx1, ty1 = subCoordinates(1.0, 1.0) transform = "translate(%r, %r) scale(%r, %r)" % (tx0, ty0, tx1 - tx0, ty1 - ty0) attribs = {"transform": transform} svgId = self.get("svgId") if svgId is not None: attribs["id"] = svgId if "style" in svgBinding.attrib: attribs["style"] = svgBinding.attrib["style"] return svg.g(*(copy.deepcopy(svgBinding).getchildren()), **attribs)
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 x1 = float(self["x1"]) y1 = float(self["y1"]) x2 = float(self["x2"]) y2 = float(self["y2"]) inlineSvg = self.getchildren() fileName = self.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = inlineSvg[0] elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError("PlotSvgContent should specify an inline SVG or a fileName but not both or neither") sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) subCoordinates = PlotCoordinatesWindow(plotCoordinates, sx1, sy1, sx2, sy2, x1, y1, x2 - x1, y2 - y1) tx0, ty0 = subCoordinates(0.0, 0.0) tx1, ty1 = subCoordinates(1.0, 1.0) transform = "translate(%r, %r) scale(%r, %r)" % (tx0, ty0, tx1 - tx0, ty1 - ty0) attribs = {"transform": transform} svgId = self.get("svgId") if svgId is not None: attribs["id"] = svgId if "style" in svgBinding.attrib: attribs["style"] = svgBinding.attrib["style"] return svg.g(*(copy.deepcopy(svgBinding).getchildren()), **attribs)
def makeMarker(svgIdMarker, marker, style, plotSvgMarker): """Construct a marker from a set of known shapes or an SVG pictogram. @type svgIdMarker: string @param svgIdMarker: SVG id for the new marker. @type marker: string @param marker: Name of the marker shape; must be one of PLOT-MARKER-TYPE. @type style: dict @param style: CSS style for the marker in dictionary form. @type plotSvgMarker: PmmlBinding or None @param plotSvgMarker: A PlotSvgMarker element, which either contains an inline SvgBinding or a fileName pointing to an external image. @rtype: SvgBinding @return: The marker image, appropriate for adding to a PlotDefinitions. """ svg = SvgBinding.elementMaker style["stroke"] = style["marker-outline"] del style["marker-outline"] markerSize = float(style["marker-size"]) del style["marker-size"] if marker == "circle": return svg.circle(id=svgIdMarker, cx="0", cy="0", r=repr(markerSize), style=PlotStyle.toString(style)) elif marker == "square": p = markerSize m = -markerSize return svg.path(id=svgIdMarker, d="M %r,%r L %r,%r L %r,%r L %r,%r z" % (m,m, p,m, p,p, m,p), style=PlotStyle.toString(style)) elif marker == "diamond": p = math.sqrt(2.0) * markerSize m = -math.sqrt(2.0) * markerSize return svg.path(id=svgIdMarker, d="M %r,0 L 0,%r L %r,0 L 0,%r z" % (m, m, p, p), style=PlotStyle.toString(style)) elif marker == "plus": p = markerSize m = -markerSize if style["stroke"] == "none": style["stroke"] = style["fill"] style["fill"] = "none" return svg.path(id=svgIdMarker, d="M %r,0 L %r,0 M 0,%r L 0,%r" % (m, p, m, p), style=PlotStyle.toString(style)) elif marker == "times": p = math.sqrt(2.0) * markerSize m = -math.sqrt(2.0) * markerSize if style["stroke"] == "none": style["stroke"] = style["fill"] style["fill"] = "none" return svg.path(id=svgIdMarker, d="M %r,%r L %r,%r M %r,%r L %r,%r" % (m,m, p,p, p,m, m,p), style=PlotStyle.toString(style)) elif marker == "svg": if plotSvgMarker is None: raise defs.PmmlValidationError("When marker is \"svg\", a PlotSvgMarker must be provided") inlineSvg = plotSvgMarker.getchildren() fileName = plotSvgMarker.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = inlineSvg[0] elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError("PlotSvgMarker should specify an inline SVG or a fileName but not both or neither") sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) tx1, ty1 = -markerSize, -markerSize tx2, ty2 = markerSize, markerSize transform = "translate(%r, %r) scale(%r, %r)" % (tx1 - sx1, ty1 - sy1, (tx2 - tx1)/float(sx2 - sx1), (ty2 - ty1)/float(sy2 - sy1)) return svg.g(copy.deepcopy(svgBinding), id=svgIdMarker, transform=transform)
def draw(self, dataTable, functionTable, performanceTable, plotCoordinates, plotContentBox, plotDefinitions): """Draw the plot annotation. @type dataTable: DataTable @param dataTable: Contains the data to plot, if any. @type functionTable: FunctionTable @param functionTable: Defines functions that may be used to transform data for plotting. @type performanceTable: PerformanceTable @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process. @type plotCoordinates: PlotCoordinates @param plotCoordinates: The coordinate system in which this plot will be placed. @type plotContentBox: PlotContentBox @param plotContentBox: A bounding box in which this plot will be placed. @type plotDefinitions: PlotDefinitions @type plotDefinitions: The dictionary of key-value pairs that forms the <defs> section of the SVG document. @rtype: SvgBinding @return: An SVG fragment representing the fully drawn plot. """ svg = SvgBinding.elementMaker svgId = self.get("svgId") if svgId is None: output = svg.g() else: output = svg.g(**{"id": svgId}) content = [output] inlineSvg = self.getchildren() fileName = self.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = inlineSvg[0] elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError( "PlotSvgAnnotation should specify an inline SVG or a fileName but not both or neither" ) style = self.getStyleState() if style.get("margin-bottom") == "auto": del style["margin-bottom"] if style.get("margin-top") == "auto": del style["margin-top"] if style.get("margin-left") == "auto": del style["margin-left"] if style.get("margin-right") == "auto": del style["margin-right"] subContentBox = plotContentBox.subContent(style) sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) nominalHeight = sy2 - sy1 nominalWidth = sx2 - sx1 if nominalHeight < subContentBox.height: if "margin-bottom" in style and "margin-top" in style: pass elif "margin-bottom" in style: style["margin-top"] = subContentBox.height - nominalHeight elif "margin-top" in style: style["margin-bottom"] = subContentBox.height - nominalHeight else: style["margin-bottom"] = style["margin-top"] = ( subContentBox.height - nominalHeight) / 2.0 if nominalWidth < subContentBox.width: if "margin-left" in style and "margin-right" in style: pass elif "margin-left" in style: style["margin-right"] = subContentBox.width - nominalWidth elif "margin-right" in style: style["margin-left"] = subContentBox.width - nominalWidth else: style["margin-left"] = style["margin-right"] = ( subContentBox.width - nominalWidth) / 2.0 subContentBox = plotContentBox.subContent(style) borderRect = plotContentBox.border(style) if subContentBox is not None: tx1, ty1 = plotCoordinates(subContentBox.x, subContentBox.y) tx2, ty2 = plotCoordinates(subContentBox.x + subContentBox.width, subContentBox.y + subContentBox.height) output.extend([copy.deepcopy(x) for x in svgBinding.getchildren()]) output["transform"] = "translate(%r, %r) scale(%r, %r)" % ( tx1 - sx1, ty1 - sy1, (tx2 - tx1) / float(sx2 - sx1), (ty2 - ty1) / float(sy2 - sy1)) if borderRect is not None: rectStyle = {"stroke": style["border-color"]} if rectStyle["stroke"] != "none": for styleProperty in "border-dasharray", "border-dashoffset", "border-linecap", "border-linejoin", "border-miterlimit", "border-opacity", "border-width": if styleProperty in style: rectStyle[styleProperty.replace( "border-", "stroke-")] = style[styleProperty] x1 = borderRect.x y1 = borderRect.y x2 = borderRect.x + borderRect.width y2 = borderRect.y + borderRect.height x1, y1 = plotCoordinates(x1, y1) x2, y2 = plotCoordinates(x2, y2) subAttrib = { "x": repr(x1), "y": repr(y1), "width": repr(x2 - x1), "height": repr(y2 - y1), "style": PlotStyle.toString(rectStyle) } subAttrib["style"] = PlotStyle.toString(rectStyle) if svgId is not None: subAttrib["id"] = svgId + ".border" content.append(svg.rect(**subAttrib)) return svg.g(*content)
def draw(self, dataTable, functionTable, performanceTable, plotCoordinates, plotContentBox, plotDefinitions): """Draw the plot annotation. @type dataTable: DataTable @param dataTable: Contains the data to plot, if any. @type functionTable: FunctionTable @param functionTable: Defines functions that may be used to transform data for plotting. @type performanceTable: PerformanceTable @param performanceTable: Measures and records performance (time and memory consumption) of the drawing process. @type plotCoordinates: PlotCoordinates @param plotCoordinates: The coordinate system in which this plot will be placed. @type plotContentBox: PlotContentBox @param plotContentBox: A bounding box in which this plot will be placed. @type plotDefinitions: PlotDefinitions @type plotDefinitions: The dictionary of key-value pairs that forms the <defs> section of the SVG document. @rtype: SvgBinding @return: An SVG fragment representing the fully drawn plot. """ svg = SvgBinding.elementMaker svgId = self.get("svgId") if svgId is None: output = svg.g() else: output = svg.g(**{"id": svgId}) content = [output] inlineSvg = self.getchildren() fileName = self.get("fileName") if len(inlineSvg) == 1 and fileName is None: svgBinding = inlineSvg[0] elif len(inlineSvg) == 0 and fileName is not None: svgBinding = SvgBinding.loadXml(fileName) else: raise defs.PmmlValidationError( "PlotSvgAnnotation should specify an inline SVG or a fileName but not both or neither" ) style = self.getStyleState() if style.get("margin-bottom") == "auto": del style["margin-bottom"] if style.get("margin-top") == "auto": del style["margin-top"] if style.get("margin-left") == "auto": del style["margin-left"] if style.get("margin-right") == "auto": del style["margin-right"] subContentBox = plotContentBox.subContent(style) sx1, sy1, sx2, sy2 = PlotSvgAnnotation.findSize(svgBinding) nominalHeight = sy2 - sy1 nominalWidth = sx2 - sx1 if nominalHeight < subContentBox.height: if "margin-bottom" in style and "margin-top" in style: pass elif "margin-bottom" in style: style["margin-top"] = subContentBox.height - nominalHeight elif "margin-top" in style: style["margin-bottom"] = subContentBox.height - nominalHeight else: style["margin-bottom"] = style["margin-top"] = (subContentBox.height - nominalHeight) / 2.0 if nominalWidth < subContentBox.width: if "margin-left" in style and "margin-right" in style: pass elif "margin-left" in style: style["margin-right"] = subContentBox.width - nominalWidth elif "margin-right" in style: style["margin-left"] = subContentBox.width - nominalWidth else: style["margin-left"] = style["margin-right"] = (subContentBox.width - nominalWidth) / 2.0 subContentBox = plotContentBox.subContent(style) borderRect = plotContentBox.border(style) if subContentBox is not None: tx1, ty1 = plotCoordinates(subContentBox.x, subContentBox.y) tx2, ty2 = plotCoordinates(subContentBox.x + subContentBox.width, subContentBox.y + subContentBox.height) output.extend([copy.deepcopy(x) for x in svgBinding.getchildren()]) output["transform"] = "translate(%r, %r) scale(%r, %r)" % ( tx1 - sx1, ty1 - sy1, (tx2 - tx1) / float(sx2 - sx1), (ty2 - ty1) / float(sy2 - sy1), ) if borderRect is not None: rectStyle = {"stroke": style["border-color"]} if rectStyle["stroke"] != "none": for styleProperty in ( "border-dasharray", "border-dashoffset", "border-linecap", "border-linejoin", "border-miterlimit", "border-opacity", "border-width", ): if styleProperty in style: rectStyle[styleProperty.replace("border-", "stroke-")] = style[styleProperty] x1 = borderRect.x y1 = borderRect.y x2 = borderRect.x + borderRect.width y2 = borderRect.y + borderRect.height x1, y1 = plotCoordinates(x1, y1) x2, y2 = plotCoordinates(x2, y2) subAttrib = { "x": repr(x1), "y": repr(y1), "width": repr(x2 - x1), "height": repr(y2 - y1), "style": PlotStyle.toString(rectStyle), } subAttrib["style"] = PlotStyle.toString(rectStyle) if svgId is not None: subAttrib["id"] = svgId + ".border" content.append(svg.rect(**subAttrib)) return svg.g(*content)