Skip to content

Commit 611294e

Browse files
committed
Add private shared parse function
1 parent 95c2602 commit 611294e

1 file changed

Lines changed: 56 additions & 70 deletions

File tree

pvlib/iotools/meteonorm.py

Lines changed: 56 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Functions for reading and retrieving data from Meteonorm."""
1+
"""Functions for retrieving data from Meteonorm."""
22

33
import pandas as pd
44
import requests
@@ -38,7 +38,7 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
3838
The Meteonorm data options are described in [1]_ and the API is described
3939
in [2]_. A detailed list of API options can be found in [3]_.
4040
41-
This function supports both historical and forecast data, but not TMY.
41+
This function supports historical and forecast data, but not TMY.
4242
4343
Parameters
4444
----------
@@ -57,35 +57,35 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
5757
endpoint : str
5858
API endpoint, see [3]_. Must be one of:
5959
60-
* '/observation/training' - historical data with a 7-day delay
61-
* '/observation/realtime' - near-real time (past 7-days)
62-
* '/forecast/basic' - forcasts with hourly resolution
63-
* '/forecast/precision' - forecsat with 15-min resolution
60+
* ``'/observation/training'`` - historical data with a 7-day delay
61+
* ``'/observation/realtime'`` - near-real time (past 7-days)
62+
* ``'/forecast/basic'`` - forcast with hourly resolution
63+
* ``'/forecast/precision'`` - forecast with 15-min resolution
6464
6565
parameters : list, optional
6666
List of parameters to request or 'all' to get all parameters. The
67-
default is "all".
68-
surface_tilt: float, default: 0
69-
Tilt angle from horizontal plane.
70-
surface_azimuth: float, default: 180
67+
default is 'all'.
68+
surface_tilt: float, optional
69+
Tilt angle from horizontal plane. The default is 0.
70+
surface_azimuth: float, optional
7171
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
72-
(north=0, east=90, south=180, west=270).
72+
(north=0, east=90, south=180, west=270). The default is 180.
7373
time_step : {'1min', '15min', '1h'}, optional
74-
Frequency of the time series. The default is '15min'. The parameter is
75-
ignored if forcasting data is requested.
74+
Frequency of the time series. The parameter is ignored when requesting
75+
forcasting data. The default is '15min'.
7676
horizon : optional
77-
Specification of the horizon line. Can be either a flat, 'auto', or
77+
Specification of the horizon line. Can be either a 'flat', 'auto', or
7878
a list of 360 horizon elevation angles. The default is 'auto'.
7979
interval_index: bool, optional
8080
Whether the index of the returned data object is of the type
8181
pd.DatetimeIndex or pd.IntervalIndex. This is an experimental feature
8282
which may be removed without warning. The default is False.
83-
map_variables: bool, default: True
83+
map_variables: bool, optional
8484
When true, renames columns of the Dataframe to pvlib variable names
8585
where applicable. The default is True. See variable
8686
:const:`VARIABLE_MAP`.
8787
url: str, optional
88-
Base url of the Meteonorm API. The ``endpoint`` parameter is
88+
Base URL of the Meteonorm API. The ``endpoint`` parameter is
8989
appended to the url. The default is
9090
:const:`pvlib.iotools.meteonorm.URL`.
9191
@@ -98,7 +98,7 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
9898
-------
9999
data : pd.DataFrame
100100
Time series data. The index corresponds to the start (left) of the
101-
interval.
101+
interval unless ``interval_index`` is set to False.
102102
meta : dict
103103
Metadata.
104104
@@ -125,57 +125,40 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
125125
'lon': longitude,
126126
'start': start.strftime('%Y-%m-%dT%H:%M:%SZ'),
127127
'end': end.strftime('%Y-%m-%dT%H:%M:%SZ'),
128+
'parameters': parameters,
128129
'surface_tilt': surface_tilt,
129130
'surface_azimuth': surface_azimuth,
130131
'horizon': horizon,
131-
'parameters': parameters,
132132
}
133133

134-
if 'forecast' not in endpoint.lower():
135-
params['frequency'] = time_step_map.get(time_step, time_step)
136-
137134
# convert list to string with values separated by commas
138135
if not isinstance(params['parameters'], (str, type(None))):
139136
# allow the use of pvlib parameter names
140137
parameter_dict = {v: k for k, v in VARIABLE_MAP.items()}
141138
parameters = [parameter_dict.get(p, p) for p in parameters]
142139
params['parameters'] = ','.join(parameters)
143140

141+
if horizon not in ['auto', 'flat']:
142+
params['horizon'] = ','.join(horizon)
143+
144+
if 'forecast' not in endpoint.lower():
145+
params['frequency'] = time_step_map.get(time_step, time_step)
146+
144147
headers = {"Authorization": f"Bearer {api_key}"}
145148

146149
response = requests.get(
147150
urljoin(url, endpoint), headers=headers, params=params)
148-
151+
print(response)
149152
if not response.ok:
150153
# response.raise_for_status() does not give a useful error message
151154
raise requests.HTTPError(response.json())
152155

153-
data_json = response.json()['values']
154-
# identify empty columns
155-
empty_columns = [k for k, v in data_json.items() if v is None]
156-
# remove empty columns
157-
_ = [data_json.pop(k) for k in empty_columns]
158-
159-
data = pd.DataFrame(data_json)
156+
data, meta = _parse_meteonorm(response, interval_index, map_variables)
160157

161-
# xxx: experimental feature - see parameter description
162-
if interval_index:
163-
data.index = pd.IntervalIndex.from_arrays(
164-
left=pd.to_datetime(response.json()['start_times']),
165-
right=pd.to_datetime(response.json()['end_times']),
166-
closed='both',
167-
)
168-
else:
169-
data.index = pd.to_datetime(response.json()['start_times'])
170-
171-
meta = response.json()['meta']
158+
return data, meta
172159

173-
if map_variables:
174-
data = data.rename(columns=VARIABLE_MAP)
175-
meta['latitude'] = meta.pop('lat')
176-
meta['longitude'] = meta.pop('lon')
177160

178-
return data, meta
161+
TMY_ENDPOINT = 'climate/tmy'
179162

180163

181164
def get_meteonorm_tmy(latitude, longitude, api_key,
@@ -187,15 +170,11 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
187170
future_year=None, interval_index=False,
188171
map_variables=True, url=URL):
189172
"""
190-
Retrieve irradiance and weather data from Meteonorm.
173+
Retrieve TMY irradiance and weather data from Meteonorm.
191174
192175
The Meteonorm data options are described in [1]_ and the API is described
193176
in [2]_. A detailed list of API options can be found in [3]_.
194177
195-
This function supports the endpoints 'realtime' for data for the past 7
196-
days, 'training' for historical data with a delay of 7 days. The function
197-
does not support TMY climate data.
198-
199178
Parameters
200179
----------
201180
latitude: float
@@ -207,11 +186,11 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
207186
parameters: list, optional
208187
List of parameters to request or 'all' to get all parameters. The
209188
default is 'all'.
210-
surface_tilt: float, default: 0
211-
Tilt angle from horizontal plane.
212-
surface_azimuth : float, default: 180
189+
surface_tilt: float, optional
190+
Tilt angle from horizontal plane. The default is 0.
191+
surface_azimuth : float, optional
213192
Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
214-
(north=0, east=90, south=180, west=270).
193+
(north=0, east=90, south=180, west=270). The default is 180.
215194
time_step: {'1min', '1h'}, optional
216195
Frequency of the time series. The default is '1h'.
217196
horizon: optional
@@ -244,13 +223,13 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
244223
Whether the index of the returned data object is of the type
245224
pd.DatetimeIndex or pd.IntervalIndex. This is an experimental feature
246225
which may be removed without warning. The default is False.
247-
map_variables: bool, default: True
226+
map_variables: bool, optional
248227
When true, renames columns of the Dataframe to pvlib variable names
249-
where applicable. The default is True. See variable
250-
:const:`VARIABLE_MAP`.
228+
where applicable. See variable :const:`VARIABLE_MAP`. The default is
229+
True.
251230
url: str, optional.
252-
Base url of the Meteonorm API. 'climate/tmy'` is
253-
appended to the url. The default is:
231+
Base URL of the Meteonorm API. 'climate/tmy'` is
232+
appended to the URL. The default is:
254233
:const:`pvlib.iotools.meteonorm.URL`.
255234
256235
Raises
@@ -262,7 +241,7 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
262241
-------
263242
data : pd.DataFrame
264243
Time series data. The index corresponds to the start (left) of the
265-
interval.
244+
interval unless ``interval_index`` is set to False.
266245
meta : dict
267246
Metadata.
268247
@@ -293,6 +272,16 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
293272
'data_version': data_version,
294273
}
295274

275+
# convert list to string with values separated by commas
276+
if not isinstance(params['parameters'], (str, type(None))):
277+
# allow the use of pvlib parameter names
278+
parameter_dict = {v: k for k, v in VARIABLE_MAP.items()}
279+
parameters = [parameter_dict.get(p, p) for p in parameters]
280+
params['parameters'] = ','.join(parameters)
281+
282+
if horizon not in ['auto', 'flat']:
283+
params['horizon'] = ','.join(horizon)
284+
296285
if turbidity != 'auto':
297286
params['turbidity'] = ','.join(turbidity)
298287

@@ -305,24 +294,21 @@ def get_meteonorm_tmy(latitude, longitude, api_key,
305294
if future_year is not None:
306295
params['future_year'] = future_year
307296

308-
# convert list to string with values separated by commas
309-
if not isinstance(params['parameters'], (str, type(None))):
310-
# allow the use of pvlib parameter names
311-
parameter_dict = {v: k for k, v in VARIABLE_MAP.items()}
312-
parameters = [parameter_dict.get(p, p) for p in parameters]
313-
params['parameters'] = ','.join(parameters)
314-
315297
headers = {"Authorization": f"Bearer {api_key}"}
316298

317-
endpoint = 'climate/tmy'
318-
319299
response = requests.get(
320-
urljoin(url, endpoint), headers=headers, params=params)
300+
urljoin(url, TMY_ENDPOINT), headers=headers, params=params)
321301

322302
if not response.ok:
323303
# response.raise_for_status() does not give a useful error message
324304
raise requests.HTTPError(response.json())
325305

306+
data, meta = _parse_meteonorm(response, interval_index, map_variables)
307+
308+
return data, meta
309+
310+
311+
def _parse_meteonorm(response, interval_index, map_variables):
326312
data_json = response.json()['values']
327313
# identify empty columns
328314
empty_columns = [k for k, v in data_json.items() if v is None]

0 commit comments

Comments
 (0)