Skip to content

Commit 9129ad2

Browse files
committed
Split get_meteonorm into forecast and observation
1 parent b15a170 commit 9129ad2

File tree

5 files changed

+154
-39
lines changed

5 files changed

+154
-39
lines changed

docs/sphinx/source/reference/iotools.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,8 @@ Meteonorm
9898
.. autosummary::
9999
:toctree: generated/
100100

101-
iotools.get_meteonorm
101+
iotools.get_meteonorm_observation
102+
iotools.get_meteonorm_forecast
102103
iotools.get_meteonorm_tmy
103104

104105

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ Bug fixes
2121
Enhancements
2222
~~~~~~~~~~~~
2323
* Add iotools functions to retrieve irradiance and weather data from Meteonorm:
24-
:py:func:`~pvlib.iotools.get_meteonorm` and :py:func:`~pvlib.iotools.get_meteonorm_tmy`.
24+
:py:func:`~pvlib.iotools.get_meteonorm_observation`, :py:func:`~pvlib.iotools.get_meteonorm_forecast`,
25+
and :py:func:`~pvlib.iotools.get_meteonorm_tmy`.
2526
(:pull:`2499`)
2627
* Add :py:func:`pvlib.iotools.get_nasa_power` to retrieve data from NASA POWER free API.
2728
(:pull:`2500`)

pvlib/iotools/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
from pvlib.iotools.solcast import get_solcast_historic # noqa: F401
4040
from pvlib.iotools.solcast import get_solcast_tmy # noqa: F401
4141
from pvlib.iotools.solargis import get_solargis # noqa: F401
42-
from pvlib.iotools.meteonorm import get_meteonorm # noqa: F401
42+
from pvlib.iotools.meteonorm import get_meteonorm_observation # noqa: F401
43+
from pvlib.iotools.meteonorm import get_meteonorm_forecast # noqa: F401
4344
from pvlib.iotools.meteonorm import get_meteonorm_tmy # noqa: F401
4445
from pvlib.iotools.nasa_power import get_nasa_power # noqa: F401

pvlib/iotools/meteonorm.py

Lines changed: 133 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,19 @@
3131
}
3232

3333

34-
def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
35-
parameters='all', *, surface_tilt=0, surface_azimuth=180,
36-
time_step='15min', horizon='auto', interval_index=False,
37-
map_variables=True, url=URL):
34+
def get_meteonorm_observation(
35+
latitude, longitude, start, end, api_key, endpoint='training',
36+
parameters='all', *, surface_tilt=0, surface_azimuth=180,
37+
time_step='15min', horizon='auto', interval_index=False,
38+
map_variables=True, url=URL):
3839
"""
39-
Retrieve irradiance and weather data from Meteonorm.
40+
Retrieve historical and near real-time observational data from Meteonorm.
4041
4142
The Meteonorm data options are described in [1]_ and the API is described
4243
in [2]_. A detailed list of API options can be found in [3]_.
4344
44-
This function supports retrieval of historical and forecast data, but not
45-
TMY.
45+
This function supports retrieval of observation data, either the
46+
'training' or the 'realtime' endpoints.
4647
4748
Parameters
4849
----------
@@ -58,14 +59,11 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
5859
specified, UTC is assumed.
5960
api_key : str
6061
Meteonorm API key.
61-
endpoint : str
62+
endpoint : str, default : training
6263
API endpoint, see [3]_. Must be one of:
6364
64-
* ``'observation/training'`` - historical data with a 7-day delay
65-
* ``'observation/realtime'`` - near-real time (past 7-days)
66-
* ``'forecast/basic'`` - forecast with hourly resolution
67-
* ``'forecast/precision'`` - forecast with 1-min, 15-min, or hourly
68-
resolution
65+
* ``'training'`` - historical data with a 7-day delay
66+
* ``'realtime'`` - near-real time (past 7-days)
6967
7068
parameters : list or 'all', default : 'all'
7169
List of parameters to request or `'all'` to get all parameters.
@@ -75,8 +73,7 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
7573
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
7674
(north=0, east=90, south=180, west=270).
7775
time_step : {'1min', '15min', '1h'}, default : '15min'
78-
Frequency of the time series. The endpoint ``'forecast/basic'`` only
79-
supports ``time_step='1h'``.
76+
Frequency of the time series.
8077
horizon : str or list, default : 'auto'
8178
Specification of the horizon line. Can be either 'flat', 'auto', or
8279
a list of 360 integer horizon elevation angles.
@@ -87,8 +84,7 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
8784
When true, renames columns of the Dataframe to pvlib variable names
8885
where applicable. See variable :const:`VARIABLE_MAP`.
8986
url : str, optional
90-
Base URL of the Meteonorm API. The ``endpoint`` parameter is
91-
appended to the url. The default is
87+
Base URL of the Meteonorm API. The default is
9288
:const:`pvlib.iotools.meteonorm.URL`.
9389
9490
Raises
@@ -107,11 +103,11 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
107103
Examples
108104
--------
109105
>>> # Retrieve historical time series data
110-
>>> df, meta = pvlib.iotools.get_meteonorm( # doctest: +SKIP
106+
>>> df, meta = pvlib.iotools.get_meteonorm_observatrion( # doctest: +SKIP
111107
... latitude=50, longitude=10, # doctest: +SKIP
112108
... start='2023-01-01', end='2025-01-01', # doctest: +SKIP
113109
... api_key='redacted', # doctest: +SKIP
114-
... endpoint='observation/training') # doctest: +SKIP
110+
... endpoint='training') # doctest: +SKIP
115111
116112
See Also
117113
--------
@@ -126,6 +122,120 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
126122
.. [3] `Meteonorm API reference
127123
<https://docs.meteonorm.com/api>`_
128124
"""
125+
endpoint_base = 'observation/'
126+
127+
data, meta = _get_meteonorm(
128+
latitude, longitude, start, end, api_key,
129+
endpoint_base, endpoint,
130+
parameters, surface_tilt, surface_azimuth,
131+
time_step, horizon, interval_index,
132+
map_variables, url)
133+
return data, meta
134+
135+
136+
def get_meteonorm_forecast(
137+
latitude, longitude, start, end, api_key, endpoint='precision',
138+
parameters='all', *, surface_tilt=0, surface_azimuth=180,
139+
time_step='15min', horizon='auto', interval_index=False,
140+
map_variables=True, url=URL):
141+
"""
142+
Retrieve historical and near real-time observational data from Meteonorm.
143+
144+
The Meteonorm data options are described in [1]_ and the API is described
145+
in [2]_. A detailed list of API options can be found in [3]_.
146+
147+
This function supports retrieval of forecasting data, either the
148+
'training' or the 'basic' endpoints.
149+
150+
Parameters
151+
----------
152+
latitude : float
153+
In decimal degrees, north is positive (ISO 19115).
154+
longitude: float
155+
In decimal degrees, east is positive (ISO 19115).
156+
start : datetime like
157+
First timestamp of the requested period. If a timezone is not
158+
specified, UTC is assumed.
159+
end : datetime like
160+
Last timestamp of the requested period. If a timezone is not
161+
specified, UTC is assumed.
162+
api_key : str
163+
Meteonorm API key.
164+
endpoint : str, default : precision
165+
API endpoint, see [3]_. Must be one of:
166+
167+
* ``'precision'`` - forecast with 1-min, 15-min, or hourly
168+
resolution
169+
* ``'basic'`` - forecast with hourly resolution
170+
171+
parameters : list or 'all', default : 'all'
172+
List of parameters to request or `'all'` to get all parameters.
173+
surface_tilt : float, default : 0
174+
Tilt angle from horizontal plane.
175+
surface_azimuth : float, default : 180
176+
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
177+
(north=0, east=90, south=180, west=270).
178+
time_step : {'1min', '15min', '1h'}, default : '15min'
179+
Frequency of the time series. The endpoint ``'basic'`` only
180+
supports ``time_step='1h'``.
181+
horizon : str or list, default : 'auto'
182+
Specification of the horizon line. Can be either 'flat', 'auto', or
183+
a list of 360 integer horizon elevation angles.
184+
interval_index : bool, default : False
185+
Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
186+
This is an experimental feature which may be removed without warning.
187+
map_variables : bool, default : True
188+
When true, renames columns of the Dataframe to pvlib variable names
189+
where applicable. See variable :const:`VARIABLE_MAP`.
190+
url : str, optional
191+
Base URL of the Meteonorm API. The default is
192+
:const:`pvlib.iotools.meteonorm.URL`.
193+
194+
Raises
195+
------
196+
requests.HTTPError
197+
Raises an error when an incorrect request is made.
198+
199+
Returns
200+
-------
201+
data : pd.DataFrame
202+
Time series data. The index corresponds to the start (left) of the
203+
interval unless ``interval_index`` is set to True.
204+
meta : dict
205+
Metadata.
206+
207+
See Also
208+
--------
209+
pvlib.iotools.get_meteonorm_observation,
210+
pvlib.iotools.get_meteonorm_tmy
211+
212+
References
213+
----------
214+
.. [1] `Meteonorm
215+
<https://meteonorm.com/>`_
216+
.. [2] `Meteonorm API
217+
<https://docs.meteonorm.com/docs/getting-started>`_
218+
.. [3] `Meteonorm API reference
219+
<https://docs.meteonorm.com/api>`_
220+
"""
221+
endpoint_base = 'forecast/'
222+
223+
data, meta = _get_meteonorm(
224+
latitude, longitude, start, end, api_key,
225+
endpoint_base, endpoint,
226+
parameters, surface_tilt, surface_azimuth,
227+
time_step, horizon, interval_index,
228+
map_variables, url)
229+
return data, meta
230+
231+
232+
def _get_meteonorm(
233+
latitude, longitude, start, end, api_key,
234+
endpoint_base, endpoint,
235+
parameters, surface_tilt, surface_azimuth,
236+
time_step, horizon, interval_index,
237+
map_variables, url):
238+
129239
# Relative date strings are not yet supported
130240
start = pd.Timestamp(start)
131241
end = pd.Timestamp(end)
@@ -167,7 +277,8 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
167277
headers = {"Authorization": f"Bearer {api_key}"}
168278

169279
response = requests.get(
170-
urljoin(url, endpoint.lstrip('/')), headers=headers, params=params)
280+
urljoin(url, endpoint_base + endpoint.lstrip('/')),
281+
headers=headers, params=params)
171282

172283
if not response.ok:
173284
# response.raise_for_status() does not give a useful error message
@@ -265,7 +376,8 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
265376
266377
See Also
267378
--------
268-
pvlib.iotools.get_meteonorm
379+
pvlib.iotools.get_meteonorm_observation,
380+
pvlib.iotools.get_meteonorm_forecast
269381
270382
References
271383
----------

tests/iotools/test_meteonorm.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -111,12 +111,12 @@ def expected_columns_all():
111111
def test_get_meteonorm_training(
112112
demo_api_key, demo_url, expected_meta, expected_meteonorm_index,
113113
expected_metenorm_data):
114-
data, meta = pvlib.iotools.get_meteonorm(
114+
data, meta = pvlib.iotools.get_meteonorm_observation(
115115
latitude=50, longitude=10,
116116
start='2023-01-01', end='2025-01-01',
117117
api_key=demo_api_key,
118118
parameters=['ghi', 'global_horizontal_irradiance_with_shading'],
119-
endpoint='observation/training',
119+
endpoint='training',
120120
time_step='1h',
121121
url=demo_url)
122122

@@ -128,14 +128,14 @@ def test_get_meteonorm_training(
128128
@pytest.mark.remote_data
129129
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
130130
def test_get_meteonorm_realtime(demo_api_key, demo_url, expected_columns_all):
131-
data, meta = pvlib.iotools.get_meteonorm(
131+
data, meta = pvlib.iotools.get_meteonorm_observation(
132132
latitude=21, longitude=79,
133133
start=pd.Timestamp.now(tz='UTC') - pd.Timedelta(hours=5),
134134
end=pd.Timestamp.now(tz='UTC') - pd.Timedelta(hours=1),
135135
surface_tilt=20, surface_azimuth=10,
136136
parameters=['all'],
137137
api_key=demo_api_key,
138-
endpoint='/observation/realtime',
138+
endpoint='realtime',
139139
time_step='1min',
140140
horizon='flat',
141141
map_variables=False,
@@ -158,14 +158,14 @@ def test_get_meteonorm_realtime(demo_api_key, demo_url, expected_columns_all):
158158
@pytest.mark.remote_data
159159
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
160160
def test_get_meteonorm_forecast_basic(demo_api_key, demo_url):
161-
data, meta = pvlib.iotools.get_meteonorm(
161+
data, meta = pvlib.iotools.get_meteonorm_forecast(
162162
latitude=50, longitude=10,
163163
start=pd.Timestamp.now(tz='UTC'),
164164
end=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=5),
165165
time_step='1h',
166166
api_key=demo_api_key,
167167
parameters='ghi',
168-
endpoint='forecast/basic',
168+
endpoint='basic',
169169
url=demo_url)
170170

171171
assert data.shape == (6, 1)
@@ -177,13 +177,13 @@ def test_get_meteonorm_forecast_basic(demo_api_key, demo_url):
177177
@pytest.mark.remote_data
178178
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
179179
def test_get_meteonorm_forecast_precision(demo_api_key, demo_url):
180-
data, meta = pvlib.iotools.get_meteonorm(
180+
data, meta = pvlib.iotools.get_meteonorm_forecast(
181181
latitude=50, longitude=10,
182182
start=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=5),
183183
end=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=6),
184184
api_key=demo_api_key,
185185
parameters='ghi',
186-
endpoint='forecast/precision',
186+
endpoint='precision',
187187
time_step='15min',
188188
url=demo_url)
189189

@@ -195,45 +195,45 @@ def test_get_meteonorm_forecast_precision(demo_api_key, demo_url):
195195
@pytest.mark.remote_data
196196
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
197197
def test_get_meteonorm_custom_horizon(demo_api_key, demo_url):
198-
data, meta = pvlib.iotools.get_meteonorm(
198+
data, meta = pvlib.iotools.get_meteonorm_forecast(
199199
latitude=50, longitude=10,
200200
start=pd.Timestamp.now(tz='UTC'),
201201
end=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=5),
202202
api_key=demo_api_key,
203203
parameters='ghi',
204204
time_step='1h',
205-
endpoint='forecast/basic',
205+
endpoint='basic',
206206
horizon=list(np.ones(360).astype(int)*80),
207207
url=demo_url)
208208

209209

210210
@pytest.mark.remote_data
211211
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
212-
def test_get_meteonorm_HTTPError(demo_api_key, demo_url):
212+
def test_get_meteonorm_forecast_HTTPError(demo_api_key, demo_url):
213213
with pytest.raises(
214214
HTTPError, match="unknown parameter: not_a_real_parameter"):
215-
_ = pvlib.iotools.get_meteonorm(
215+
_ = pvlib.iotools.get_meteonorm_forecast(
216216
latitude=50, longitude=10,
217217
start=pd.Timestamp.now(tz='UTC'),
218218
end=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=5),
219219
time_step='1h',
220220
api_key=demo_api_key,
221221
parameters='not_a_real_parameter',
222-
endpoint='forecast/basic',
222+
endpoint='basic',
223223
url=demo_url)
224224

225225

226226
def test_get_meteonorm_basic_forecast_incorrect_time_step(
227227
demo_api_key, demo_url):
228228
with pytest.raises(
229229
ValueError, match="only supports ``time_step='1h'``"):
230-
_ = pvlib.iotools.get_meteonorm(
230+
_ = pvlib.iotools.get_meteonorm_forecast(
231231
latitude=50, longitude=10,
232232
start=pd.Timestamp.now(tz='UTC'),
233233
end=pd.Timestamp.now(tz='UTC') + pd.Timedelta(hours=5),
234234
time_step='15min', # only '1h' is supported for tmy
235235
api_key=demo_api_key,
236-
endpoint='forecast/basic',
236+
endpoint='basic',
237237
url=demo_url)
238238

239239

0 commit comments

Comments
 (0)