2929
3030
3131def get_meteonorm (latitude , longitude , start , end , api_key , endpoint ,
32- parameters = " all" , * , surface_tilt = 0 , surface_azimuth = 180 ,
32+ parameters = ' all' , * , surface_tilt = 0 , surface_azimuth = 180 ,
3333 time_step = '15min' , horizon = 'auto' , interval_index = False ,
3434 map_variables = True , url = URL ):
3535 """
@@ -38,9 +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 the end points 'realtime' for data for the past 7
42- days, 'training' for historical data with a delay of 7 days. The function
43- does not support TMY climate data.
41+ This function supports both historical and forecast data, but not TMY.
4442
4543 Parameters
4644 ----------
@@ -57,28 +55,27 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
5755 api_key: str
5856 Meteonorm API key.
5957 endpoint : str
60- API end point , see [3]_. Must be one of:
58+ API endpoint , see [3]_. Must be one of:
6159
62- * '/observation/training'
63- * '/observation/realtime'
64- * '/forecast/basic'
65- * '/forecast/precision'
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
6664
6765 parameters : list, optional
68- List of parameters to request or " all" to get all parameters. The
66+ List of parameters to request or ' all' to get all parameters. The
6967 default is "all".
7068 surface_tilt: float, default: 0
7169 Tilt angle from horizontal plane.
7270 surface_azimuth: float, default: 180
7371 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
7472 (north=0, east=90, south=180, west=270).
7573 time_step : {'1min', '15min', '1h'}, optional
76- ime step of the time series. The default is '15min'. Ignored if
77- requesting forecast data.
74+ Frequency of the time series. The default is '15min'. The parameter is
75+ ignored if forcasting data is requested .
7876 horizon : optional
79- Specification of the hoirzon line. Can be either 'flat' or 'auto', or
80- specified as a list of 360 horizon elevation angles. The default is
81- 'auto'.
77+ Specification of the horizon line. Can be either a flat, 'auto', or
78+ a list of 360 horizon elevation angles. The default is 'auto'.
8279 interval_index: bool, optional
8380 Whether the index of the returned data object is of the type
8481 pd.DatetimeIndex or pd.IntervalIndex. This is an experimental feature
@@ -87,9 +84,10 @@ 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. The default is True. See variable
8986 :const:`VARIABLE_MAP`.
90- url: str, default: :const:`pvlib.iotools.meteonorm.URL`
87+ url: str, optional
9188 Base url of the Meteonorm API. The ``endpoint`` parameter is
92- appended to the url.
89+ appended to the url. The default is
90+ :const:`pvlib.iotools.meteonorm.URL`.
9391
9492 Raises
9593 ------
@@ -145,7 +143,181 @@ def get_meteonorm(latitude, longitude, start, end, api_key, endpoint,
145143
146144 headers = {"Authorization" : f"Bearer { api_key } " }
147145
148- response = requests .get (urljoin (url , endpoint ), headers = headers , params = params )
146+ response = requests .get (
147+ urljoin (url , endpoint ), headers = headers , params = params )
148+
149+ if not response .ok :
150+ # response.raise_for_status() does not give a useful error message
151+ raise requests .HTTPError (response .json ())
152+
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 )
160+
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' ]
172+
173+ if map_variables :
174+ data = data .rename (columns = VARIABLE_MAP )
175+ meta ['latitude' ] = meta .pop ('lat' )
176+ meta ['longitude' ] = meta .pop ('lon' )
177+
178+ return data , meta
179+
180+
181+ def get_meteonorm_tmy (latitude , longitude , api_key ,
182+ parameters = 'all' , * , surface_tilt = 0 ,
183+ surface_azimuth = 180 , time_step = '15min' , horizon = 'auto' ,
184+ terrain = 'open' , albedo = 0.2 , turbidity = 'auto' ,
185+ random_seed = None , clear_sky_radiation_model = 'esra' ,
186+ data_version = 'latest' , future_scenario = None ,
187+ future_year = None , interval_index = False ,
188+ map_variables = True , url = URL ):
189+ """
190+ Retrieve irradiance and weather data from Meteonorm.
191+
192+ The Meteonorm data options are described in [1]_ and the API is described
193+ in [2]_. A detailed list of API options can be found in [3]_.
194+
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+
199+ Parameters
200+ ----------
201+ latitude: float
202+ In decimal degrees, north is positive (ISO 19115).
203+ longitude: float
204+ In decimal degrees, east is positive (ISO 19115).
205+ api_key: str
206+ Meteonorm API key.
207+ parameters: list, optional
208+ List of parameters to request or 'all' to get all parameters. The
209+ default is 'all'.
210+ surface_tilt: float, default: 0
211+ Tilt angle from horizontal plane.
212+ surface_azimuth : float, default: 180
213+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
214+ (north=0, east=90, south=180, west=270).
215+ time_step: {'1min', '1h'}, optional
216+ Frequency of the time series. The default is '1h'.
217+ horizon: optional
218+ Specification of the hoirzon line. Can be either 'flat' or 'auto', or
219+ specified as a list of 360 horizon elevation angles. The default is
220+ 'auto'.
221+ terrain: string, optional
222+ Local terrain situation. Must be one of: ['open', 'depression',
223+ 'cold_air_lake', 'sea_lake', 'city', 'slope_south',
224+ 'slope_west_east']. The default is 'open'.
225+ albedo: float, optional
226+ Ground albedo. Albedo changes due to snow fall are modelled. The
227+ default is 0.2.
228+ turbidity: list or 'auto', optional
229+ List of 12 monthly mean atmospheric Linke turbidity values. The default
230+ is 'auto'.
231+ random_seed: int, optional
232+ Random seed to be used for stochastic processes. Two identical requests
233+ with the same random seed will yield identical results.
234+ clear_sky_radiation_model : {'esra', 'solis'}
235+ Which clearsky model to use. The default is 'esra'.
236+ data_version : string, optional
237+ Version of Meteonorm climatological data to be used. The default is
238+ 'latest'.
239+ future_scenario: string, optional
240+ Future climate scenario.
241+ future_year : integer, optional
242+ Central year for a 20-year reference period in the future.
243+ interval_index: bool, optional
244+ Whether the index of the returned data object is of the type
245+ pd.DatetimeIndex or pd.IntervalIndex. This is an experimental feature
246+ which may be removed without warning. The default is False.
247+ map_variables: bool, default: True
248+ When true, renames columns of the Dataframe to pvlib variable names
249+ where applicable. The default is True. See variable
250+ :const:`VARIABLE_MAP`.
251+ url: str, optional.
252+ Base url of the Meteonorm API. 'climate/tmy'` is
253+ appended to the url. The default is:
254+ :const:`pvlib.iotools.meteonorm.URL`.
255+
256+ Raises
257+ ------
258+ requests.HTTPError
259+ Raises an error when an incorrect request is made.
260+
261+ Returns
262+ -------
263+ data : pd.DataFrame
264+ Time series data. The index corresponds to the start (left) of the
265+ interval.
266+ meta : dict
267+ Metadata.
268+
269+ See Also
270+ --------
271+ pvlib.iotools.get_meteonorm
272+
273+ References
274+ ----------
275+ .. [1] `Meteonorm
276+ <https://meteonorm.com/>`_
277+ .. [2] `Meteonorm API
278+ <https://docs.meteonorm.com/docs/getting-started>`_
279+ .. [3] `Meteonorm API reference
280+ <https://docs.meteonorm.com/api>`_
281+ """
282+ params = {
283+ 'lat' : latitude ,
284+ 'lon' : longitude ,
285+ 'surface_tilt' : surface_tilt ,
286+ 'surface_azimuth' : surface_azimuth ,
287+ 'frequency' : time_step ,
288+ 'parameters' : parameters ,
289+ 'horizon' : horizon ,
290+ 'terrain' : terrain ,
291+ 'turbidity' : turbidity ,
292+ 'clear_sky_radiation_model' : clear_sky_radiation_model ,
293+ 'data_version' : data_version ,
294+ }
295+
296+ if turbidity != 'auto' :
297+ params ['turbidity' ] = ',' .join (turbidity )
298+
299+ if random_seed is not None :
300+ params ['random_seed' ] = random_seed
301+
302+ if future_scenario is not None :
303+ params ['future_scenario' ] = future_scenario
304+
305+ if future_year is not None :
306+ params ['future_year' ] = future_year
307+
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+
315+ headers = {"Authorization" : f"Bearer { api_key } " }
316+
317+ endpoint = 'climate/tmy'
318+
319+ response = requests .get (
320+ urljoin (url , endpoint ), headers = headers , params = params )
149321
150322 if not response .ok :
151323 # response.raise_for_status() does not give a useful error message
0 commit comments