""" Functions to access data from the Solcast API.
"""
import requests
import pandas as pd
from dataclasses import dataclass
BASE_URL = "https://api.solcast.com.au/data"
@dataclass
class ParameterMap:
solcast_name: str
pvlib_name: str
conversion: callable = lambda x: x
# define the conventions between Solcast and pvlib nomenclature and units
VARIABLE_MAP = [
# air_temp -> temp_air (deg C)
ParameterMap("air_temp", "temp_air"),
# surface_pressure (hPa) -> pressure (Pa)
ParameterMap("surface_pressure", "pressure", lambda x: x*100),
# dewpoint_temp -> temp_dew (deg C)
ParameterMap("dewpoint_temp", "temp_dew"),
# gti (W/m^2) -> poa_global (W/m^2)
ParameterMap("gti", "poa_global"),
# wind_speed_10m (m/s) -> wind_speed (m/s)
ParameterMap("wind_speed_10m", "wind_speed"),
# wind_direction_10m (deg) -> wind_direction (deg)
ParameterMap("wind_direction_10m", "wind_direction"),
# azimuth -> solar_azimuth (degrees) (different convention)
ParameterMap(
"azimuth", "solar_azimuth", lambda x: -x % 360
),
# precipitable_water (kg/m2) -> precipitable_water (cm)
ParameterMap("precipitable_water", "precipitable_water", lambda x: x/10),
# zenith -> solar_zenith
ParameterMap("zenith", "solar_zenith"),
# clearsky
ParameterMap("clearsky_dhi", "dhi_clear"),
ParameterMap("clearsky_dni", "dni_clear"),
ParameterMap("clearsky_ghi", "ghi_clear"),
ParameterMap("clearsky_gti", "poa_global_clear")
]
[docs]def get_solcast_tmy(
latitude, longitude, api_key, map_variables=True, **kwargs
):
"""Get irradiance and weather for a
Typical Meteorological Year (TMY) at a requested location.
Data is derived from a multi-year time series selected to present the
unique weather phenomena with annual averages that are consistent with
long term averages. See [1]_ for details on the calculation.
Parameters
----------
latitude : float
in decimal degrees, between -90 and 90, north is positive
longitude : float
in decimal degrees, between -180 and 180, east is positive
api_key : str
To access Solcast data you will need an API key [2]_.
map_variables: bool, default: True
When true, renames columns of the DataFrame to pvlib variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Time is the index shifted to the midpoint of each interval
from Solcast's "period end" convention.
kwargs:
Optional parameters passed to the API.
See [3]_ for full list of parameters.
Returns
-------
data : pandas.DataFrame
containing the values for the parameters requested. The times
in the DataFrame index indicate the midpoint of each interval.
metadata: dict
latitude and longitude of the request.
Examples
--------
>>> df, meta = pvlib.iotools.solcast.get_solcast_tmy(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> api_key="your-key"
>>> )
you can pass any of the parameters listed in the API docs,
like ``time_zone``. Here we set the value of 10 for
"10 hours ahead of UTC":
>>> df, meta = pvlib.iotools.solcast.get_solcast_tmy(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> time_zone=10,
>>> api_key="your-key"
>>> )
References
----------
.. [1] `Solcast TMY Docs <https://solcast.com/tmy>`_
.. [2] `Get an API Key <https://toolkit.solcast.com.au/register>`_
.. [3] `Solcast API Docs <https://docs.solcast.com.au/>`_
See Also
--------
pvlib.iotools.get_solcast_historic, pvlib.iotools.get_solcast_forecast,
pvlib.iotools.get_solcast_live
"""
params = dict(
latitude=latitude,
longitude=longitude,
format="json",
**kwargs
)
data = _get_solcast(
endpoint="tmy/radiation_and_weather",
params=params,
api_key=api_key,
map_variables=map_variables
)
return data, {"latitude": latitude, "longitude": longitude}
[docs]def get_solcast_historic(
latitude,
longitude,
start,
api_key,
end=None,
duration=None,
map_variables=True,
**kwargs
):
"""Get historical irradiance and weather estimates.
for up to 31 days of data at a time for a requested location,
derived from satellite (clouds and irradiance
over non-polar continental areas) and
numerical weather models (other data).
Data is available from 2007-01-01T00:00Z up to real time estimated actuals.
Parameters
----------
latitude : float
in decimal degrees, between -90 and 90, north is positive
longitude : float
in decimal degrees, between -180 and 180, east is positive
start : datetime-like
First day of the requested period
end : optional, datetime-like
Last day of the requested period.
Must include one of ``end`` or ``duration``.
duration : optional, ISO 8601 compliant duration
Must include either ``end`` or ``duration``.
ISO 8601 compliant duration for the historic data,
like "P1D" for one day of data.
Must be within 31 days of ``start``.
map_variables: bool, default: True
When true, renames columns of the DataFrame to pvlib variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Time is the index shifted to the midpoint of each interval
from Solcast's "period end" convention.
api_key : str
To access Solcast data you will need an API key [1]_.
kwargs:
Optional parameters passed to the API.
See [2]_ for full list of parameters.
Returns
-------
data : pandas.DataFrame
containing the values for the parameters requested. The times
in the DataFrame index indicate the midpoint of each interval.
metadata: dict
latitude and longitude of the request.
Examples
--------
>>> df, meta = pvlib.iotools.solcast.get_solcast_historic(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> start='2007-01-01T00:00Z',
>>> duration='P1D',
>>> api_key="your-key"
>>> )
you can pass any of the parameters listed in the API docs,
for example using the ``end`` parameter instead
>>> df, meta = pvlib.iotools.solcast.get_solcast_historic(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> start='2007-01-01T00:00Z',
>>> end='2007-01-02T00:00Z',
>>> api_key="your-key"
>>> )
References
----------
.. [1] `Get an API Key <https://toolkit.solcast.com.au/register>`_
.. [2] `Solcast API Docs <https://docs.solcast.com.au/>`_
See Also
--------
pvlib.iotools.get_solcast_tmy, pvlib.iotools.get_solcast_forecast,
pvlib.iotools.get_solcast_live
"""
params = dict(
latitude=latitude,
longitude=longitude,
start=start,
end=end,
duration=duration,
api_key=api_key,
format="json",
**kwargs
)
data = _get_solcast(
endpoint="historic/radiation_and_weather",
params=params,
api_key=api_key,
map_variables=map_variables
)
return data, {"latitude": latitude, "longitude": longitude}
[docs]def get_solcast_forecast(
latitude, longitude, api_key, map_variables=True, **kwargs
):
"""Get irradiance and weather forecasts from the present time
up to 14 days ahead.
Parameters
----------
latitude : float
in decimal degrees, between -90 and 90, north is positive
longitude : float
in decimal degrees, between -180 and 180, east is positive
api_key : str
To access Solcast data you will need an API key [1]_.
map_variables: bool, default: True
When true, renames columns of the DataFrame to pvlib variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Time is the index shifted to the midpoint of each interval
from Solcast's "period end" convention.
kwargs:
Optional parameters passed to the API.
See [2]_ for full list of parameters.
Returns
-------
data : pandas.DataFrame
Contains the values for the parameters requested. The times
in the DataFrame index indicate the midpoint of each interval.
metadata: dict
latitude and longitude of the request.
Examples
--------
>>> df, meta = pvlib.iotools.solcast.get_solcast_forecast(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> api_key="your-key"
>>> )
you can pass any of the parameters listed in the API docs,
like asking for specific variables for a specific time horizon:
>>> df, meta = pvlib.iotools.solcast.get_solcast_forecast(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> output_parameters=['dni', 'clearsky_dni', 'snow_soiling_rooftop'],
>>> hours=24,
>>> api_key="your-key"
>>> )
References
----------
.. [1] `Get an API Key <https://toolkit.solcast.com.au/register>`_
.. [2] `Solcast API Docs <https://docs.solcast.com.au/>`_
See Also
--------
pvlib.iotools.get_solcast_tmy, pvlib.iotools.get_solcast_historic,
pvlib.iotools.get_solcast_live
"""
params = dict(
latitude=latitude,
longitude=longitude,
format="json",
**kwargs
)
data = _get_solcast(
endpoint="forecast/radiation_and_weather",
params=params,
api_key=api_key,
map_variables=map_variables
)
return data, {"latitude": latitude, "longitude": longitude}
[docs]def get_solcast_live(
latitude, longitude, api_key, map_variables=True, **kwargs
):
"""Get irradiance and weather estimated actuals for near real-time
and past 7 days.
Parameters
----------
latitude : float
in decimal degrees, between -90 and 90, north is positive
longitude : float
in decimal degrees, between -180 and 180, east is positive
api_key : str
To access Solcast data you will need an API key [1]_.
map_variables: bool, default: True
When true, renames columns of the DataFrame to pvlib variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Time is the index shifted to the midpoint of each interval
from Solcast's "period end" convention.
kwargs:
Optional parameters passed to the API.
See [2]_ for full list of parameters.
Returns
-------
data : pandas.DataFrame
containing the values for the parameters requested. The times
in the DataFrame index indicate the midpoint of each interval.
metadata: dict
latitude and longitude of the request.
Examples
--------
>>> df, meta = pvlib.iotools.solcast.get_solcast_live(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> api_key="your-key"
>>> )
you can pass any of the parameters listed in the API docs, like
>>> df, meta = pvlib.iotools.solcast.get_solcast_live(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> terrain_shading=True,
>>> output_parameters=['ghi', 'clearsky_ghi', 'snow_soiling_rooftop'],
>>> api_key="your-key"
>>> )
use ``map_variables=False`` to avoid converting the data
to PVLib's conventions.
>>> df, meta = pvlib.iotools.solcast.get_solcast_live(
>>> latitude=-33.856784,
>>> longitude=151.215297,
>>> map_variables=False,
>>> api_key="your-key"
>>> )
References
----------
.. [1] `Get an API Key <https://toolkit.solcast.com.au/register>`_
.. [2] `Solcast API Docs <https://docs.solcast.com.au/>`_
See Also
--------
pvlib.iotools.get_solcast_tmy, pvlib.iotools.get_solcast_historic,
pvlib.iotools.get_solcast_forecast
"""
params = dict(
latitude=latitude,
longitude=longitude,
format="json",
**kwargs
)
data = _get_solcast(
endpoint="live/radiation_and_weather",
params=params,
api_key=api_key,
map_variables=map_variables
)
return data, {"latitude": latitude, "longitude": longitude}
def _solcast2pvlib(data):
"""Format the data from Solcast to pvlib's conventions.
Parameters
----------
data : pandas.DataFrame
contains the data returned from the Solcast API
Returns
-------
a pandas.DataFrame with the data cast to pvlib's conventions
"""
# move from period_end to period_middle as per pvlib convention
data["period_mid"] = pd.to_datetime(
data.period_end) - pd.to_timedelta(data.period.values) / 2
data = data.set_index("period_mid").drop(columns=["period_end", "period"])
# rename and convert variables
for variable in VARIABLE_MAP:
if variable.solcast_name in data.columns:
data.rename(
columns={variable.solcast_name: variable.pvlib_name},
inplace=True
)
data[variable.pvlib_name] = data[
variable.pvlib_name].apply(variable.conversion)
return data
def _get_solcast(
endpoint,
params,
api_key,
map_variables
):
"""Retrieve weather, irradiance and power data from the Solcast API.
Parameters
----------
endpoint : str
one of Solcast API endpoint:
- live/radiation_and_weather
- forecast/radiation_and_weather
- historic/radiation_and_weather
- tmy/radiation_and_weather
params : dict
parameters to be passed to the API
api_key : str
To access Solcast data you will need an API key [1]_.
map_variables: bool, default: True
When true, renames columns of the DataFrame to pvlib's variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Time is the index shifted to the midpoint of each interval
from Solcast's "period end" convention.
Returns
-------
A pandas.DataFrame with the data if the request is successful,
an error message otherwise
References
----------
.. [1] `register <https://toolkit.solcast.com.au/register>`
"""
response = requests.get(
url='/'.join([BASE_URL, endpoint]),
params=params,
headers={"Authorization": f"Bearer {api_key}"}
)
if response.status_code == 200:
j = response.json()
df = pd.DataFrame.from_dict(j[list(j.keys())[0]])
if map_variables:
return _solcast2pvlib(df)
else:
return df
else:
raise Exception(response.json())