Revision: 8296
http://matplotlib.svn.sourceforge.net/matplotlib/?rev=8296&view=rev
Author: mdboom
Date: 2010-05-05 17:57:19 +0000 (Wed, 05 May 2010)
Log Message:
-----------
Improvements to DateLocators to partially address #2976576:
- Take into account "interval" when determining autorange (particularly
important for expanding singular ranges)
- prevent a near-infinite loop in the "dateutil.rrule.between" method
(calculating a repeating event between two dates) by estimating with division
the approximate number, and if this number is way more than reasonable, raise
an Exception.
Modified Paths:
--------------
trunk/matplotlib/lib/matplotlib/dates.py
Modified: trunk/matplotlib/lib/matplotlib/dates.py
===================================================================
--- trunk/matplotlib/lib/matplotlib/dates.py 2010-05-04 19:46:28 UTC (rev
8295)
+++ trunk/matplotlib/lib/matplotlib/dates.py 2010-05-05 17:57:19 UTC (rev
8296)
@@ -507,11 +507,18 @@
"""
return 1
+ def _get_interval(self):
+ """
+ Return the number of units for each tick.
+ """
+ return 1
+
def nonsingular(self, vmin, vmax):
unit = self._get_unit()
+ interval = self._get_interval()
if abs(vmax - vmin) < 1e-6:
- vmin -= 2*unit
- vmax += 2*unit
+ vmin -= 2*unit*interval
+ vmax += 2*unit*interval
return vmin, vmax
class RRuleLocator(DateLocator):
@@ -542,8 +549,22 @@
# The magic number!
stop = _from_ordinalf( 3652059.9999999 )
- self.rule.set(dtstart=start, until=stop)
+ self.rule.set(dtstart=start, until=stop, count=self.MAXTICKS + 1)
+
+ # estimate the number of ticks very approximately so we don't
+ # have to do a very expensive (and potentially near infinite)
+ # 'between' calculation, only to find out it will fail.
+ nmax, nmin = date2num((dmax, dmin))
+ estimate = (nmax - nmin) / (self._get_unit() * self._get_interval())
+ # This estimate is only an estimate, so be really conservative
+ # about bailing...
+ if estimate > self.MAXTICKS * 2:
+ raise RuntimeError(
+ 'RRuleLocator estimated to generate %d ticks from %s to %s:
exceeds Locator.MAXTICKS * 2 (%d) ' % (estimate, dmin, dmax, self.MAXTICKS * 2))
+
dates = self.rule.between(dmin, dmax, True)
+ if len(dates) == 0:
+ return date2num([dmin, dmax])
return self.raise_if_exceeds(date2num(dates))
def _get_unit(self):
@@ -552,14 +573,17 @@
intelligent autoscaling.
"""
freq = self.rule._rrule._freq
+ return self.get_unit_generic(freq)
+
+ def get_unit_generic(freq):
if ( freq == YEARLY ):
- return 365
+ return 365.0
elif ( freq == MONTHLY ):
- return 30
+ return 30.0
elif ( freq == WEEKLY ):
- return 7
+ return 7.0
elif ( freq == DAILY ):
- return 1
+ return 1.0
elif ( freq == HOURLY ):
return (1.0/24.0)
elif ( freq == MINUTELY ):
@@ -569,7 +593,11 @@
else:
# error
return -1 #or should this just return '1'?
+ get_unit_generic = staticmethod(get_unit_generic)
+ def _get_interval(self):
+ return self.rule._rrule._interval
+
def autoscale(self):
"""
Set the view limits to include the data range.
@@ -702,23 +730,7 @@
self._locator = self.get_locator(dmin, dmax)
def _get_unit(self):
- if ( self._freq == YEARLY ):
- return 365.0
- elif ( self._freq == MONTHLY ):
- return 30.0
- elif ( self._freq == WEEKLY ):
- return 7.0
- elif ( self._freq == DAILY ):
- return 1.0
- elif ( self._freq == HOURLY ):
- return 1.0/24
- elif ( self._freq == MINUTELY ):
- return 1.0/(24*60)
- elif ( self._freq == SECONDLY ):
- return 1.0/(24*3600)
- else:
- # error
- return -1
+ return RRuleLocator.get_unit_generic(self._freq)
def autoscale(self):
'Try to choose the view limits intelligently.'
@@ -828,14 +840,6 @@
'tzinfo' : tz
}
-
- def _get_unit(self):
- """
- Return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 365
-
def __call__(self):
dmin, dmax = self.viewlim_to_dt()
ymin = self.base.le(dmin.year)
@@ -864,6 +868,7 @@
vmax = date2num(vmax)
return self.nonsingular(vmin, vmax)
+
class MonthLocator(RRuleLocator):
"""
Make ticks on occurances of each month month, eg 1, 3, 12.
@@ -881,14 +886,7 @@
interval=interval, **self.hms0d)
RRuleLocator.__init__(self, o, tz)
- def _get_unit(self):
- """
- Return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 30
-
class WeekdayLocator(RRuleLocator):
"""
Make ticks on occurances of each weekday.
@@ -909,14 +907,7 @@
interval=interval, **self.hms0d)
RRuleLocator.__init__(self, o, tz)
- def _get_unit(self):
- """
- return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 7
-
class DayLocator(RRuleLocator):
"""
Make ticks on occurances of each day of the month. For example,
@@ -934,13 +925,7 @@
interval=interval, **self.hms0d)
RRuleLocator.__init__(self, o, tz)
- def _get_unit(self):
- """
- Return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 1
-
+
class HourLocator(RRuleLocator):
"""
Make ticks on occurances of each hour.
@@ -958,13 +943,7 @@
byminute=0, bysecond=0)
RRuleLocator.__init__(self, rule, tz)
- def _get_unit(self):
- """
- return how many days a unit of the locator is; use for
- intelligent autoscaling
- """
- return 1/24.
-
+
class MinuteLocator(RRuleLocator):
"""
Make ticks on occurances of each minute.
@@ -982,13 +961,7 @@
bysecond=0)
RRuleLocator.__init__(self, rule, tz)
- def _get_unit(self):
- """
- Return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 1./(24*60)
-
+
class SecondLocator(RRuleLocator):
"""
Make ticks on occurances of each second.
@@ -1006,15 +979,7 @@
rule = rrulewrapper(SECONDLY, bysecond=bysecond, interval=interval)
RRuleLocator.__init__(self, rule, tz)
- def _get_unit(self):
- """
- Return how many days a unit of the locator is; used for
- intelligent autoscaling.
- """
- return 1./(24*60*60)
-
-
def _close_to_dt(d1, d2, epsilon=5):
'Assert that datetimes *d1* and *d2* are within *epsilon* microseconds.'
delta = d2-d1
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
_______________________________________________
Matplotlib-checkins mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/matplotlib-checkins