123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- #******************************************************************************
- #******************************************************************************
- # import libraries
- from math import pi, floor
- from src import pysoleng as pse #
- from numpy.testing import assert_allclose
- import datetime as dt
- from zoneinfo import ZoneInfo
- # test using "!python -m pytest -s --cov --cov-report term-missing"
-
- # *****************************************************************************
- # *****************************************************************************
- class TestTimePlace:
-
- def test_flat_surface(self):
-
- # 1) single azimuth, single slope
-
- fs1 = pse.timeplace.FlatSurface(
- azimuth=45*pi/180,
- slope=45*pi/180
- )
-
- assert fs1.has_fixed_azimuth()
- assert fs1.has_fixed_slope()
- assert fs1.number_orientations() == 1
-
- # 2) single azimuth, multiple slopes
-
- fs2 = pse.timeplace.FlatSurface(
- azimuth=45*pi/180,
- slope=[45*pi/180, 0]
- )
-
- assert fs2.has_fixed_azimuth()
- assert not fs2.has_fixed_slope()
- assert fs2.number_orientations() == 2
-
- # 3) single azimuth, multiple slopes
-
- fs3 = pse.timeplace.FlatSurface(
- azimuth=[45*pi/180, 90*pi/180],
- slope=45*pi/180
- )
-
- assert not fs3.has_fixed_azimuth()
- assert fs3.has_fixed_slope()
- assert fs3.number_orientations() == 2
-
- # 4) multiple azimuths, multiple slopes
-
- fs4 = pse.timeplace.FlatSurface(
- azimuth=[45*pi/180],
- slope=[45*pi/180]
- )
-
- assert not fs4.has_fixed_azimuth()
- assert not fs4.has_fixed_slope()
- assert fs4.number_orientations() == 1
-
- # *********************************************************************
- # *********************************************************************
-
- # trigger errors
-
- # incorrect input area
- error_raised = False
- try:
- pse.timeplace.FlatSurface(
- azimuth=45*pi/180,
- slope=45*pi/180,
- area='a'
- )
- except TypeError:
- error_raised = True
- assert error_raised
-
- # incorrect sizes
- error_raised = False
- try:
- pse.timeplace.FlatSurface(
- azimuth=[45*pi/180],
- slope=[45*pi/180, 60*pi/180]
- )
- except ValueError:
- error_raised = True
- assert error_raised
-
- # incorrect elements in the iterables
- error_raised = False
- try:
- pse.timeplace.FlatSurface(
- azimuth=[45*pi/180, 'a'],
- slope=[45*pi/180, 60*pi/180]
- )
- except ValueError:
- error_raised = True
- assert error_raised
-
- # incorrect elements in the iterables
- error_raised = False
- try:
- pse.timeplace.FlatSurface(
- azimuth=[45*pi/180, 60*pi/180],
- slope=[45*pi/180, 'b']
- )
- except ValueError:
- error_raised = True
- assert error_raised
-
- # *********************************************************************
- # *********************************************************************
-
- # *************************************************************************
- # *************************************************************************
- def test_location_creation(self):
-
- #transformer = Transformer.from_crs("EPSG:4326", "EPSG:3857", always_xy=True)
-
- list_locations = [
- # unprojected
- ((30,110),'epsg:4326',(30,110)),
- ((30,-110),'epsg:4326',(30,-110)),
- ((-30,110),'epsg:4326',(-30,110)),
- ((-30,-110),'epsg:4326',(-30,-110)),
- # projected
- ((3339584.723798207, 3503549.8435043753),'epsg:3857',(30,30)),
- ((3339584.723798207, -3503549.8435043753),'epsg:3857',(30,-30)),
- ((-3339584.723798207, 3503549.8435043753),'epsg:3857',(-30,30)),
- ((-3339584.723798207, -3503549.8435043753),'epsg:3857',(-30,-30)),
- # projected, time
- ((-466939.6922422622, 3361248.752300655),'epsg:32633',(5,30)),
- ((-56351.259575267904, 6693618.35050865),'epsg:32633',(5,60)),
- ((17453.33453025535, 3329329.800484811),'epsg:32633',(10,30)),
- ((221288.77024763188, 6661953.040544909),'epsg:32633',(10,60))
- ]
-
- for location in list_locations:
-
- mylocation = pse.timeplace.Location(
- coordinates=location[0],
- crs_string=location[1])
-
- assert_allclose(mylocation.latitude, location[2][0])
- assert_allclose(mylocation.longitude, location[2][1])
-
- # locations with issues
-
- list_locations = [
- # epsg:4326
- ((30,182),'epsg:4326',(30,30)), # longitude out of bounds
- ((30,-181),'epsg:4326',(30,30)), # longitude out of bounds
- ((95,30),'epsg:4326',(30,30)), # latitute out of bounds
- ((-95,30),'epsg:4326',(30,30)), # latitute out of bounds
- ((-95,30,0),'epsg:4326',(30,30)) # 3 coordinates
- ]
-
- error_counter = 0
-
- for location in list_locations:
-
- # trigger the errors
-
- try:
-
- mylocation = pse.timeplace.Location(
- coordinates=location[0],
- crs_string=location[1])
-
- except ValueError:
-
- error_counter += 1
-
- assert error_counter == len(list_locations)
-
- # *************************************************************************
- # *************************************************************************
- def test_day_of_the_year(self):
-
- list_tests = [
- # non-leap year
- ('2011-01-15 12:00:00-00:00',15),
- ('2011-02-15 12:00:00-00:00',31+15),
- ('2011-03-15 12:00:00-00:00',59+15),
- ('2011-04-15 12:00:00-00:00',90+15),
- ('2011-05-15 12:00:00-00:00',120+15),
- ('2011-06-15 12:00:00-00:00',151+15),
- ('2011-07-15 12:00:00-00:00',181+15),
- ('2011-08-15 12:00:00-00:00',212+15),
- ('2011-09-15 12:00:00-00:00',243+15),
- ('2011-10-15 12:00:00-00:00',273+15),
- ('2011-11-15 12:00:00-00:00',304+15),
- ('2011-12-15 12:00:00-00:00',334+15),
- # leap year: a day is added after february
- ('2012-01-15 12:00:00-00:00',15),
- ('2012-02-15 12:00:00-00:00',31+15),
- ('2012-03-15 12:00:00-00:00',59+1+15), # after february: add +1
- ('2012-04-15 12:00:00-00:00',90+1+15),
- ('2012-05-15 12:00:00-00:00',120+1+15),
- ('2012-06-15 12:00:00-00:00',151+1+15),
- ('2012-07-15 12:00:00-00:00',181+1+15),
- ('2012-08-15 12:00:00-00:00',212+1+15),
- ('2012-09-15 12:00:00-00:00',243+1+15),
- ('2012-10-15 12:00:00-00:00',273+1+15),
- ('2012-11-15 12:00:00-00:00',304+1+15),
- ('2012-12-15 12:00:00-00:00',334+1+15),
- # UTC offsets: + is ahead of UTC (east), - is before UTC (west)
- ('2011-01-15 03:00:00+05:00',15-1), # 5 hours ahead: 22 pm UTC
- ('2011-02-15 03:00:00+05:00',31+15-1), # 5 hours ahead: 22 pm UTC
- ('2011-03-15 03:00:00+05:00',59+15-1), # 5 hours ahead: 22 pm UTC
- ('2011-01-15 22:00:00-05:00',15+1), # 5 hours before UTC: 3 am UTC
- ('2011-02-15 22:00:00-05:00',31+15+1), # 5 hours before UTC: 3 am UTC
- ('2011-03-15 22:00:00-05:00',59+15+1), # 5 hours before UTC: 3 am UTC
- ]
-
- for test in list_tests:
-
- # create datetime object
-
- my_dt = dt.datetime.fromisoformat(test[0])
-
- # get the day of the year
-
- doty = pse.timeplace.day_of_the_year(my_time=my_dt,
- as_integer=True)
-
- # validate it
-
- assert_allclose(doty, test[1])
-
- # get the non-integer variant
-
- doty_ni = pse.timeplace.day_of_the_year(my_time=my_dt,
- as_integer=False)
-
- # validate it
-
- assert_allclose(floor(doty_ni), test[1])
-
- # *************************************************************************
- # *************************************************************************
- def test_is_datetime_aware(self):
-
- list_tests = [
- ('2011-01-15 12:00:00-00:00', True),
- ('2011-01-15 12:00:00', False),
- ]
-
- # for each test
-
- for test in list_tests:
-
- # create datetime object
-
- my_dt = dt.datetime.fromisoformat(test[0])
-
- assert pse.timeplace.is_datetime_aware(my_dt) == test[1]
-
- # *************************************************************************
- # *************************************************************************
-
- def test_finding_standard_meridian(self):
-
- # TODO: get the daylight time savings time
-
- list_tests = [
- # format: coordinates, crs, time, dst offset degrees, true standard meridian
- # Nicosia: 35°10′21″N 33°21′54″E (Kalogirou, 2009; ex. 2.1, page 52)
- ((35.0, 33.3), 'epsg:4326', '2022-11-15 10:43:00+02:00', 0, 30),# winter
- ((35.0, 33.3), 'epsg:4326', '2022-11-15 10:43:00+03:00', 3600, 30),# summer
- # Madison: 43°04′29″N 89°23′03″W (Duffie and Beckman, 2013; ex. 1.5.1, page 11)
- ((43.0, -89.4), 'epsg:4326', '2010-02-03 10:19:00-06:00', 0, -90),# winter
- ((43.0, -89.4), 'epsg:4326', '2010-02-03 10:19:00-05:00', 3600, -90),# summer
- ]
-
- for test in list_tests:
-
- mylocation = pse.timeplace.Location(
- coordinates=test[0],
- crs_string=test[1])
-
- mytime = dt.datetime.fromisoformat(test[2])
-
- # compute the local meridian (subtract dst offset since it is included)
-
- local_meridian = mylocation.local_standard_meridian_longitude(
- mytime, test[3])
-
- assert_allclose(local_meridian, test[4])
-
- # try causing errors
-
- list_tests = [
- # naive datetime objects
- # Nicosia: 35°10′21″N 33°21′54″E (Kalogirou, 2009; ex. 2.1, page 52)
- ((35.0, 33.3), 'epsg:4326', '2022-11-15 10:43:00', 0, 30),# winter
- ((35.0, 33.3), 'epsg:4326', '2022-11-15 10:43:00', 3600, 30),# summer
- # Madison: 43°04′29″N 89°23′03″W (Duffie and Beckman, 2013; ex. 1.5.1, page 11)
- ((43.0, -89.4), 'epsg:4326', '2010-02-03 10:19:00', 0, -90),# winter
- ((43.0, -89.4), 'epsg:4326', '2010-02-03 10:19:00', 3600, -90),# summer
- ]
-
- # error counter
-
- error_counter = 0
-
- for test in list_tests:
-
- # trigger the errors
-
- try:
- mylocation = pse.timeplace.Location(
- coordinates=test[0],
- crs_string=test[1])
-
- mytime = dt.datetime.fromisoformat(test[2])
-
- # compute the local meridian (subtract dst offset since it is included)
-
- local_meridian = mylocation.local_standard_meridian_longitude(
- mytime, test[3])
-
- except ValueError:
-
- error_counter += 1
-
- assert error_counter == len(list_tests)
-
- # *************************************************************************
- # *************************************************************************
-
- def test_local_timezone_with_dst(self):
-
- # create utc time zone
-
- tz_utc = dt.timezone.utc
-
- # create time zone with dst, northern hemisphere
-
- tz_with_dst_nh = ZoneInfo("Portugal")
-
- # create time zone without dst, northern hemisphere
-
- tz_without_dst_nh = ZoneInfo("Iceland")
-
- # create time zone with dst, southern hemisphere
-
- tz_with_dst_sh = ZoneInfo("America/Asuncion")
-
- # create time zone without dst, souther hemisphere
-
- tz_without_dst_sh = ZoneInfo("America/Argentina/Buenos_Aires")
-
- # create tests with datetime objects with and without dst
-
- list_tests = [ # local time object, utc time, is dst active?, utc offset
- # Portugal, (Northern) Summer
- (dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_with_dst_nh),
- dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=11,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- True,
- 3600),
- # Portugal, (Northern) Winter
- (dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_with_dst_nh),
- dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0),
- # Iceland, (Northern) Summer
- (dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_without_dst_nh),
- dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0),
- # Iceland, (Northern) Winter
- (dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_without_dst_nh),
- dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0),
- # Paraguay, (Northern) Summer
- (dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_with_dst_sh),
- dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=16,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0),
- # Paraguay, (Northern) Winter
- (dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_with_dst_sh),
- dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=15,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- True,
- 3600),
- # Argentina, (Northern) Summer
- (dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_without_dst_sh),
- dt.datetime(
- year=2012,
- month=6,
- day=5,
- hour=15,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0),
- # Argentina, (Northern) Winter
- (dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=12,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_without_dst_sh),
- dt.datetime(
- year=2012,
- month=12,
- day=5,
- hour=15,
- minute=0,
- second=0,
- microsecond=0,
- tzinfo=tz_utc),
- False,
- 0)
- ]
-
- # verify that that the utc offset changes
-
- for test in list_tests:
-
- localtime = test[0]
-
- utc_time = test[1]
-
- # verify if there is or not dst-related offset
-
- assert test[2] == (localtime.dst() > dt.timedelta(seconds=0))
-
- assert localtime.dst() == dt.timedelta(seconds=test[3])
-
- #no offset from utc
-
- assert utc_time.utcoffset() == dt.timedelta(seconds=0)
-
- # check if utc times match (note: datetime objects are hashable)
-
- assert localtime.astimezone(tz_utc) == utc_time
-
- #******************************************************************************
- #******************************************************************************
|