def transformLabel(x, label): y = (x - lowSecond)*scale + low if x == minimumTick: newLabel = PlotNumberFormat.toUnicode(x, lowSecond, highSecond, tryPrecision=True) leadingZeros = re.match("^([0-9]*)", newLabel, re.UNICODE).group(1) if len(leadingZeros) < 2: newLabel = (u"0" * (2 - len(leadingZeros))) + newLabel return y, u"%02d:%02d:%s" % (lowDateTime.hour, lowDateTime.minute, newLabel) else: newLabel = PlotNumberFormat.toUnicode(x, lowSecond, highSecond, tryPrecision=True) return y, ":" + newLabel
def transformLabel(x, label): y = (x - lowSecond) * scale + low if x == minimumTick: newLabel = PlotNumberFormat.toUnicode( x, lowSecond, highSecond, tryPrecision=True) leadingZeros = re.match("^([0-9]*)", newLabel, re.UNICODE).group(1) if len(leadingZeros) < 2: newLabel = (u"0" * (2 - len(leadingZeros))) + newLabel return y, u"%02d:%02d:%s" % ( lowDateTime.hour, lowDateTime.minute, newLabel) else: newLabel = PlotNumberFormat.toUnicode( x, lowSecond, highSecond, tryPrecision=True) return y, ":" + newLabel
def _computeLogticks(low, high, base, N): eps = (high / low)**defs.EPSILON low, high = low / eps, high * eps if N >= 0: output = {} x = low for i in xrange(N): output[x] = PlotNumberFormat.toUnicode(x) x += (high - low) / (N - 1.0) return output N = -N lowN = math.floor(math.log(low, base)) highN = math.ceil(math.log(high, base)) output = {} for n in range(long(lowN), long(highN) + 1): x = base**n label = PlotNumberFormat.toUnicode(x) if low <= x <= high: output[x] = label for i in xrange(1, len(output)): keys = output.keys() keys.sort() keys = keys[::i] values = map(lambda k: output[k], keys) if len(values) <= N: for k in output.keys(): if k not in keys: output[k] = u"" break if len(output) <= 2: output2 = PlotTickMarks._computeTicks(low, high, -long(math.ceil(N / 2.0))) lowest = min(output2) for k in output: if k < lowest: output2[k] = output[k] output = output2 return output
def _computeLogticks(low, high, base, N): eps = (high/low)**defs.EPSILON low, high = low/eps, high*eps if N >= 0: output = {} x = low for i in xrange(N): output[x] = PlotNumberFormat.toUnicode(x) x += (high - low)/(N - 1.0) return output N = -N lowN = math.floor(math.log(low, base)) highN = math.ceil(math.log(high, base)) output = {} for n in range(long(lowN), long(highN)+1): x = base**n label = PlotNumberFormat.toUnicode(x) if low <= x <= high: output[x] = label for i in xrange(1, len(output)): keys = output.keys() keys.sort() keys = keys[::i] values = map(lambda k: output[k], keys) if len(values) <= N: for k in output.keys(): if k not in keys: output[k] = u"" break if len(output) <= 2: output2 = PlotTickMarks._computeTicks(low, high, -long(math.ceil(N/2.0))) lowest = min(output2) for k in output: if k < lowest: output2[k] = output[k] output = output2 return output
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 performanceTable.begin("PlotLegendNumber") myLabelAttributes = dict(labelAttributes) style = PlotStyle.toDict(myLabelAttributes["style"]) style.update(self.getStyleState()) myLabelAttributes["style"] = PlotStyle.toString(style) myLabelAttributes["font-size"] = style["font-size"] svgId = self.get("svgId") if svgId is not None: myLabelAttributes["id"] = svgId try: float(self.text) except (ValueError, TypeError): self.text = "0" digits = self.get("digits") if digits is not None: astext = PlotNumberFormat.roundDigits(float(self.text), int(digits)) else: astext = PlotNumberFormat.toUnicode(self.text) cellContents[rowIndex, colIndex] = svg.text(astext, **myLabelAttributes) colIndex += 1 performanceTable.end("PlotLegendNumber") return rowIndex, colIndex
def transformLabel(x, label): y = (x - lowSecond)*scale + low newLabel = PlotNumberFormat.toUnicode(x % 60, lowSecond, highSecond) leadingZeros = re.match("^([0-9]*)", newLabel, re.UNICODE).group(1) if len(leadingZeros) < 2: newLabel = (u"0" * (2 - len(leadingZeros))) + newLabel if x == minimumNewMinute: return y, u"%02d:%02d:%s" % (highDateTime.hour, highDateTime.minute, newLabel) else: return y, ":" + newLabel
def transformLabel(x, label): y = (x - lowSecond) * scale + low newLabel = PlotNumberFormat.toUnicode( x % 60, lowSecond, highSecond) leadingZeros = re.match("^([0-9]*)", newLabel, re.UNICODE).group(1) if len(leadingZeros) < 2: newLabel = (u"0" * (2 - len(leadingZeros))) + newLabel if x == minimumNewMinute: return y, u"%02d:%02d:%s" % ( highDateTime.hour, highDateTime.minute, newLabel) else: return y, ":" + newLabel
def interpret(tickSpecification, low, high): """Interpret a tick specification string and generate tickmarks and mini-tickmarks. A tick specification string can be formed like one of the following: - C{linear(~N)}: approximately N major ticks; mini-ticks fill in between. - C{linear(N)}: exactly N major ticks; mini-ticks fill in between. - C{log(~N)}: approximately N major logarithmic ticks (base 10); mini-ticks fill in between. - C{logB(~N)}: approximately N major logarithmic ticks (base B); mini-ticks fill in between. - C{explicit({#: "num1", #: "num2"})}: an explicit set of ticks as a dictionary that maps locations (numbers) to display values (strings); this does not generate mini-ticks. - C{explicit([#, #, #, #])}: an explicit set of ticks as a list of locations (numbers); the display values are generated from the numbers. - C{fillmini(...)}: an explicit set of ticks, following the same convention as C{explicit}, but filled in with mini-ticks. - C{time()}: temporally meaningful ticks, automatically chosen based on the range. The output is a C{ticks, miniticks} pair, where C{ticks} is a dictionary mapping locations (numbers) to display values (strings) and C{miniticks} is a list of locations. @type tickSpecification: string @param tickSpecification: As described above. @type low: number @param low: Minimum edge of the plot window to mark with ticks. @type high: number @param high: Maximum edge of the plot window to mark with ticks. @rtype: 2-tuple of C{ticks}, C{miniticks} @return: As described above. @raise ValueError: If the tick specification is not well formed, this function raises an error. """ if low >= high: raise ValueError, "low must be strictly less than high" m = re.match("^\s*linear\s*\(\s*(~?)([0-9]+)\s*\)\s*$", tickSpecification) if m is not None: tilde, N = m.group(1), int(m.group(2)) if N < 2: raise ValueError("N must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if tilde == "~": N = -N ticks = PlotTickMarks._computeTicks(low, high, N) miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) return ticks, miniticks m = re.match("^\s*log([0-9]*)\s*\(\s*(~?)([0-9]+)\s*\)\s*$", tickSpecification) if m is not None: base, tilde, N = m.group(1), m.group(2), int(m.group(3)) if base == "": base = 10 else: base = int(base) if base < 2: raise ValueError("log base must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if N < 2: raise ValueError("N must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if tilde == "~": N = -N if low <= 0.0 or high <= 0.0: # fallback (might be encountered in production, so don't raise an error) ticks = PlotTickMarks._computeTicks(low, high, N) miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) return ticks, miniticks else: ticks = PlotTickMarks._computeLogticks(low, high, base, N) miniticks = PlotTickMarks._computeLogminiticks(low, high, base) return ticks, miniticks m = re.match("^\s*(explicit|fillmini)\s*\((.*)\)$", tickSpecification) if m is not None and m.group(1) in ("explicit", "fillmini"): spec = m.group(2) if spec[0] == "[": try: spec = json.loads(spec) spec = map(float, spec) low, high = min(spec), max(spec) ticks = dict((x, PlotNumberFormat.toUnicode(x, low, high)) for x in spec) if m.group(1) == "fillmini": miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) else: miniticks = [] return ticks, miniticks except ValueError: pass elif spec[0] == "{": try: ticks = {} for x, label in json.loads(re.sub(r"\b([+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)\s*:", r'"\1":', spec)).items(): if isinstance(label, (float, int, long)): label = repr(label) if not isinstance(label, basestring): raise ValueError ticks[float(x)] = label if m.group(1) == "fillmini": miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) else: miniticks = [] return ticks, miniticks except ValueError, AttributeError: pass
def _computeTicks(low, high, N): eps = defs.EPSILON * (high - low) low, high = low - eps, high + eps if N >= 0: output = {} x = low for i in xrange(N): if abs(x) < eps: label = u"0" else: label = PlotNumberFormat.toUnicode(x, low, high) output[x] = label x += (high - low)/(N - 1.0) return output N = -N counter = 0 granularity = 10**math.ceil(math.log10(max(abs(low), abs(high)))) lowN = math.ceil(low / granularity) highN = math.floor(high / granularity) while lowN > highN: countermod3 = counter % 3 if countermod3 == 0: granularity *= 0.5 elif countermod3 == 1: granularity *= 0.4 else: granularity *= 0.5 counter += 1 lowN = math.ceil(low / granularity) highN = math.floor(high / granularity) lastGranularity = granularity lastTrial = None while True: trial = {} for n in range(long(lowN), long(highN)+1): x = n * granularity if abs(x) < eps: label = u"0" else: label = PlotNumberFormat.toUnicode(x, low, high) trial[x] = label if long(highN)+1 - long(lowN) >= N: if lastTrial is None: v1, v2 = low, high return {v1: PlotNumberFormat.toUnicode(v1, low, high), v2: PlotNumberFormat.toUnicode(v2, low, high)} else: return lastTrial lastGranularity = granularity lastTrial = trial countermod3 = counter % 3 if countermod3 == 0: granularity *= 0.5 elif countermod3 == 1: granularity *= 0.4 else: granularity *= 0.5 counter += 1 lowN = math.ceil(low / granularity) highN = math.floor(high / granularity)
def interpret(tickSpecification, low, high): """Interpret a tick specification string and generate tickmarks and mini-tickmarks. A tick specification string can be formed like one of the following: - C{linear(~N)}: approximately N major ticks; mini-ticks fill in between. - C{linear(N)}: exactly N major ticks; mini-ticks fill in between. - C{log(~N)}: approximately N major logarithmic ticks (base 10); mini-ticks fill in between. - C{logB(~N)}: approximately N major logarithmic ticks (base B); mini-ticks fill in between. - C{explicit({#: "num1", #: "num2"})}: an explicit set of ticks as a dictionary that maps locations (numbers) to display values (strings); this does not generate mini-ticks. - C{explicit([#, #, #, #])}: an explicit set of ticks as a list of locations (numbers); the display values are generated from the numbers. - C{fillmini(...)}: an explicit set of ticks, following the same convention as C{explicit}, but filled in with mini-ticks. - C{time()}: temporally meaningful ticks, automatically chosen based on the range. The output is a C{ticks, miniticks} pair, where C{ticks} is a dictionary mapping locations (numbers) to display values (strings) and C{miniticks} is a list of locations. @type tickSpecification: string @param tickSpecification: As described above. @type low: number @param low: Minimum edge of the plot window to mark with ticks. @type high: number @param high: Maximum edge of the plot window to mark with ticks. @rtype: 2-tuple of C{ticks}, C{miniticks} @return: As described above. @raise ValueError: If the tick specification is not well formed, this function raises an error. """ if low >= high: raise ValueError, "low must be strictly less than high" m = re.match("^\s*linear\s*\(\s*(~?)([0-9]+)\s*\)\s*$", tickSpecification) if m is not None: tilde, N = m.group(1), int(m.group(2)) if N < 2: raise ValueError( "N must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if tilde == "~": N = -N ticks = PlotTickMarks._computeTicks(low, high, N) miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) return ticks, miniticks m = re.match("^\s*log([0-9]*)\s*\(\s*(~?)([0-9]+)\s*\)\s*$", tickSpecification) if m is not None: base, tilde, N = m.group(1), m.group(2), int(m.group(3)) if base == "": base = 10 else: base = int(base) if base < 2: raise ValueError( "log base must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if N < 2: raise ValueError( "N must be greater than 1 in tick-marks specification: \"%s\"" % tickSpecification) if tilde == "~": N = -N if low <= 0.0 or high <= 0.0: # fallback (might be encountered in production, so don't raise an error) ticks = PlotTickMarks._computeTicks(low, high, N) miniticks = PlotTickMarks._computeMiniticks(low, high, ticks) return ticks, miniticks else: ticks = PlotTickMarks._computeLogticks(low, high, base, N) miniticks = PlotTickMarks._computeLogminiticks(low, high, base) return ticks, miniticks m = re.match("^\s*(explicit|fillmini)\s*\((.*)\)$", tickSpecification) if m is not None and m.group(1) in ("explicit", "fillmini"): spec = m.group(2) if spec[0] == "[": try: spec = json.loads(spec) spec = map(float, spec) low, high = min(spec), max(spec) ticks = dict((x, PlotNumberFormat.toUnicode(x, low, high)) for x in spec) if m.group(1) == "fillmini": miniticks = PlotTickMarks._computeMiniticks( low, high, ticks) else: miniticks = [] return ticks, miniticks except ValueError: pass elif spec[0] == "{": try: ticks = {} for x, label in json.loads( re.sub( r"\b([+-]? *(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?)\s*:", r'"\1":', spec)).items(): if isinstance(label, (float, int, long)): label = repr(label) if not isinstance(label, basestring): raise ValueError ticks[float(x)] = label if m.group(1) == "fillmini": miniticks = PlotTickMarks._computeMiniticks( low, high, ticks) else: miniticks = [] return ticks, miniticks except ValueError, AttributeError: pass
def _computeTicks(low, high, N): eps = defs.EPSILON * (high - low) low, high = low - eps, high + eps if N >= 0: output = {} x = low for i in xrange(N): if abs(x) < eps: label = u"0" else: label = PlotNumberFormat.toUnicode(x, low, high) output[x] = label x += (high - low) / (N - 1.0) return output N = -N counter = 0 granularity = 10**math.ceil(math.log10(max(abs(low), abs(high)))) lowN = math.ceil(low / granularity) highN = math.floor(high / granularity) while lowN > highN: countermod3 = counter % 3 if countermod3 == 0: granularity *= 0.5 elif countermod3 == 1: granularity *= 0.4 else: granularity *= 0.5 counter += 1 lowN = math.ceil(low / granularity) highN = math.floor(high / granularity) lastGranularity = granularity lastTrial = None while True: trial = {} for n in range(long(lowN), long(highN) + 1): x = n * granularity if abs(x) < eps: label = u"0" else: label = PlotNumberFormat.toUnicode(x, low, high) trial[x] = label if long(highN) + 1 - long(lowN) >= N: if lastTrial is None: v1, v2 = low, high return { v1: PlotNumberFormat.toUnicode(v1, low, high), v2: PlotNumberFormat.toUnicode(v2, low, high) } else: return lastTrial lastGranularity = granularity lastTrial = trial countermod3 = counter % 3 if countermod3 == 0: granularity *= 0.5 elif countermod3 == 1: granularity *= 0.4 else: granularity *= 0.5 counter += 1 lowN = math.ceil(low / granularity) highN = math.floor(high / granularity)