Skip to content

Commit 2746c32

Browse files
committed
Adding altitude lookup for location class
Set altitude automatically from built-in map when not specified. See pvlib#1547
1 parent f8b1294 commit 2746c32

File tree

2 files changed

+39
-24
lines changed

2 files changed

+39
-24
lines changed

pvlib/location.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
# Will Holmgren, University of Arizona, 2014-2016.
66

7-
import os
7+
import pathlib
88
import datetime
99

1010
import pandas as pd
@@ -14,6 +14,7 @@
1414
from pvlib import solarposition, clearsky, atmosphere, irradiance
1515
from pvlib.tools import _degrees_to_index
1616

17+
1718
class Location:
1819
"""
1920
Location objects are convenient containers for latitude, longitude,
@@ -44,8 +45,10 @@ class Location:
4445
pytz.timezone objects will be converted to strings.
4546
ints and floats must be in hours from UTC.
4647
47-
altitude : float, default 0.
48+
altitude : None or float, default None
4849
Altitude from sea level in meters.
50+
If None, the altitude will be fetched from
51+
:py:func:`pvlib.location.lookup_altitude`.
4952
5053
name : None or string, default None.
5154
Sets the name attribute of the Location object.
@@ -55,7 +58,8 @@ class Location:
5558
pvlib.pvsystem.PVSystem
5659
"""
5760

58-
def __init__(self, latitude, longitude, tz='UTC', altitude=0, name=None):
61+
def __init__(self, latitude, longitude, tz='UTC', altitude=None,
62+
name=None):
5963

6064
self.latitude = latitude
6165
self.longitude = longitude
@@ -75,6 +79,9 @@ def __init__(self, latitude, longitude, tz='UTC', altitude=0, name=None):
7579
else:
7680
raise TypeError('Invalid tz specification')
7781

82+
if altitude is None:
83+
altitude = lookup_altitude(latitude, longitude)
84+
7885
self.altitude = altitude
7986

8087
self.name = name
@@ -427,8 +434,8 @@ def lookup_altitude(latitude, longitude):
427434
428435
"""
429436

430-
pvlib_path = os.path.dirname(os.path.abspath(__file__))
431-
filepath = os.path.join(pvlib_path, 'data', 'Altitude.h5')
437+
pvlib_path = pathlib.Path(__file__).parent
438+
filepath = pvlib_path / 'data' / 'Altitude.h5'
432439

433440
latitude_index = _degrees_to_index(latitude, coordinate='latitude')
434441
longitude_index = _degrees_to_index(longitude, coordinate='longitude')

pvlib/tests/test_location.py

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from pytz.exceptions import UnknownTimeZoneError
1313

1414
import pvlib
15+
from pvlib import location
1516
from pvlib.location import Location, lookup_altitude
1617
from pvlib.solarposition import declination_spencer71
1718
from pvlib.solarposition import equation_of_time_spencer71
@@ -212,7 +213,7 @@ def test_get_clearsky_valueerror(times):
212213
def test_from_tmy_3():
213214
from pvlib.tests.iotools.test_tmy import TMY3_TESTFILE
214215
from pvlib.iotools import read_tmy3
215-
data, meta = read_tmy3(TMY3_TESTFILE, map_variables=True)
216+
data, meta = read_tmy3(TMY3_TESTFILE)
216217
loc = Location.from_tmy(meta, data)
217218
assert loc.name is not None
218219
assert loc.altitude != 0
@@ -328,21 +329,28 @@ def test_extra_kwargs():
328329
Location(32.2, -111, arbitrary_kwarg='value')
329330

330331

331-
def test_lookup_altitude():
332-
max_alt_error = 125
333-
# location name, latitude, longitude, altitude
334-
test_locations = [
335-
('Tucson, USA', 32.2540, -110.9742, 724),
336-
('Lusaka, Zambia', -15.3875, 28.3228, 1253),
337-
('Tokio, Japan', 35.6762, 139.6503, 40),
338-
('Canberra, Australia', -35.2802, 149.1310, 566),
339-
('Bogota, Colombia', 4.7110, -74.0721, 2555),
340-
('Dead Sea, West Bank', 31.525849, 35.449214, -415),
341-
('New Delhi, India', 28.6139, 77.2090, 214),
342-
('Null Island, Atlantic Ocean', 0, 0, 0),
343-
]
344-
345-
for name, lat, lon, expected_alt in test_locations:
346-
alt_found = lookup_altitude(lat, lon)
347-
assert abs(alt_found - expected_alt) < max_alt_error, \
348-
f'Max error exceded for {name} - e: {expected_alt} f: {alt_found}'
332+
@pytest.mark.parametrize('lat,lon,expected_alt', [
333+
pytest.param(32.2540, -110.9742, 724, id='Tucson, USA'),
334+
pytest.param(-15.3875, 28.3228, 1253, id='Lusaka, Zambia'),
335+
pytest.param(35.6762, 139.6503, 40, id='Tokyo, Japan'),
336+
pytest.param(-35.2802, 149.1310, 566, id='Canberra, Australia'),
337+
pytest.param(4.7110, -74.0721, 2555, id='Bogota, Colombia'),
338+
pytest.param(31.525849, 35.449214, -415, id='Dead Sea, West Bank'),
339+
pytest.param(28.6139, 77.2090, 214, id='New Delhi, India'),
340+
pytest.param(0, 0, 0, id='Null Island, Atlantic Ocean'),
341+
])
342+
def test_lookup_altitude(lat, lon, expected_alt):
343+
alt_found = lookup_altitude(lat, lon)
344+
assert alt_found == pytest.approx(expected_alt, abs=125)
345+
346+
347+
def test_location_lookup_altitude(mocker):
348+
mocker.spy(location, 'lookup_altitude')
349+
tus = Location(32.2, -111, 'US/Arizona', 700, 'Tucson')
350+
location.lookup_altitude.assert_not_called()
351+
assert tus.altitude == 700
352+
location.lookup_altitude.reset_mock()
353+
354+
tus = Location(32.2, -111, 'US/Arizona')
355+
location.lookup_altitude.assert_called_once_with(32.2, -111)
356+
assert tus.altitude == location.lookup_altitude(32.2, -111)

0 commit comments

Comments
 (0)