6
6
# Rob Andrews (@Calama-Consulting), Calama Consulting, 2014
7
7
# Will Holmgren (@wholmgren), University of Arizona, 2014
8
8
# Tony Lorenzo (@alorenzo175), University of Arizona, 2015
9
+ # Cliff hansen (@cwhanse), Sandia National Laboratories, 2018
9
10
10
11
from __future__ import division
11
12
import os
@@ -438,7 +439,26 @@ def get_sun_rise_set_transit(time, latitude, longitude, how='numpy',
438
439
return result
439
440
440
441
441
- def _ephem_setup (latitude , longitude , altitude , pressure , temperature ):
442
+ def _ephem_convert_to_seconds_and_microseconds (date ):
443
+ # utility from unreleased PyEphem 3.6.7.1
444
+ """Converts a PyEphem date into seconds"""
445
+ microseconds = int (round (24 * 60 * 60 * 1000000 * date ))
446
+ seconds , microseconds = divmod (microseconds , 1000000 )
447
+ seconds -= 2209032000 # difference between epoch 1900 and epoch 1970
448
+ return seconds , microseconds
449
+
450
+
451
+ def _ephem_to_timezone (date , tzinfo ):
452
+ # utility from unreleased PyEphem 3.6.7.1
453
+ """"Convert a PyEphem Date into a timezone aware python datetime"""
454
+ seconds , microseconds = _ephem_convert_to_seconds_and_microseconds (date )
455
+ date = dt .datetime .fromtimestamp (seconds , tzinfo )
456
+ date = date .replace (microsecond = microseconds )
457
+ return date
458
+
459
+
460
+ def _ephem_setup (latitude , longitude , altitude , pressure , temperature ,
461
+ horizon ):
442
462
import ephem
443
463
# initialize a PyEphem observer
444
464
obs = ephem .Observer ()
@@ -447,14 +467,97 @@ def _ephem_setup(latitude, longitude, altitude, pressure, temperature):
447
467
obs .elevation = altitude
448
468
obs .pressure = pressure / 100. # convert to mBar
449
469
obs .temp = temperature
470
+ obs .horizon = horizon
450
471
451
472
# the PyEphem sun
452
473
sun = ephem .Sun ()
453
474
return obs , sun
454
475
455
476
477
+ def rise_set_transit_ephem (time , latitude , longitude ,
478
+ next_or_previous = 'next' ,
479
+ altitude = 0 ,
480
+ pressure = 101325 , temperature = 12 , horizon = '0:00' ):
481
+ """
482
+ Calculate the next sunrise and sunset times using the PyEphem package.
483
+
484
+ Parameters
485
+ ----------
486
+ time : pandas.DatetimeIndex
487
+ Must be localized
488
+ latitude : float
489
+ positive is north of 0
490
+ longitude : float
491
+ positive is east of 0
492
+ next_or_previous : str
493
+ 'next' or 'previous' sunrise and sunset relative to time
494
+ altitude : float, default 0
495
+ distance above sea level in meters.
496
+ pressure : int or float, optional, default 101325
497
+ air pressure in Pascals.
498
+ temperature : int or float, optional, default 12
499
+ air temperature in degrees C.
500
+ horizon : string, format +/-X:YY
501
+ arc degrees:arc minutes from geometrical horizon for sunrise and
502
+ sunset, e.g., horizon='+0:00' to use sun center crossing the
503
+ geometrical horizon to define sunrise and sunset,
504
+ horizon='-0:34' for when the sun's upper edge crosses the
505
+ geometrical horizon
506
+
507
+ Returns
508
+ -------
509
+ pandas.DataFrame
510
+ index is the same as input `time` argument
511
+ columns are 'sunrise', 'sunset', and 'transit'
512
+
513
+ See also
514
+ --------
515
+ pyephem
516
+ """
517
+
518
+ try :
519
+ import ephem
520
+ except ImportError :
521
+ raise ImportError ('PyEphem must be installed' )
522
+
523
+ # times must be localized
524
+ if not time .tz :
525
+ raise ValueError ('rise_set_ephem: times must be localized' )
526
+
527
+ obs , sun = _ephem_setup (latitude , longitude , altitude ,
528
+ pressure , temperature , horizon )
529
+ # create lists of sunrise and sunset time localized to time.tz
530
+ if next_or_previous .lower () == 'next' :
531
+ rising = obs .next_rising
532
+ setting = obs .next_setting
533
+ transit = obs .next_transit
534
+ elif next_or_previous .lower () == 'previous' :
535
+ rising = obs .previous_rising
536
+ setting = obs .previous_setting
537
+ transit = obs .previous_transit
538
+ else :
539
+ raise ValueError ("next_or_previous must be either 'next' or" +
540
+ " 'previous'" )
541
+
542
+ sunrise = []
543
+ sunset = []
544
+ trans = []
545
+ for thetime in time :
546
+ thetime = thetime .to_pydatetime ()
547
+ # pyephem drops timezone when converting to its internal datetime
548
+ # format, so handle timezone explicitly here
549
+ obs .date = ephem .Date (thetime - thetime .utcoffset ())
550
+ sunrise .append (_ephem_to_timezone (rising (sun ), time .tz ))
551
+ sunset .append (_ephem_to_timezone (setting (sun ), time .tz ))
552
+ trans .append (_ephem_to_timezone (transit (sun ), time .tz ))
553
+
554
+ return pd .DataFrame (index = time , data = {'sunrise' : sunrise ,
555
+ 'sunset' : sunset ,
556
+ 'transit' : trans })
557
+
558
+
456
559
def pyephem (time , latitude , longitude , altitude = 0 , pressure = 101325 ,
457
- temperature = 12 ):
560
+ temperature = 12 , horizon = '+0:00' ):
458
561
"""
459
562
Calculate the solar position using the PyEphem package.
460
563
@@ -463,17 +566,26 @@ def pyephem(time, latitude, longitude, altitude=0, pressure=101325,
463
566
time : pandas.DatetimeIndex
464
567
Localized or UTC.
465
568
latitude : float
569
+ positive is north of 0
466
570
longitude : float
571
+ positive is east of 0
467
572
altitude : float, default 0
468
- distance above sea level.
573
+ distance above sea level in meters .
469
574
pressure : int or float, optional, default 101325
470
575
air pressure in Pascals.
471
576
temperature : int or float, optional, default 12
472
577
air temperature in degrees C.
578
+ horizon : string, optional, default '+0:00'
579
+ arc degrees:arc minutes from geometrical horizon for sunrise and
580
+ sunset, e.g., horizon='+0:00' to use sun center crossing the
581
+ geometrical horizon to define sunrise and sunset,
582
+ horizon='-0:34' for when the sun's upper edge crosses the
583
+ geometrical horizon
473
584
474
585
Returns
475
586
-------
476
- DataFrame
587
+ pandas.DataFrame
588
+ index is the same as input `time` argument
477
589
The DataFrame will have the following columns:
478
590
apparent_elevation, elevation,
479
591
apparent_azimuth, azimuth,
@@ -499,7 +611,7 @@ def pyephem(time, latitude, longitude, altitude=0, pressure=101325,
499
611
sun_coords = pd .DataFrame (index = time )
500
612
501
613
obs , sun = _ephem_setup (latitude , longitude , altitude ,
502
- pressure , temperature )
614
+ pressure , temperature , horizon )
503
615
504
616
# make and fill lists of the sun's altitude and azimuth
505
617
# this is the pressure and temperature corrected apparent alt/az.
@@ -711,7 +823,8 @@ def ephemeris(time, latitude, longitude, pressure=101325, temperature=12):
711
823
712
824
713
825
def calc_time (lower_bound , upper_bound , latitude , longitude , attribute , value ,
714
- altitude = 0 , pressure = 101325 , temperature = 12 , xtol = 1.0e-12 ):
826
+ altitude = 0 , pressure = 101325 , temperature = 12 , horizon = '+0:00' ,
827
+ xtol = 1.0e-12 ):
715
828
"""
716
829
Calculate the time between lower_bound and upper_bound
717
830
where the attribute is equal to value. Uses PyEphem for
@@ -736,6 +849,12 @@ def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
736
849
atmospheric correction.
737
850
temperature : int or float, optional, default 12
738
851
Air temperature in degrees C.
852
+ horizon : string, optional, default '+0:00'
853
+ arc degrees:arc minutes from geometrical horizon for sunrise and
854
+ sunset, e.g., horizon='+0:00' to use sun center crossing the
855
+ geometrical horizon to define sunrise and sunset,
856
+ horizon='-0:34' for when the sun's upper edge crosses the
857
+ geometrical horizon
739
858
xtol : float, optional, default 1.0e-12
740
859
The allowed error in the result from value
741
860
@@ -758,7 +877,7 @@ def calc_time(lower_bound, upper_bound, latitude, longitude, attribute, value,
758
877
raise ImportError ('The calc_time function requires scipy' )
759
878
760
879
obs , sun = _ephem_setup (latitude , longitude , altitude ,
761
- pressure , temperature )
880
+ pressure , temperature , horizon )
762
881
763
882
def compute_attr (thetime , target , attr ):
764
883
obs .date = thetime
0 commit comments