# HG changeset patch # User Paul Boddie # Date 1318116641 -7200 # Node ID 95024a39b9770d1ab6a0f458b9ac001a68f33c05 # Parent b0f8e8bead248607ecc9fd7b66dbbf24e0174197 Moved various dimension and data property initialisation tasks into the Plot class. Added support for calculating the chart's dimensions separately so that the transclusion can include calculated width and height attributes. Changed get_chart to return the calculated width and height as well as the chart itself. diff -r b0f8e8bead24 -r 95024a39b977 SVGChartSupport.py --- a/SVGChartSupport.py Sat Oct 08 22:41:09 2011 +0200 +++ b/SVGChartSupport.py Sun Oct 09 01:30:41 2011 +0200 @@ -55,13 +55,50 @@ "Support for plotting points and lines." - def __init__(self, xmin, ymin, xmax, ymax, xmultiplier, ymultiplier): - self.xmin = xmin - self.ymin = ymin - self.xmax = xmax - self.ymax = ymax - self.xmultiplier = xmultiplier - self.ymultiplier = ymultiplier + def __init__(self, data, xorigin, yorigin, xdivisions, ydivisions): + + # Get the data properties. + + min_x = min([t[0] for t in data]) + min_y = min([t[1] for t in data]) + max_x = max([t[0] for t in data]) + max_y = max([t[1] for t in data]) + + # Get the chart properties. + + xinterval = math.pow(10, math.floor(math.log10(max(abs(max_x), abs(min_x))))) + yinterval = math.pow(10, math.floor(math.log10(max(abs(max_y), abs(min_y))))) + + xstep = float(xinterval) / xdivisions + ystep = float(yinterval) / ydivisions + + # Store various attributes. + + self.xmin, self.xmax = round(min_x, xinterval, -1), round(max_x, xinterval) + self.ymin, self.ymax = round(min_y, yinterval, -1), round(max_y, yinterval) + + self.xsequence = frange(min(xorigin, self.xmin), max(xorigin, self.xmax) + xstep, xstep) + self.ysequence = frange(min(xorigin, self.ymin), max(yorigin, self.ymax) + ystep, ystep) + + self.xmultiplier = float(self.ymax) / self.xmax + self.ymultiplier = -1 + + # Work out the extent of the chart. + + self.left = min(self.get_xbase(), self.scale_x(xorigin)) - self.scaled_xpc(40) + self.left_to_right = self.scaled_xpc(180) + self.bottom = min(self.get_ybase(), self.scale_y(yorigin)) - self.scaled_ypc(10) + self.bottom_to_top = self.scaled_ypc(180) + + def get_dimensions(self, chart_width=None, chart_height=None): + if chart_width is not None: + if chart_height is None: + chart_height = chart_width / self.left_to_right * self.bottom_to_top + else: + if chart_height is not None: + chart_width = chart_height / self.bottom_to_top * self.left_to_right + + return map(int, (chart_width, chart_height)) def get_width(self): return abs(self.xmax - self.xmin) @@ -374,42 +411,27 @@ value += step return l -def get_chart(data, chart_width=900, chart_height=None, xorigin=0, yorigin=0, xdivisions=10, ydivisions=10, encoding="utf-8", - styles_url=""): - +def convert_data(data): new_data = [] for t in data: x, y = t[:2] new_data.append([float(x), float(y)] + t[2:]) - data = new_data - - # Get the data properties. + return new_data - min_x = min([t[0] for t in data]) - min_y = min([t[1] for t in data]) - max_x = max([t[0] for t in data]) - max_y = max([t[1] for t in data]) - - # Get the chart properties. +def get_dimensions(data, chart_width=900, chart_height=None, xorigin=0, yorigin=0, xdivisions=10, ydivisions=10): - xinterval = math.pow(10, math.floor(math.log10(max(abs(max_x), abs(min_x))))) - yinterval = math.pow(10, math.floor(math.log10(max(abs(max_y), abs(min_y))))) - - xmin, xmax = round(min_x, xinterval, -1), round(max_x, xinterval) - ymin, ymax = round(min_y, yinterval, -1), round(max_y, yinterval) + data = convert_data(data) + plot = Plot(data, xorigin, yorigin, xdivisions, ydivisions) + return plot.get_dimensions(chart_width, chart_height) - xstep = float(xinterval) / xdivisions - ystep = float(yinterval) / ydivisions +def get_chart(data, chart_width=900, chart_height=None, xorigin=0, yorigin=0, xdivisions=10, ydivisions=10, encoding="utf-8", + styles_url=""): - xsequence = frange(min(xorigin, xmin), max(xorigin, xmax) + xstep, xstep) - ysequence = frange(min(xorigin, ymin), max(yorigin, ymax) + ystep, ystep) - - xmultiplier = float(ymax) / xmax - ymultiplier = -1 + data = convert_data(data) # Initialise the chart components. - plot = Plot(xmin, ymin, xmax, ymax, xmultiplier, ymultiplier) + plot = Plot(data, xorigin, yorigin, xdivisions, ydivisions) axis_label_x = plot.xpc(20) axis_label_y = plot.ypc(15) @@ -417,8 +439,8 @@ width = plot.get_width() height = plot.get_height() - x_axis = Axis(plot, 0, yorigin, xsequence, xdivisions, plot.ypc(0.5), plot.ypc(1)) - y_axis = Axis(plot, 1, xorigin, ysequence, ydivisions, plot.xpc(0.5), plot.xpc(1)) + x_axis = Axis(plot, 0, yorigin, plot.xsequence, xdivisions, plot.ypc(0.5), plot.ypc(1)) + y_axis = Axis(plot, 1, xorigin, plot.ysequence, ydivisions, plot.xpc(0.5), plot.xpc(1)) # Render the chart. @@ -471,27 +493,15 @@ all_elements.append(get_labelled_points(plot, x_axis, y_axis, points, labelx, labely, font_height, axis_label_x, axis_label_y, attributes=styles)) - # Work out the extent of the chart. - - left = min(plot.get_xbase(), plot.scale_x(xorigin)) - plot.scaled_xpc(40) - left_to_right = plot.scaled_xpc(180) - bottom = min(plot.get_ybase(), plot.scale_y(yorigin)) - plot.scaled_ypc(10) - bottom_to_top = plot.scaled_ypc(180) - - if chart_width is not None: - if chart_height is None: - chart_height = chart_width / left_to_right * bottom_to_top - else: - if chart_height is not None: - chart_width = chart_height / bottom_to_top * left_to_right + chart_width, chart_height = plot.get_dimensions(chart_width, chart_height) # Write the SVG file. - return template % { - "width" : int(chart_width), - "height" : int(chart_height), + return chart_width, chart_height, template % { + "width" : chart_width, + "height" : chart_height, "viewBox" : "%d %d %d %d" % ( - left, bottom, left_to_right, bottom_to_top, + plot.left, plot.bottom, plot.left_to_right, plot.bottom_to_top, ), "chart" : "".join(all_elements), "styles_url" : styles_url, diff -r b0f8e8bead24 -r 95024a39b977 parsers/SVGChart.py --- a/parsers/SVGChart.py Sat Oct 08 22:41:09 2011 +0200 +++ b/parsers/SVGChart.py Sun Oct 09 01:30:41 2011 +0200 @@ -8,7 +8,7 @@ from MoinMoin.action import cache from MoinMoin import wikiutil -from SVGChartSupport import get_chart +from SVGChartSupport import get_chart, get_dimensions Dependencies = ["pages"] @@ -36,8 +36,13 @@ # The attributes returned from the formatting arguments are encoded like # strings. - self.xorigin = float((attrs.get("xorigin") or '"0"')[1:-1]) - self.yorigin = float((attrs.get("yorigin") or '"0"')[1:-1]) + quotes = '"' + "'" + self.xorigin = float((attrs.get("xorigin") or '0').strip(quotes)) + self.yorigin = float((attrs.get("yorigin") or '0').strip(quotes)) + width = (attrs.get("width") or '').strip(quotes) or None + height = (attrs.get("height") or '').strip(quotes) or None + self.width = width and int(width) + self.height = height and int(height) def format(self, fmt): @@ -47,16 +52,18 @@ page = request.page _ = request.getText - # NOTE: Store and retrieve the width and height. + cache_key = cache.key(request, itemname=page.page_name, content="%s,%s,%s,%s,%s" % ( + self.width, self.height, self.xorigin, self.yorigin, self.raw)) - cache_key = cache.key(request, itemname=page.page_name, content="%s,%s,%s" % (self.xorigin, self.yorigin, self.raw)) if not cache.exists(request, cache_key): - chart = get_chart(self.data, xorigin=self.xorigin, yorigin=self.yorigin, + width, height, chart = get_chart(self.data, self.width, self.height, self.xorigin, self.yorigin, styles_url="%s/%s/css/svgchart.css" % (request.cfg.url_prefix_static, request.theme.name)) cache.put(request, cache_key, chart, content_type="image/svg+xml") + else: + width, height = get_dimensions(self.data, self.width, self.height, self.xorigin, self.yorigin) request.write(fmt.div(1, css_class="svgchart")) - request.write(fmt.transclusion(1, data=cache.url(request, cache_key))) + request.write(fmt.transclusion(1, data=cache.url(request, cache_key), width=width, height=height)) request.write(fmt.text(_("SVG chart of CSV data."))) request.write(fmt.transclusion(0)) request.write(fmt.div(0))