Source code for pvlib.iotools.meteonorm

"""Functions for retrieving data from Meteonorm."""

import pandas as pd
import requests
from urllib.parse import urljoin
from pandas._libs.tslibs.parsing import DateParseError

URL = "https://api.meteonorm.com/v1/"

VARIABLE_MAP = {
    "global_horizontal_irradiance": "ghi",
    "diffuse_horizontal_irradiance": "dhi",
    "direct_normal_irradiance": "dni",
    "direct_horizontal_irradiance": "bhi",
    "global_clear_sky_irradiance": "ghi_clear",
    "diffuse_clear_sky_irradiance": "dhi_clear",
    "direct_normal_clear_sky_irradiance": "dni_clear",
    "direct_horizontal_clear_sky_irradiance": "bhi_clear",
    "diffuse_tilted_irradiance": "poa_diffuse",
    "direct_tilted_irradiance": "poa_direct",
    "global_tilted_irradiance": "poa",
    "temperature": "temp_air",
    "dew_point_temperature": "temp_dew",
}

TIME_STEP_MAP = {
    "1h": "1_hour",
    "h": "1_hour",
    "15min": "15_minutes",
    "1min": "1_minute",
    "min": "1_minute",
}


[docs] def get_meteonorm_forecast_basic( latitude, longitude, start, end, api_key, parameters="all", *, surface_tilt=0, surface_azimuth=180, horizon="auto", interval_index=False, map_variables=True, url=URL): """ Retrieve basic forecast data from Meteonorm. The basic forecast data only supports hourly time step. The Meteonorm data options are described in [1]_ and the API is described in [2]_. A detailed list of API options can be found in [3]_. Parameters ---------- latitude : float In decimal degrees, north is positive (ISO 19115). longitude: float In decimal degrees, east is positive (ISO 19115). start : datetime like or str First timestamp of the requested period. If a timezone is not specified, UTC is assumed. Relative date/time strings are also allowed, e.g., 'now' or '+3hours'. end : datetime like or str Last timestamp of the requested period. If a timezone is not specified, UTC is assumed. Relative date/time strings are also allowed, e.g., 'now' or '+3hours'. api_key : str Meteonorm API key. parameters : list or 'all', default : 'all' List of parameters to request or `'all'` to get all parameters. surface_tilt : float, default : 0 Tilt angle from horizontal plane. surface_azimuth : float, default : 180 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). horizon : str or list, default : 'auto' Specification of the horizon line. Can be either 'flat', 'auto', or a list of 360 integer horizon elevation angles. interval_index : bool, default : False Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True. This is an experimental feature which may be removed without warning. map_variables : bool, default : True When true, renames columns of the Dataframe to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. url : str, optional Base URL of the Meteonorm API. The default is :const:`pvlib.iotools.meteonorm.URL`. Raises ------ requests.HTTPError Raises an error when an incorrect request is made. Returns ------- data : pd.DataFrame Time series data. The index corresponds to the middle of the interval unless ``interval_index`` is set to True. meta : dict Metadata. See Also -------- pvlib.iotools.get_meteonorm_forecast_precision, pvlib.iotools.get_meteonorm_observation_realtime, pvlib.iotools.get_meteonorm_observation_training, pvlib.iotools.get_meteonorm_tmy References ---------- .. [1] `Meteonorm <https://meteonorm.com/>`_ .. [2] `Meteonorm API <https://docs.meteonorm.com/docs/getting-started>`_ .. [3] `Meteonorm API reference <https://docs.meteonorm.com/api>`_ """ endpoint = "forecast/basic" time_step = None data, meta = _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint) return data, meta
[docs] def get_meteonorm_forecast_precision( latitude, longitude, start, end, api_key, parameters="all", *, surface_tilt=0, surface_azimuth=180, time_step="15min", horizon="auto", interval_index=False, map_variables=True, url=URL): """ Retrieve precision forecast data from Meteonorm. The Meteonorm data options are described in [1]_ and the API is described in [2]_. A detailed list of API options can be found in [3]_. Parameters ---------- latitude : float In decimal degrees, north is positive (ISO 19115). longitude: float In decimal degrees, east is positive (ISO 19115). start : datetime like or str First timestamp of the requested period. If a timezone is not specified, UTC is assumed. Relative date/time strings are also allowed, e.g., 'now' or '+3hours'. end : datetime like or str Last timestamp of the requested period. If a timezone is not specified, UTC is assumed. Relative date/time strings are also allowed, e.g., 'now' or '+3hours'. api_key : str Meteonorm API key. parameters : list or 'all', default : 'all' List of parameters to request or `'all'` to get all parameters. surface_tilt : float, default : 0 Tilt angle from horizontal plane. surface_azimuth : float, default : 180 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). time_step : {'1min', '15min', '1h'}, default : '15min' Frequency of the time series. horizon : str or list, default : 'auto' Specification of the horizon line. Can be either 'flat', 'auto', or a list of 360 integer horizon elevation angles. interval_index : bool, default : False Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True. This is an experimental feature which may be removed without warning. map_variables : bool, default : True When true, renames columns of the Dataframe to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. url : str, optional Base URL of the Meteonorm API. The default is :const:`pvlib.iotools.meteonorm.URL`. Raises ------ requests.HTTPError Raises an error when an incorrect request is made. Returns ------- data : pd.DataFrame Time series data. The index corresponds to the middle of the interval unless ``interval_index`` is set to True. meta : dict Metadata. See Also -------- pvlib.iotools.get_meteonorm_forecast_basic, pvlib.iotools.get_meteonorm_observation_realtime, pvlib.iotools.get_meteonorm_observation_training, pvlib.iotools.get_meteonorm_tmy References ---------- .. [1] `Meteonorm <https://meteonorm.com/>`_ .. [2] `Meteonorm API <https://docs.meteonorm.com/docs/getting-started>`_ .. [3] `Meteonorm API reference <https://docs.meteonorm.com/api>`_ """ endpoint = "forecast/precision" data, meta = _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint) return data, meta
[docs] def get_meteonorm_observation_realtime( latitude, longitude, start, end, api_key, parameters="all", *, surface_tilt=0, surface_azimuth=180, time_step="15min", horizon="auto", interval_index=False, map_variables=True, url=URL): """ Retrieve near real-time observational data from Meteonorm. The Meteonorm data options are described in [1]_ and the API is described in [2]_. A detailed list of API options can be found in [3]_. Near-real time is supports data access for the past 7-days. Parameters ---------- latitude : float In decimal degrees, north is positive (ISO 19115). longitude: float In decimal degrees, east is positive (ISO 19115). start : datetime like First timestamp of the requested period. If a timezone is not specified, UTC is assumed. end : datetime like Last timestamp of the requested period. If a timezone is not specified, UTC is assumed. api_key : str Meteonorm API key. parameters : list or 'all', default : 'all' List of parameters to request or `'all'` to get all parameters. surface_tilt : float, default : 0 Tilt angle from horizontal plane. surface_azimuth : float, default : 180 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). time_step : {'1min', '15min', '1h'}, default : '15min' Frequency of the time series. horizon : str or list, default : 'auto' Specification of the horizon line. Can be either 'flat', 'auto', or a list of 360 integer horizon elevation angles. interval_index : bool, default : False Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True. This is an experimental feature which may be removed without warning. map_variables : bool, default : True When true, renames columns of the Dataframe to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. url : str, optional Base URL of the Meteonorm API. The default is :const:`pvlib.iotools.meteonorm.URL`. Raises ------ requests.HTTPError Raises an error when an incorrect request is made. Returns ------- data : pd.DataFrame Time series data. The index corresponds to the middle of the interval unless ``interval_index`` is set to True. meta : dict Metadata. See Also -------- pvlib.iotools.get_meteonorm_forecast_basic, pvlib.iotools.get_meteonorm_forecast_precision, pvlib.iotools.get_meteonorm_observation_training, pvlib.iotools.get_meteonorm_tmy References ---------- .. [1] `Meteonorm <https://meteonorm.com/>`_ .. [2] `Meteonorm API <https://docs.meteonorm.com/docs/getting-started>`_ .. [3] `Meteonorm API reference <https://docs.meteonorm.com/api>`_ """ endpoint = "observation/realtime" data, meta = _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint) return data, meta
[docs] def get_meteonorm_observation_training( latitude, longitude, start, end, api_key, parameters="all", *, surface_tilt=0, surface_azimuth=180, time_step="15min", horizon="auto", interval_index=False, map_variables=True, url=URL): """ Retrieve historical observational data from Meteonorm. The Meteonorm data options are described in [1]_ and the API is described in [2]_. A detailed list of API options can be found in [3]_. Parameters ---------- latitude : float In decimal degrees, north is positive (ISO 19115). longitude: float In decimal degrees, east is positive (ISO 19115). start : datetime like First timestamp of the requested period. If a timezone is not specified, UTC is assumed. end : datetime like Last timestamp of the requested period. If a timezone is not specified, UTC is assumed. api_key : str Meteonorm API key. parameters : list or 'all', default : 'all' List of parameters to request or `'all'` to get all parameters. surface_tilt : float, default : 0 Tilt angle from horizontal plane. surface_azimuth : float, default : 180 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). time_step : {'1min', '15min', '1h'}, default : '15min' Frequency of the time series. horizon : str or list, default : 'auto' Specification of the horizon line. Can be either 'flat', 'auto', or a list of 360 integer horizon elevation angles. interval_index : bool, default : False Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True. This is an experimental feature which may be removed without warning. map_variables : bool, default : True When true, renames columns of the Dataframe to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. url : str, optional Base URL of the Meteonorm API. The default is :const:`pvlib.iotools.meteonorm.URL`. Raises ------ requests.HTTPError Raises an error when an incorrect request is made. Returns ------- data : pd.DataFrame Time series data. The index corresponds to the middle of the interval unless ``interval_index`` is set to True. meta : dict Metadata. Examples -------- >>> # Retrieve historical time series data >>> df, meta = pvlib.iotools.get_meteonorm_observation_training( # doctest: +SKIP ... latitude=50, longitude=10, # doctest: +SKIP ... start='2023-01-01', end='2025-01-01', # doctest: +SKIP ... api_key='redacted') # doctest: +SKIP See Also -------- pvlib.iotools.get_meteonorm_forecast_basic, pvlib.iotools.get_meteonorm_forecast_precision, pvlib.iotools.get_meteonorm_observation_realtime, pvlib.iotools.get_meteonorm_tmy References ---------- .. [1] `Meteonorm <https://meteonorm.com/>`_ .. [2] `Meteonorm API <https://docs.meteonorm.com/docs/getting-started>`_ .. [3] `Meteonorm API reference <https://docs.meteonorm.com/api>`_ """ # noqa: E501 endpoint = "observation/training" data, meta = _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint) return data, meta
[docs] def get_meteonorm_tmy( latitude, longitude, api_key, parameters="all", *, surface_tilt=0, surface_azimuth=180, time_step="1h", horizon="auto", terrain_situation="open", albedo=None, turbidity="auto", random_seed=None, clear_sky_radiation_model="esra", data_version="latest", future_scenario=None, future_year=None, interval_index=False, map_variables=True, url=URL): """ Retrieve TMY irradiance and weather data from Meteonorm. The Meteonorm data options are described in [1]_ and the API is described in [2]_. A detailed list of API options can be found in [3]_. Parameters ---------- latitude : float In decimal degrees, north is positive (ISO 19115). longitude : float In decimal degrees, east is positive (ISO 19115). api_key : str Meteonorm API key. parameters : list or 'all', default : 'all' List of parameters to request or `'all'` to get all parameters. surface_tilt : float, default : 0 Tilt angle from horizontal plane. surface_azimuth : float, default : 180 Orientation (azimuth angle) of the (fixed) plane. Clockwise from north (north=0, east=90, south=180, west=270). time_step : {'1min', '1h'}, default : '1h' Frequency of the time series. horizon : str, optional Specification of the horizon line. Can be either 'flat' or 'auto', or specified as a list of 360 integer horizon elevation angles. 'auto'. terrain_situation : str, default : 'open' Local terrain situation. Must be one of: ['open', 'depression', 'cold_air_lake', 'sea_lake', 'city', 'slope_south', 'slope_west_east']. albedo : float, optional Constant ground albedo. If no value is specified a baseline albedo of 0.2 is used and albedo changes due to snow fall are modeled. If a value is specified, then snow fall is not modeled. turbidity : list or 'auto', optional List of 12 monthly mean atmospheric Linke turbidity values. The default is 'auto'. random_seed : int, optional Random seed to be used for stochastic processes. Two identical requests with the same random seed will yield identical results. clear_sky_radiation_model : str, default : 'esra' Which clearsky model to use. Must be either `'esra'` or `'solis'`. data_version : str, default : 'latest' Version of Meteonorm climatological data to be used. future_scenario : str, optional Future climate scenario. future_year : int, optional Central year for a 20-year reference period in the future. interval_index : bool, default : False Index is pd.DatetimeIndex when False, and pd.IntervalIndex when True. This is an experimental feature which may be removed without warning. map_variables : bool, default : True When true, renames columns of the Dataframe to pvlib variable names where applicable. See variable :const:`VARIABLE_MAP`. url : str, optional. Base URL of the Meteonorm API. `'climate/tmy'` is appended to the URL. The default is: :const:`pvlib.iotools.meteonorm.URL`. Raises ------ requests.HTTPError Raises an error when an incorrect request is made. Returns ------- data : pd.DataFrame Time series data. The index corresponds to the middle of the interval unless ``interval_index`` is set to True. meta : dict Metadata. See Also -------- pvlib.iotools.get_meteonorm_forecast_basic, pvlib.iotools.get_meteonorm_forecast_precision, pvlib.iotools.get_meteonorm_observation_realtime, pvlib.iotools.get_meteonorm_observation_training References ---------- .. [1] `Meteonorm <https://meteonorm.com/>`_ .. [2] `Meteonorm API <https://docs.meteonorm.com/docs/getting-started>`_ .. [3] `Meteonorm API reference <https://docs.meteonorm.com/api>`_ """ additional_params = { "situation": terrain_situation, "turbidity": turbidity, "clear_sky_radiation_model": clear_sky_radiation_model, "data_version": data_version, "random_seed": random_seed, "future_scenario": future_scenario, "future_year": future_year, "response_format": "json", } if not isinstance(turbidity, str): additional_params["turbidity"] = ",".join(map(str, turbidity)) endpoint = "climate/tmy" start, end = None, None data, meta = _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint, **additional_params) return data, meta
def _get_meteonorm( latitude, longitude, start, end, api_key, parameters, surface_tilt, surface_azimuth, time_step, horizon, interval_index, map_variables, url, endpoint, **kwargs): # Check for None type in case of TMY request # Check for DateParseError in case of relative times, e.g., '+3hours' if (start is not None) & (start != 'now'): try: start = pd.Timestamp(start) start = start.tz_localize("UTC") if start.tzinfo is None else start start = start.strftime("%Y-%m-%dT%H:%M:%SZ") except DateParseError: pass if (end is not None) & (end != 'now'): try: end = pd.Timestamp(end) end = end.tz_localize("UTC") if end.tzinfo is None else end end = end.strftime("%Y-%m-%dT%H:%M:%SZ") except DateParseError: pass params = { "lat": latitude, "lon": longitude, 'start': start, 'end': end, "parameters": parameters, "surface_tilt": surface_tilt, "surface_azimuth": surface_azimuth, "horizon": horizon, 'frequency': TIME_STEP_MAP.get(time_step, time_step), "response_format": "json", **kwargs } # Allow specifying single parameters as string if isinstance(parameters, str): parameters = [parameters] # allow the use of pvlib parameter names parameter_dict = {v: k for k, v in VARIABLE_MAP.items()} parameters = [parameter_dict.get(p, p) for p in parameters] # convert list to string with values separated by commas params["parameters"] = ",".join(parameters) if not isinstance(horizon, str): params["horizon"] = ",".join(map(str, horizon)) headers = {"Authorization": f"Bearer {api_key}"} response = requests.get( urljoin(url, endpoint), headers=headers, params=params ) if not response.ok: # response.raise_for_status() does not give a useful error message raise requests.HTTPError( "Meteonorm API returned an error: " + response.json()["error"]["message"] ) data, meta = _parse_meteonorm(response, interval_index, map_variables) return data, meta def _parse_meteonorm(response, interval_index, map_variables): data_json = response.json()["values"] # identify empty columns empty_columns = [k for k, v in data_json.items() if v is None] # remove empty columns _ = [data_json.pop(k) for k in empty_columns] data = pd.DataFrame(data_json) # xxx: experimental feature - see parameter description data.index = pd.IntervalIndex.from_arrays( left=pd.to_datetime(response.json()["start_times"]), right=pd.to_datetime(response.json()["end_times"]), closed="left", ) if not interval_index: data.index = data.index.mid meta = response.json()["meta"] if map_variables: data = data.rename(columns=VARIABLE_MAP) meta["latitude"] = meta.pop("lat") meta["longitude"] = meta.pop("lon") return data, meta