@@ -507,11 +507,18 @@ def _get_unit(self):
507507 """
508508 return 1
509509
510+ def _get_interval (self ):
511+ """
512+ Return the number of units for each tick.
513+ """
514+ return 1
515+
510516 def nonsingular (self , vmin , vmax ):
511517 unit = self ._get_unit ()
518+ interval = self ._get_interval ()
512519 if abs (vmax - vmin ) < 1e-6 :
513- vmin -= 2 * unit
514- vmax += 2 * unit
520+ vmin -= 2 * unit * interval
521+ vmax += 2 * unit * interval
515522 return vmin , vmax
516523
517524class RRuleLocator (DateLocator ):
@@ -542,8 +549,22 @@ def __call__(self):
542549 # The magic number!
543550 stop = _from_ordinalf ( 3652059.9999999 )
544551
545- self .rule .set (dtstart = start , until = stop )
552+ self .rule .set (dtstart = start , until = stop , count = self .MAXTICKS + 1 )
553+
554+ # estimate the number of ticks very approximately so we don't
555+ # have to do a very expensive (and potentially near infinite)
556+ # 'between' calculation, only to find out it will fail.
557+ nmax , nmin = date2num ((dmax , dmin ))
558+ estimate = (nmax - nmin ) / (self ._get_unit () * self ._get_interval ())
559+ # This estimate is only an estimate, so be really conservative
560+ # about bailing...
561+ if estimate > self .MAXTICKS * 2 :
562+ raise RuntimeError (
563+ 'RRuleLocator estimated to generate %d ticks from %s to %s: exceeds Locator.MAXTICKS * 2 (%d) ' % (estimate , dmin , dmax , self .MAXTICKS * 2 ))
564+
546565 dates = self .rule .between (dmin , dmax , True )
566+ if len (dates ) == 0 :
567+ return date2num ([dmin , dmax ])
547568 return self .raise_if_exceeds (date2num (dates ))
548569
549570 def _get_unit (self ):
@@ -552,14 +573,17 @@ def _get_unit(self):
552573 intelligent autoscaling.
553574 """
554575 freq = self .rule ._rrule ._freq
576+ return self .get_unit_generic (freq )
577+
578+ def get_unit_generic (freq ):
555579 if ( freq == YEARLY ):
556- return 365
580+ return 365.0
557581 elif ( freq == MONTHLY ):
558- return 30
582+ return 30.0
559583 elif ( freq == WEEKLY ):
560- return 7
584+ return 7.0
561585 elif ( freq == DAILY ):
562- return 1
586+ return 1.0
563587 elif ( freq == HOURLY ):
564588 return (1.0 / 24.0 )
565589 elif ( freq == MINUTELY ):
@@ -569,7 +593,11 @@ def _get_unit(self):
569593 else :
570594 # error
571595 return - 1 #or should this just return '1'?
596+ get_unit_generic = staticmethod (get_unit_generic )
572597
598+ def _get_interval (self ):
599+ return self .rule ._rrule ._interval
600+
573601 def autoscale (self ):
574602 """
575603 Set the view limits to include the data range.
@@ -702,23 +730,7 @@ def refresh(self):
702730 self ._locator = self .get_locator (dmin , dmax )
703731
704732 def _get_unit (self ):
705- if ( self ._freq == YEARLY ):
706- return 365.0
707- elif ( self ._freq == MONTHLY ):
708- return 30.0
709- elif ( self ._freq == WEEKLY ):
710- return 7.0
711- elif ( self ._freq == DAILY ):
712- return 1.0
713- elif ( self ._freq == HOURLY ):
714- return 1.0 / 24
715- elif ( self ._freq == MINUTELY ):
716- return 1.0 / (24 * 60 )
717- elif ( self ._freq == SECONDLY ):
718- return 1.0 / (24 * 3600 )
719- else :
720- # error
721- return - 1
733+ return RRuleLocator .get_unit_generic (self ._freq )
722734
723735 def autoscale (self ):
724736 'Try to choose the view limits intelligently.'
@@ -828,14 +840,6 @@ def __init__(self, base=1, month=1, day=1, tz=None):
828840 'tzinfo' : tz
829841 }
830842
831-
832- def _get_unit (self ):
833- """
834- Return how many days a unit of the locator is; used for
835- intelligent autoscaling.
836- """
837- return 365
838-
839843 def __call__ (self ):
840844 dmin , dmax = self .viewlim_to_dt ()
841845 ymin = self .base .le (dmin .year )
@@ -864,6 +868,7 @@ def autoscale(self):
864868 vmax = date2num (vmax )
865869 return self .nonsingular (vmin , vmax )
866870
871+
867872class MonthLocator (RRuleLocator ):
868873 """
869874 Make ticks on occurances of each month month, eg 1, 3, 12.
@@ -881,13 +886,6 @@ def __init__(self, bymonth=None, bymonthday=1, interval=1, tz=None):
881886 interval = interval , ** self .hms0d )
882887 RRuleLocator .__init__ (self , o , tz )
883888
884- def _get_unit (self ):
885- """
886- Return how many days a unit of the locator is; used for
887- intelligent autoscaling.
888- """
889- return 30
890-
891889
892890class WeekdayLocator (RRuleLocator ):
893891 """
@@ -909,13 +907,6 @@ def __init__(self, byweekday=1, interval=1, tz=None):
909907 interval = interval , ** self .hms0d )
910908 RRuleLocator .__init__ (self , o , tz )
911909
912- def _get_unit (self ):
913- """
914- return how many days a unit of the locator is; used for
915- intelligent autoscaling.
916- """
917- return 7
918-
919910
920911class DayLocator (RRuleLocator ):
921912 """
@@ -934,13 +925,7 @@ def __init__(self, bymonthday=None, interval=1, tz=None):
934925 interval = interval , ** self .hms0d )
935926 RRuleLocator .__init__ (self , o , tz )
936927
937- def _get_unit (self ):
938- """
939- Return how many days a unit of the locator is; used for
940- intelligent autoscaling.
941- """
942- return 1
943-
928+
944929class HourLocator (RRuleLocator ):
945930 """
946931 Make ticks on occurances of each hour.
@@ -958,13 +943,7 @@ def __init__(self, byhour=None, interval=1, tz=None):
958943 byminute = 0 , bysecond = 0 )
959944 RRuleLocator .__init__ (self , rule , tz )
960945
961- def _get_unit (self ):
962- """
963- return how many days a unit of the locator is; use for
964- intelligent autoscaling
965- """
966- return 1 / 24.
967-
946+
968947class MinuteLocator (RRuleLocator ):
969948 """
970949 Make ticks on occurances of each minute.
@@ -982,13 +961,7 @@ def __init__(self, byminute=None, interval=1, tz=None):
982961 bysecond = 0 )
983962 RRuleLocator .__init__ (self , rule , tz )
984963
985- def _get_unit (self ):
986- """
987- Return how many days a unit of the locator is; used for
988- intelligent autoscaling.
989- """
990- return 1. / (24 * 60 )
991-
964+
992965class SecondLocator (RRuleLocator ):
993966 """
994967 Make ticks on occurances of each second.
@@ -1006,14 +979,6 @@ def __init__(self, bysecond=None, interval=1, tz=None):
1006979 rule = rrulewrapper (SECONDLY , bysecond = bysecond , interval = interval )
1007980 RRuleLocator .__init__ (self , rule , tz )
1008981
1009- def _get_unit (self ):
1010- """
1011- Return how many days a unit of the locator is; used for
1012- intelligent autoscaling.
1013- """
1014- return 1. / (24 * 60 * 60 )
1015-
1016-
1017982
1018983def _close_to_dt (d1 , d2 , epsilon = 5 ):
1019984 'Assert that datetimes *d1* and *d2* are within *epsilon* microseconds.'
0 commit comments