Example #1
0
    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
Example #2
0
 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
Example #3
0
    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
Example #4
0
 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
Example #5
0
    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
Example #6
0
    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
Example #7
0
                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
Example #8
0
                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
Example #9
0
    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
Example #10
0
    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)
Example #11
0
    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
Example #12
0
    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)