1- """Functions for reading and retrieving data from Meteonorm."""
1+ """Functions for retrieving data from Meteonorm."""
22
33import pandas as pd
44import 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
181164def 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