pvlib 0.13.0__py3-none-any.whl → 0.13.1a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,623 @@
1
+ """Functions for retrieving data from Meteonorm."""
2
+
3
+ import pandas as pd
4
+ import requests
5
+ from urllib.parse import urljoin
6
+ from pandas._libs.tslibs.parsing import DateParseError
7
+
8
+ URL = "https://api.meteonorm.com/v1/"
9
+
10
+ VARIABLE_MAP = {
11
+ "global_horizontal_irradiance": "ghi",
12
+ "diffuse_horizontal_irradiance": "dhi",
13
+ "direct_normal_irradiance": "dni",
14
+ "direct_horizontal_irradiance": "bhi",
15
+ "global_clear_sky_irradiance": "ghi_clear",
16
+ "diffuse_clear_sky_irradiance": "dhi_clear",
17
+ "direct_normal_clear_sky_irradiance": "dni_clear",
18
+ "direct_horizontal_clear_sky_irradiance": "bhi_clear",
19
+ "diffuse_tilted_irradiance": "poa_diffuse",
20
+ "direct_tilted_irradiance": "poa_direct",
21
+ "global_tilted_irradiance": "poa",
22
+ "temperature": "temp_air",
23
+ "dew_point_temperature": "temp_dew",
24
+ }
25
+
26
+ TIME_STEP_MAP = {
27
+ "1h": "1_hour",
28
+ "h": "1_hour",
29
+ "15min": "15_minutes",
30
+ "1min": "1_minute",
31
+ "min": "1_minute",
32
+ }
33
+
34
+
35
+ def get_meteonorm_forecast_basic(
36
+ latitude, longitude, start, end,
37
+ api_key, parameters="all", *,
38
+ surface_tilt=0, surface_azimuth=180,
39
+ horizon="auto", interval_index=False,
40
+ map_variables=True, url=URL):
41
+ """
42
+ Retrieve basic forecast data from Meteonorm.
43
+
44
+ The basic forecast data only supports hourly time step.
45
+
46
+ The Meteonorm data options are described in [1]_ and the API is described
47
+ in [2]_. A detailed list of API options can be found in [3]_.
48
+
49
+
50
+ Parameters
51
+ ----------
52
+ latitude : float
53
+ In decimal degrees, north is positive (ISO 19115).
54
+ longitude: float
55
+ In decimal degrees, east is positive (ISO 19115).
56
+ start : datetime like or str
57
+ First timestamp of the requested period. If a timezone is not
58
+ specified, UTC is assumed. Relative date/time strings are
59
+ also allowed, e.g., 'now' or '+3hours'.
60
+ end : datetime like or str
61
+ Last timestamp of the requested period. If a timezone is not
62
+ specified, UTC is assumed. Relative date/time strings are
63
+ also allowed, e.g., 'now' or '+3hours'.
64
+ api_key : str
65
+ Meteonorm API key.
66
+ parameters : list or 'all', default : 'all'
67
+ List of parameters to request or `'all'` to get all parameters.
68
+ surface_tilt : float, default : 0
69
+ Tilt angle from horizontal plane.
70
+ surface_azimuth : float, default : 180
71
+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
72
+ (north=0, east=90, south=180, west=270).
73
+ horizon : str or list, default : 'auto'
74
+ Specification of the horizon line. Can be either 'flat', 'auto', or
75
+ a list of 360 integer horizon elevation angles.
76
+ interval_index : bool, default : False
77
+ Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
78
+ This is an experimental feature which may be removed without warning.
79
+ map_variables : bool, default : True
80
+ When true, renames columns of the Dataframe to pvlib variable names
81
+ where applicable. See variable :const:`VARIABLE_MAP`.
82
+ url : str, optional
83
+ Base URL of the Meteonorm API. The default is
84
+ :const:`pvlib.iotools.meteonorm.URL`.
85
+
86
+ Raises
87
+ ------
88
+ requests.HTTPError
89
+ Raises an error when an incorrect request is made.
90
+
91
+ Returns
92
+ -------
93
+ data : pd.DataFrame
94
+ Time series data. The index corresponds to the middle of the
95
+ interval unless ``interval_index`` is set to True.
96
+ meta : dict
97
+ Metadata.
98
+
99
+ See Also
100
+ --------
101
+ pvlib.iotools.get_meteonorm_forecast_precision,
102
+ pvlib.iotools.get_meteonorm_observation_realtime,
103
+ pvlib.iotools.get_meteonorm_observation_training,
104
+ pvlib.iotools.get_meteonorm_tmy
105
+
106
+ References
107
+ ----------
108
+ .. [1] `Meteonorm
109
+ <https://meteonorm.com/>`_
110
+ .. [2] `Meteonorm API
111
+ <https://docs.meteonorm.com/docs/getting-started>`_
112
+ .. [3] `Meteonorm API reference
113
+ <https://docs.meteonorm.com/api>`_
114
+ """
115
+ endpoint = "forecast/basic"
116
+ time_step = None
117
+
118
+ data, meta = _get_meteonorm(
119
+ latitude, longitude, start, end,
120
+ api_key, parameters, surface_tilt, surface_azimuth,
121
+ time_step, horizon, interval_index, map_variables,
122
+ url, endpoint)
123
+ return data, meta
124
+
125
+
126
+ def get_meteonorm_forecast_precision(
127
+ latitude, longitude, start, end,
128
+ api_key, parameters="all", *,
129
+ surface_tilt=0, surface_azimuth=180,
130
+ time_step="15min", horizon="auto", interval_index=False,
131
+ map_variables=True, url=URL):
132
+ """
133
+ Retrieve precision forecast data from Meteonorm.
134
+
135
+ The Meteonorm data options are described in [1]_ and the API is described
136
+ in [2]_. A detailed list of API options can be found in [3]_.
137
+
138
+ Parameters
139
+ ----------
140
+ latitude : float
141
+ In decimal degrees, north is positive (ISO 19115).
142
+ longitude: float
143
+ In decimal degrees, east is positive (ISO 19115).
144
+ start : datetime like or str
145
+ First timestamp of the requested period. If a timezone is not
146
+ specified, UTC is assumed. Relative date/time strings are
147
+ also allowed, e.g., 'now' or '+3hours'.
148
+ end : datetime like or str
149
+ Last timestamp of the requested period. If a timezone is not
150
+ specified, UTC is assumed. Relative date/time strings are
151
+ also allowed, e.g., 'now' or '+3hours'.
152
+ api_key : str
153
+ Meteonorm API key.
154
+ parameters : list or 'all', default : 'all'
155
+ List of parameters to request or `'all'` to get all parameters.
156
+ surface_tilt : float, default : 0
157
+ Tilt angle from horizontal plane.
158
+ surface_azimuth : float, default : 180
159
+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
160
+ (north=0, east=90, south=180, west=270).
161
+ time_step : {'1min', '15min', '1h'}, default : '15min'
162
+ Frequency of the time series.
163
+ horizon : str or list, default : 'auto'
164
+ Specification of the horizon line. Can be either 'flat', 'auto', or
165
+ a list of 360 integer horizon elevation angles.
166
+ interval_index : bool, default : False
167
+ Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
168
+ This is an experimental feature which may be removed without warning.
169
+ map_variables : bool, default : True
170
+ When true, renames columns of the Dataframe to pvlib variable names
171
+ where applicable. See variable :const:`VARIABLE_MAP`.
172
+ url : str, optional
173
+ Base URL of the Meteonorm API. The default is
174
+ :const:`pvlib.iotools.meteonorm.URL`.
175
+
176
+ Raises
177
+ ------
178
+ requests.HTTPError
179
+ Raises an error when an incorrect request is made.
180
+
181
+ Returns
182
+ -------
183
+ data : pd.DataFrame
184
+ Time series data. The index corresponds to the middle of the
185
+ interval unless ``interval_index`` is set to True.
186
+ meta : dict
187
+ Metadata.
188
+
189
+ See Also
190
+ --------
191
+ pvlib.iotools.get_meteonorm_forecast_basic,
192
+ pvlib.iotools.get_meteonorm_observation_realtime,
193
+ pvlib.iotools.get_meteonorm_observation_training,
194
+ pvlib.iotools.get_meteonorm_tmy
195
+
196
+ References
197
+ ----------
198
+ .. [1] `Meteonorm
199
+ <https://meteonorm.com/>`_
200
+ .. [2] `Meteonorm API
201
+ <https://docs.meteonorm.com/docs/getting-started>`_
202
+ .. [3] `Meteonorm API reference
203
+ <https://docs.meteonorm.com/api>`_
204
+ """
205
+ endpoint = "forecast/precision"
206
+
207
+ data, meta = _get_meteonorm(
208
+ latitude, longitude, start, end,
209
+ api_key, parameters, surface_tilt, surface_azimuth,
210
+ time_step, horizon, interval_index, map_variables,
211
+ url, endpoint)
212
+ return data, meta
213
+
214
+
215
+ def get_meteonorm_observation_realtime(
216
+ latitude, longitude, start, end,
217
+ api_key, parameters="all", *,
218
+ surface_tilt=0, surface_azimuth=180,
219
+ time_step="15min", horizon="auto", interval_index=False,
220
+ map_variables=True, url=URL):
221
+ """
222
+ Retrieve near real-time observational data from Meteonorm.
223
+
224
+ The Meteonorm data options are described in [1]_ and the API is described
225
+ in [2]_. A detailed list of API options can be found in [3]_.
226
+
227
+ Near-real time is supports data access for the past 7-days.
228
+
229
+ Parameters
230
+ ----------
231
+ latitude : float
232
+ In decimal degrees, north is positive (ISO 19115).
233
+ longitude: float
234
+ In decimal degrees, east is positive (ISO 19115).
235
+ start : datetime like
236
+ First timestamp of the requested period. If a timezone is not
237
+ specified, UTC is assumed.
238
+ end : datetime like
239
+ Last timestamp of the requested period. If a timezone is not
240
+ specified, UTC is assumed.
241
+ api_key : str
242
+ Meteonorm API key.
243
+ parameters : list or 'all', default : 'all'
244
+ List of parameters to request or `'all'` to get all parameters.
245
+ surface_tilt : float, default : 0
246
+ Tilt angle from horizontal plane.
247
+ surface_azimuth : float, default : 180
248
+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
249
+ (north=0, east=90, south=180, west=270).
250
+ time_step : {'1min', '15min', '1h'}, default : '15min'
251
+ Frequency of the time series.
252
+ horizon : str or list, default : 'auto'
253
+ Specification of the horizon line. Can be either 'flat', 'auto', or
254
+ a list of 360 integer horizon elevation angles.
255
+ interval_index : bool, default : False
256
+ Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
257
+ This is an experimental feature which may be removed without warning.
258
+ map_variables : bool, default : True
259
+ When true, renames columns of the Dataframe to pvlib variable names
260
+ where applicable. See variable :const:`VARIABLE_MAP`.
261
+ url : str, optional
262
+ Base URL of the Meteonorm API. The default is
263
+ :const:`pvlib.iotools.meteonorm.URL`.
264
+
265
+ Raises
266
+ ------
267
+ requests.HTTPError
268
+ Raises an error when an incorrect request is made.
269
+
270
+ Returns
271
+ -------
272
+ data : pd.DataFrame
273
+ Time series data. The index corresponds to the middle of the
274
+ interval unless ``interval_index`` is set to True.
275
+ meta : dict
276
+ Metadata.
277
+
278
+ See Also
279
+ --------
280
+ pvlib.iotools.get_meteonorm_forecast_basic,
281
+ pvlib.iotools.get_meteonorm_forecast_precision,
282
+ pvlib.iotools.get_meteonorm_observation_training,
283
+ pvlib.iotools.get_meteonorm_tmy
284
+
285
+
286
+ References
287
+ ----------
288
+ .. [1] `Meteonorm
289
+ <https://meteonorm.com/>`_
290
+ .. [2] `Meteonorm API
291
+ <https://docs.meteonorm.com/docs/getting-started>`_
292
+ .. [3] `Meteonorm API reference
293
+ <https://docs.meteonorm.com/api>`_
294
+ """
295
+ endpoint = "observation/realtime"
296
+
297
+ data, meta = _get_meteonorm(
298
+ latitude, longitude, start, end,
299
+ api_key, parameters, surface_tilt, surface_azimuth,
300
+ time_step, horizon, interval_index, map_variables,
301
+ url, endpoint)
302
+ return data, meta
303
+
304
+
305
+ def get_meteonorm_observation_training(
306
+ latitude, longitude, start, end,
307
+ api_key, parameters="all", *,
308
+ surface_tilt=0, surface_azimuth=180,
309
+ time_step="15min", horizon="auto", interval_index=False,
310
+ map_variables=True, url=URL):
311
+ """
312
+ Retrieve historical observational data from Meteonorm.
313
+
314
+ The Meteonorm data options are described in [1]_ and the API is described
315
+ in [2]_. A detailed list of API options can be found in [3]_.
316
+
317
+ Parameters
318
+ ----------
319
+ latitude : float
320
+ In decimal degrees, north is positive (ISO 19115).
321
+ longitude: float
322
+ In decimal degrees, east is positive (ISO 19115).
323
+ start : datetime like
324
+ First timestamp of the requested period. If a timezone is not
325
+ specified, UTC is assumed.
326
+ end : datetime like
327
+ Last timestamp of the requested period. If a timezone is not
328
+ specified, UTC is assumed.
329
+ api_key : str
330
+ Meteonorm API key.
331
+ parameters : list or 'all', default : 'all'
332
+ List of parameters to request or `'all'` to get all parameters.
333
+ surface_tilt : float, default : 0
334
+ Tilt angle from horizontal plane.
335
+ surface_azimuth : float, default : 180
336
+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
337
+ (north=0, east=90, south=180, west=270).
338
+ time_step : {'1min', '15min', '1h'}, default : '15min'
339
+ Frequency of the time series.
340
+ horizon : str or list, default : 'auto'
341
+ Specification of the horizon line. Can be either 'flat', 'auto', or
342
+ a list of 360 integer horizon elevation angles.
343
+ interval_index : bool, default : False
344
+ Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
345
+ This is an experimental feature which may be removed without warning.
346
+ map_variables : bool, default : True
347
+ When true, renames columns of the Dataframe to pvlib variable names
348
+ where applicable. See variable :const:`VARIABLE_MAP`.
349
+ url : str, optional
350
+ Base URL of the Meteonorm API. The default is
351
+ :const:`pvlib.iotools.meteonorm.URL`.
352
+
353
+ Raises
354
+ ------
355
+ requests.HTTPError
356
+ Raises an error when an incorrect request is made.
357
+
358
+ Returns
359
+ -------
360
+ data : pd.DataFrame
361
+ Time series data. The index corresponds to the middle of the
362
+ interval unless ``interval_index`` is set to True.
363
+ meta : dict
364
+ Metadata.
365
+
366
+ Examples
367
+ --------
368
+ >>> # Retrieve historical time series data
369
+ >>> df, meta = pvlib.iotools.get_meteonorm_observation_training( # doctest: +SKIP
370
+ ... latitude=50, longitude=10, # doctest: +SKIP
371
+ ... start='2023-01-01', end='2025-01-01', # doctest: +SKIP
372
+ ... api_key='redacted') # doctest: +SKIP
373
+
374
+ See Also
375
+ --------
376
+ pvlib.iotools.get_meteonorm_forecast_basic,
377
+ pvlib.iotools.get_meteonorm_forecast_precision,
378
+ pvlib.iotools.get_meteonorm_observation_realtime,
379
+ pvlib.iotools.get_meteonorm_tmy
380
+
381
+ References
382
+ ----------
383
+ .. [1] `Meteonorm
384
+ <https://meteonorm.com/>`_
385
+ .. [2] `Meteonorm API
386
+ <https://docs.meteonorm.com/docs/getting-started>`_
387
+ .. [3] `Meteonorm API reference
388
+ <https://docs.meteonorm.com/api>`_
389
+ """ # noqa: E501
390
+ endpoint = "observation/training"
391
+
392
+ data, meta = _get_meteonorm(
393
+ latitude, longitude, start, end,
394
+ api_key, parameters, surface_tilt, surface_azimuth,
395
+ time_step, horizon, interval_index, map_variables,
396
+ url, endpoint)
397
+ return data, meta
398
+
399
+
400
+ def get_meteonorm_tmy(
401
+ latitude, longitude, api_key, parameters="all", *,
402
+ surface_tilt=0, surface_azimuth=180,
403
+ time_step="1h", horizon="auto", terrain_situation="open",
404
+ albedo=None, turbidity="auto", random_seed=None,
405
+ clear_sky_radiation_model="esra", data_version="latest",
406
+ future_scenario=None, future_year=None, interval_index=False,
407
+ map_variables=True, url=URL):
408
+ """
409
+ Retrieve TMY irradiance and weather data from Meteonorm.
410
+
411
+ The Meteonorm data options are described in [1]_ and the API is described
412
+ in [2]_. A detailed list of API options can be found in [3]_.
413
+
414
+ Parameters
415
+ ----------
416
+ latitude : float
417
+ In decimal degrees, north is positive (ISO 19115).
418
+ longitude : float
419
+ In decimal degrees, east is positive (ISO 19115).
420
+ api_key : str
421
+ Meteonorm API key.
422
+ parameters : list or 'all', default : 'all'
423
+ List of parameters to request or `'all'` to get all parameters.
424
+ surface_tilt : float, default : 0
425
+ Tilt angle from horizontal plane.
426
+ surface_azimuth : float, default : 180
427
+ Orientation (azimuth angle) of the (fixed) plane. Clockwise from north
428
+ (north=0, east=90, south=180, west=270).
429
+ time_step : {'1min', '1h'}, default : '1h'
430
+ Frequency of the time series.
431
+ horizon : str, optional
432
+ Specification of the horizon line. Can be either 'flat' or 'auto', or
433
+ specified as a list of 360 integer horizon elevation angles.
434
+ 'auto'.
435
+ terrain_situation : str, default : 'open'
436
+ Local terrain situation. Must be one of: ['open', 'depression',
437
+ 'cold_air_lake', 'sea_lake', 'city', 'slope_south',
438
+ 'slope_west_east'].
439
+ albedo : float, optional
440
+ Constant ground albedo. If no value is specified a baseline albedo of
441
+ 0.2 is used and albedo changes due to snow fall are modeled. If a value
442
+ is specified, then snow fall is not modeled.
443
+ turbidity : list or 'auto', optional
444
+ List of 12 monthly mean atmospheric Linke turbidity values. The default
445
+ is 'auto'.
446
+ random_seed : int, optional
447
+ Random seed to be used for stochastic processes. Two identical requests
448
+ with the same random seed will yield identical results.
449
+ clear_sky_radiation_model : str, default : 'esra'
450
+ Which clearsky model to use. Must be either `'esra'` or `'solis'`.
451
+ data_version : str, default : 'latest'
452
+ Version of Meteonorm climatological data to be used.
453
+ future_scenario : str, optional
454
+ Future climate scenario.
455
+ future_year : int, optional
456
+ Central year for a 20-year reference period in the future.
457
+ interval_index : bool, default : False
458
+ Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True.
459
+ This is an experimental feature which may be removed without warning.
460
+ map_variables : bool, default : True
461
+ When true, renames columns of the Dataframe to pvlib variable names
462
+ where applicable. See variable :const:`VARIABLE_MAP`.
463
+ url : str, optional.
464
+ Base URL of the Meteonorm API. `'climate/tmy'` is
465
+ appended to the URL. The default is:
466
+ :const:`pvlib.iotools.meteonorm.URL`.
467
+
468
+ Raises
469
+ ------
470
+ requests.HTTPError
471
+ Raises an error when an incorrect request is made.
472
+
473
+ Returns
474
+ -------
475
+ data : pd.DataFrame
476
+ Time series data. The index corresponds to the middle of the
477
+ interval unless ``interval_index`` is set to True.
478
+ meta : dict
479
+ Metadata.
480
+
481
+ See Also
482
+ --------
483
+ pvlib.iotools.get_meteonorm_forecast_basic,
484
+ pvlib.iotools.get_meteonorm_forecast_precision,
485
+ pvlib.iotools.get_meteonorm_observation_realtime,
486
+ pvlib.iotools.get_meteonorm_observation_training
487
+
488
+ References
489
+ ----------
490
+ .. [1] `Meteonorm
491
+ <https://meteonorm.com/>`_
492
+ .. [2] `Meteonorm API
493
+ <https://docs.meteonorm.com/docs/getting-started>`_
494
+ .. [3] `Meteonorm API reference
495
+ <https://docs.meteonorm.com/api>`_
496
+ """
497
+ additional_params = {
498
+ "situation": terrain_situation,
499
+ "turbidity": turbidity,
500
+ "clear_sky_radiation_model": clear_sky_radiation_model,
501
+ "data_version": data_version,
502
+ "random_seed": random_seed,
503
+ "future_scenario": future_scenario,
504
+ "future_year": future_year,
505
+ "response_format": "json",
506
+ }
507
+
508
+ if not isinstance(turbidity, str):
509
+ additional_params["turbidity"] = ",".join(map(str, turbidity))
510
+
511
+ endpoint = "climate/tmy"
512
+
513
+ start, end = None, None
514
+
515
+ data, meta = _get_meteonorm(
516
+ latitude, longitude, start, end,
517
+ api_key, parameters,
518
+ surface_tilt, surface_azimuth,
519
+ time_step, horizon,
520
+ interval_index, map_variables,
521
+ url, endpoint, **additional_params)
522
+ return data, meta
523
+
524
+
525
+ def _get_meteonorm(
526
+ latitude, longitude, start, end,
527
+ api_key, parameters,
528
+ surface_tilt, surface_azimuth,
529
+ time_step, horizon,
530
+ interval_index, map_variables,
531
+ url, endpoint, **kwargs):
532
+
533
+ # Check for None type in case of TMY request
534
+ # Check for DateParseError in case of relative times, e.g., '+3hours'
535
+ # TODO: remove ValueError when our minimum pandas version is high enough
536
+ # to make it unnecessary (2.0?)
537
+ if (start is not None) & (start != 'now'):
538
+ try:
539
+ start = pd.Timestamp(start)
540
+ start = start.tz_localize("UTC") if start.tzinfo is None else start
541
+ start = start.strftime("%Y-%m-%dT%H:%M:%SZ")
542
+ except (ValueError, DateParseError):
543
+ pass
544
+ if (end is not None) & (end != 'now'):
545
+ try:
546
+ end = pd.Timestamp(end)
547
+ end = end.tz_localize("UTC") if end.tzinfo is None else end
548
+ end = end.strftime("%Y-%m-%dT%H:%M:%SZ")
549
+ except (ValueError, DateParseError):
550
+ pass
551
+
552
+ params = {
553
+ "lat": latitude,
554
+ "lon": longitude,
555
+ 'start': start,
556
+ 'end': end,
557
+ "parameters": parameters,
558
+ "surface_tilt": surface_tilt,
559
+ "surface_azimuth": surface_azimuth,
560
+ "horizon": horizon,
561
+ 'frequency': TIME_STEP_MAP.get(time_step, time_step),
562
+ "response_format": "json",
563
+ **kwargs
564
+ }
565
+
566
+ # Allow specifying single parameters as string
567
+ if isinstance(parameters, str):
568
+ parameters = [parameters]
569
+
570
+ # allow the use of pvlib parameter names
571
+ parameter_dict = {v: k for k, v in VARIABLE_MAP.items()}
572
+ parameters = [parameter_dict.get(p, p) for p in parameters]
573
+ # convert list to string with values separated by commas
574
+ params["parameters"] = ",".join(parameters)
575
+
576
+ if not isinstance(horizon, str):
577
+ params["horizon"] = ",".join(map(str, horizon))
578
+
579
+ headers = {"Authorization": f"Bearer {api_key}"}
580
+
581
+ response = requests.get(
582
+ urljoin(url, endpoint), headers=headers, params=params
583
+ )
584
+
585
+ if not response.ok:
586
+ # response.raise_for_status() does not give a useful error message
587
+ raise requests.HTTPError(
588
+ "Meteonorm API returned an error: "
589
+ + response.json()["error"]["message"]
590
+ )
591
+
592
+ data, meta = _parse_meteonorm(response, interval_index, map_variables)
593
+
594
+ return data, meta
595
+
596
+
597
+ def _parse_meteonorm(response, interval_index, map_variables):
598
+ data_json = response.json()["values"]
599
+ # identify empty columns
600
+ empty_columns = [k for k, v in data_json.items() if v is None]
601
+ # remove empty columns
602
+ _ = [data_json.pop(k) for k in empty_columns]
603
+
604
+ data = pd.DataFrame(data_json)
605
+
606
+ # xxx: experimental feature - see parameter description
607
+ data.index = pd.IntervalIndex.from_arrays(
608
+ left=pd.to_datetime(response.json()["start_times"]),
609
+ right=pd.to_datetime(response.json()["end_times"]),
610
+ closed="left",
611
+ )
612
+
613
+ if not interval_index:
614
+ data.index = data.index.mid
615
+
616
+ meta = response.json()["meta"]
617
+
618
+ if map_variables:
619
+ data = data.rename(columns=VARIABLE_MAP)
620
+ meta["latitude"] = meta.pop("lat")
621
+ meta["longitude"] = meta.pop("lon")
622
+
623
+ return data, meta