Skip to content

Commit da202e2

Browse files
committed
Initial commit
1 parent 6e6a71b commit da202e2

File tree

3 files changed

+52
-39
lines changed

3 files changed

+52
-39
lines changed

docs/sphinx/source/whatsnew/v0.12.1.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Breaking Changes
1111
following the iotools convention instead of ``(data,inputs,meta)``.
1212
The ``inputs`` dictionary is now included in ``meta``, which
1313
has changed structure to accommodate it. (:pull:`2462`)
14+
* The functions :py:func:`~pvlib.iotools.read_pvgis_tmy` and
15+
:py:func:`~pvlib.iotools.get_pvgis_tmy` now return ``(data,meta)``
16+
following the iotools convention instead of ``(data,months_selected,inputs,meta)``.
17+
(:pull:`XXXX`)
1418
* Remove ``outputformat='basic'`` option in :py:func:`~pvlib.iotools.get_pvgis_tmy`.
1519
(:pull:`2416`)
1620

pvlib/iotools/pvgis.py

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ def _parse_pvgis_hourly_json(src, map_variables):
261261

262262
def _parse_pvgis_hourly_csv(src, map_variables):
263263
# The first 4 rows are latitude, longitude, elevation, radiation database
264-
metadata = {'inputs': {}}
264+
metadata = {'inputs': {}, 'descriptions': {}}
265265
# 'location' metadata
266266
# 'Latitude (decimal degrees): 45.000\r\n'
267267
metadata['inputs']['latitude'] = float(src.readline().split(':')[1])
@@ -440,6 +440,13 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
440440
441441
For more information see the PVGIS [1]_ TMY tool documentation [2]_.
442442
443+
.. versionchanged:: 0.13.0
444+
The function now returns two items ``(data,meta)``. Previous
445+
versions of this function returned three elements
446+
``(data,months_selected,inputs,meta)``. The ``inputs`` dictionary and
447+
``months_selected`` are now included in ``meta``, which has changed
448+
structure to accommodate it.
449+
443450
Parameters
444451
----------
445452
latitude : float
@@ -478,10 +485,6 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
478485
-------
479486
data : pandas.DataFrame
480487
the weather data
481-
months_selected : list
482-
TMY year for each month, ``None`` for EPW
483-
inputs : dict
484-
the inputs, ``None`` for EPW
485488
metadata : list or dict
486489
file metadata
487490
@@ -527,17 +530,16 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
527530
else:
528531
raise requests.HTTPError(err_msg['message'])
529532
# initialize data to None in case API fails to respond to bad outputformat
530-
data = None, None, None, None
533+
data = None, None
531534
if outputformat == 'json':
532535
src = res.json()
533-
data, months_selected, inputs, meta = _parse_pvgis_tmy_json(src)
536+
data, meta = _parse_pvgis_tmy_json(src)
534537
elif outputformat == 'csv':
535538
with io.BytesIO(res.content) as src:
536-
data, months_selected, inputs, meta = _parse_pvgis_tmy_csv(src)
539+
data, meta = _parse_pvgis_tmy_csv(src)
537540
elif outputformat == 'epw':
538541
with io.StringIO(res.content.decode('utf-8')) as src:
539542
data, meta = read_epw(src)
540-
months_selected, inputs = None, None
541543
elif outputformat == 'basic':
542544
err_msg = ("outputformat='basic' is no longer supported by pvlib, "
543545
"please use outputformat='csv' instead.")
@@ -551,34 +553,37 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
551553
coerce_year = coerce_year or 1990
552554
data = _coerce_and_roll_tmy(data, roll_utc_offset, coerce_year)
553555

554-
return data, months_selected, inputs, meta
556+
return data, meta
555557

556558

557559
def _parse_pvgis_tmy_json(src):
558-
inputs = src['inputs']
559-
meta = src['meta']
560-
months_selected = src['outputs']['months_selected']
560+
meta = src['meta'].copy()
561+
# Override the "inputs" in metadata
562+
meta['inputs'] = src['inputs']
563+
# Re-add the inputs in metadata one-layer down
564+
meta['inputs']['descriptions'] = src['meta']['inputs']
565+
meta['months_selected'] = src['outputs']['months_selected']
561566
data = pd.DataFrame(src['outputs']['tmy_hourly'])
562567
data.index = pd.to_datetime(
563568
data['time(UTC)'], format='%Y%m%d:%H%M', utc=True)
564569
data = data.drop('time(UTC)', axis=1)
565-
return data, months_selected, inputs, meta
570+
return data, meta
566571

567572

568573
def _parse_pvgis_tmy_csv(src):
569574
# the first 3 rows are latitude, longitude, elevation
570-
inputs = {}
575+
meta = {'inputs': {}, 'descriptions': {}}
571576
# 'Latitude (decimal degrees): 45.000\r\n'
572-
inputs['latitude'] = float(src.readline().split(b':')[1])
577+
meta['inputs']['latitude'] = float(src.readline().split(b':')[1])
573578
# 'Longitude (decimal degrees): 8.000\r\n'
574-
inputs['longitude'] = float(src.readline().split(b':')[1])
579+
meta['inputs']['longitude'] = float(src.readline().split(b':')[1])
575580
# Elevation (m): 1389.0\r\n
576-
inputs['elevation'] = float(src.readline().split(b':')[1])
581+
meta['inputs']['elevation'] = float(src.readline().split(b':')[1])
577582

578583
# TMY has an extra line here: Irradiance Time Offset (h): 0.1761\r\n
579584
line = src.readline()
580585
if line.startswith(b'Irradiance Time Offset'):
581-
inputs['irradiance time offset'] = float(line.split(b':')[1])
586+
meta['inputs']['irradiance time offset'] = float(line.split(b':')[1])
582587
src.readline() # skip over the "month,year\r\n"
583588
else:
584589
# `line` is already the "month,year\r\n" line, so nothing to do
@@ -589,6 +594,7 @@ def _parse_pvgis_tmy_csv(src):
589594
for month in range(12):
590595
months_selected.append(
591596
{'month': month+1, 'year': int(src.readline().split(b',')[1])})
597+
meta['months_selected'] = months_selected
592598
# then there's the TMY (typical meteorological year) data
593599
# first there's a header row:
594600
# time(UTC),T2m,RH,G(h),Gb(n),Gd(h),IR(h),WS10m,WD10m,SP
@@ -601,14 +607,24 @@ def _parse_pvgis_tmy_csv(src):
601607
data = pd.DataFrame(data, dtype=float)
602608
data.index = dtidx
603609
# finally there's some meta data
604-
meta = [line.decode('utf-8').strip() for line in src.readlines()]
605-
return data, months_selected, inputs, meta
610+
for line in src.readlines():
611+
if ':' in line:
612+
meta['descriptions'][line.split(':')[0]] = \
613+
line.split(':')[1].strip()
614+
return data, meta
606615

607616

608617
def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
609618
"""
610619
Read a TMY file downloaded from PVGIS.
611620
621+
.. versionchanged:: 0.13.0
622+
The function now returns two items ``(data,meta)``. Previous
623+
versions of this function returned three elements
624+
``(data,months_selected,inputs,meta)``. The ``inputs`` dictionary and
625+
``months_selected`` are now included in ``meta``, which has changed
626+
structure to accommodate it.
627+
612628
Parameters
613629
----------
614630
filename : str, pathlib.Path, or file-like buffer
@@ -629,10 +645,6 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
629645
-------
630646
data : pandas.DataFrame
631647
the weather data
632-
months_selected : list
633-
TMY year for each month, ``None`` for EPW
634-
inputs : dict
635-
the inputs, ``None`` for EPW
636648
metadata : list or dict
637649
file metadata
638650
@@ -662,7 +674,6 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
662674
# EPW: use the EPW parser from the pvlib.iotools epw.py module
663675
if outputformat == 'epw':
664676
data, meta = read_epw(filename)
665-
months_selected, inputs = None, None
666677

667678
# NOTE: json and csv output formats have parsers defined as private
668679
# functions in this module
@@ -676,16 +687,14 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
676687
except AttributeError: # str/path has no .read() attribute
677688
with open(str(filename), 'r') as fbuf:
678689
src = json.load(fbuf)
679-
data, months_selected, inputs, meta = _parse_pvgis_tmy_json(src)
690+
data, meta = _parse_pvgis_tmy_json(src)
680691

681692
elif outputformat == 'csv':
682693
try:
683-
data, months_selected, inputs, meta = \
684-
_parse_pvgis_tmy_csv(filename)
694+
data, meta = _parse_pvgis_tmy_csv(filename)
685695
except AttributeError: # str/path has no .read() attribute
686696
with open(str(filename), 'rb') as fbuf:
687-
data, months_selected, inputs, meta = \
688-
_parse_pvgis_tmy_csv(fbuf)
697+
data, meta = _parse_pvgis_tmy_csv(fbuf)
689698

690699
elif outputformat == 'basic':
691700
err_msg = "outputformat='basic' is no longer supported, please use " \
@@ -702,7 +711,7 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
702711
if map_variables:
703712
data = data.rename(columns=VARIABLE_MAP)
704713

705-
return data, months_selected, inputs, meta
714+
return data, meta
706715

707716

708717
def get_pvgis_horizon(latitude, longitude, url=URL, **kwargs):

tests/iotools/test_pvgis.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -440,7 +440,7 @@ def test_get_pvgis_tmy_kwargs(userhorizon_expected):
440440
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
441441
def test_get_pvgis_tmy_coerce_year():
442442
"""test utc_offset and coerce_year work as expected"""
443-
base_case, _, _, _ = get_pvgis_tmy(45, 8) # Turin
443+
base_case, _ = get_pvgis_tmy(45, 8) # Turin
444444
assert str(base_case.index.tz) == 'UTC'
445445
assert base_case.index.name == 'time(UTC)'
446446
noon_test_data = [
@@ -449,7 +449,7 @@ def test_get_pvgis_tmy_coerce_year():
449449
cet_tz = 1 # Turin time is CET
450450
cet_name = 'Etc/GMT-1'
451451
# check indices of rolled data after converting timezone
452-
pvgis_data, _, _, _ = get_pvgis_tmy(45, 8, roll_utc_offset=cet_tz)
452+
pvgis_data, _ = get_pvgis_tmy(45, 8, roll_utc_offset=cet_tz)
453453
jan1_midnight = pd.Timestamp('1990-01-01 00:00:00', tz=cet_name)
454454
dec31_midnight = pd.Timestamp('1990-12-31 23:00:00', tz=cet_name)
455455
assert pvgis_data.index[0] == jan1_midnight
@@ -461,7 +461,7 @@ def test_get_pvgis_tmy_coerce_year():
461461
assert all(test_case == expected)
462462
# repeat tests with year coerced
463463
test_yr = 2021
464-
pvgis_data, _, _, _ = get_pvgis_tmy(
464+
pvgis_data, _ = get_pvgis_tmy(
465465
45, 8, roll_utc_offset=cet_tz, coerce_year=test_yr)
466466
jan1_midnight = pd.Timestamp(f'{test_yr}-01-01 00:00:00', tz=cet_name)
467467
dec31_midnight = pd.Timestamp(f'{test_yr}-12-31 23:00:00', tz=cet_name)
@@ -472,7 +472,7 @@ def test_get_pvgis_tmy_coerce_year():
472472
expected = pvgis_data[pvgis_data.index.month == m+1].iloc[12+cet_tz]
473473
assert all(test_case == expected)
474474
# repeat tests with year coerced but utc offset none or zero
475-
pvgis_data, _, _, _ = get_pvgis_tmy(45, 8, coerce_year=test_yr)
475+
pvgis_data, _ = get_pvgis_tmy(45, 8, coerce_year=test_yr)
476476
jan1_midnight = pd.Timestamp(f'{test_yr}-01-01 00:00:00', tz='UTC')
477477
dec31_midnight = pd.Timestamp(f'{test_yr}-12-31 23:00:00', tz='UTC')
478478
assert pvgis_data.index[0] == jan1_midnight
@@ -556,8 +556,8 @@ def test_get_pvgis_tmy_basic():
556556

557557
@pytest.mark.remote_data
558558
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
559-
def test_get_pvgis_map_variables(pvgis_tmy_mapped_columns):
560-
actual, _, _, _ = get_pvgis_tmy(45, 8, map_variables=True)
559+
def test_get_pvgis_tmy_map_variables(pvgis_tmy_mapped_columns):
560+
actual, _ = get_pvgis_tmy(45, 8, map_variables=True)
561561
assert all(c in pvgis_tmy_mapped_columns for c in actual.columns)
562562

563563

@@ -580,7 +580,7 @@ def test_read_pvgis_horizon_invalid_coords():
580580

581581
def test_read_pvgis_tmy_map_variables(pvgis_tmy_mapped_columns):
582582
fn = TESTS_DATA_DIR / 'tmy_45.000_8.000_2005_2023.json'
583-
actual, _, _, _ = read_pvgis_tmy(fn, map_variables=True)
583+
actual, _ = read_pvgis_tmy(fn, map_variables=True)
584584
assert all(c in pvgis_tmy_mapped_columns for c in actual.columns)
585585

586586

0 commit comments

Comments
 (0)