|
| 1 | +""" Functions to access data from the Solcast API. |
| 2 | +""" |
| 3 | + |
| 4 | +import requests |
| 5 | +import pandas as pd |
| 6 | +from dataclasses import dataclass |
| 7 | + |
| 8 | + |
| 9 | +BASE_URL = "https://api.solcast.com.au/data" |
| 10 | + |
| 11 | +@dataclass |
| 12 | +class ParameterMap: |
| 13 | + solcast_name: str |
| 14 | + pvlib_name: str |
| 15 | + conversion: callable=lambda x: x |
| 16 | + |
| 17 | +# define the conventions between Solcast and PVLib nomenclature and units |
| 18 | +VARIABLE_MAP = [ |
| 19 | + ParameterMap("air_temp", "temp_air"), # air_temp -> temp_air (deg C) |
| 20 | + ParameterMap("surface_pressure", "pressure", lambda x: x*100), # surface_pressure (hPa) -> pressure (Pa) |
| 21 | + ParameterMap("dewpoint_temp", "temp_dew"), # dewpoint_temp -> temp_dew (deg C) |
| 22 | + ParameterMap("gti", "poa_global"), # gti (W/m^2) -> poa_global (W/m^2) |
| 23 | + ParameterMap("wind_speed_10m", "wind_speed"), # wind_speed_10m (m/s) -> wind_speed (m/s) |
| 24 | + ParameterMap("wind_direction_10m", "wind_direction"), # wind_direction_10m (deg) -> wind_direction (deg) (Convention?) |
| 25 | + ParameterMap( |
| 26 | + "azimuth", "solar_azimuth", lambda x: abs(x) if x <= 0 else 360 - x |
| 27 | + ), # azimuth -> solar_azimuth (degrees) (different convention) |
| 28 | + ParameterMap("precipitable_water", "precipitable_water", lambda x: x*10), # precipitable_water (kg/m2) -> precipitable_water (cm) |
| 29 | + ParameterMap("zenith", "solar_zenith") # zenith -> solar_zenith |
| 30 | +] |
| 31 | + |
| 32 | + |
| 33 | +def get_solcast_tmy( |
| 34 | + latitude, longitude, api_key, map_variables=True, **kwargs |
| 35 | +): |
| 36 | + """Get the irradiance and weather for a Typical Meteorological Year (TMY) at a requested location. |
| 37 | +
|
| 38 | + Derived from satellite (clouds and irradiance over non-polar continental areas) and |
| 39 | + numerical weather models (other data). The TMY is calculated with data from 2007 to 2023. |
| 40 | +
|
| 41 | + Parameters |
| 42 | + ---------- |
| 43 | + latitude : float |
| 44 | + in decimal degrees, between -90 and 90, north is positive |
| 45 | + longitude : float |
| 46 | + in decimal degrees, between -180 and 180, east is positive |
| 47 | + api_key : str |
| 48 | + To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register. |
| 49 | + map_variables: bool, default: True |
| 50 | + When true, renames columns of the DataFrame to pvlib variable names |
| 51 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 52 | + kwargs: |
| 53 | + Optional parameters passed to the API. See https://docs.solcast.com.au/ for full list of parameters. |
| 54 | +
|
| 55 | + Returns |
| 56 | + ------- |
| 57 | + df : pandas.DataFrame |
| 58 | + containing the values for the parameters requested |
| 59 | +
|
| 60 | + Examples |
| 61 | + -------- |
| 62 | + get_solcast_tmy( |
| 63 | + latitude=-33.856784, |
| 64 | + longitude=151.215297, |
| 65 | + api_key="your-key" |
| 66 | + ) |
| 67 | +
|
| 68 | + you can pass any of the parameters listed in the API docs, like time_zone: |
| 69 | +
|
| 70 | + get_solcast_tmy( |
| 71 | + latitude=-33.856784, |
| 72 | + longitude=151.215297, |
| 73 | + time_zone=10, |
| 74 | + api_key="your-key" |
| 75 | + ) |
| 76 | +
|
| 77 | + """ |
| 78 | + |
| 79 | + params = dict( |
| 80 | + latitude=latitude, |
| 81 | + longitude=longitude, |
| 82 | + format="json", |
| 83 | + **kwargs |
| 84 | + ) |
| 85 | + |
| 86 | + return _get_solcast( |
| 87 | + endpoint="tmy/radiation_and_weather", |
| 88 | + params=params, |
| 89 | + api_key=api_key, |
| 90 | + map_variables=map_variables |
| 91 | + ) |
| 92 | + |
| 93 | + |
| 94 | +def get_solcast_historic( |
| 95 | + latitude, |
| 96 | + longitude, |
| 97 | + start, |
| 98 | + api_key, |
| 99 | + end=None, |
| 100 | + duration=None, |
| 101 | + map_variables=True, |
| 102 | + **kwargs |
| 103 | +): |
| 104 | + """Get historical irradiance and weather estimated actuals |
| 105 | +
|
| 106 | + for up to 31 days of data at a time for a requested location, |
| 107 | + derived from satellite (clouds and irradiance |
| 108 | + over non-polar continental areas) and numerical weather models (other data). |
| 109 | + Data is available from 2007-01-01T00:00Z up to real time estimated actuals. |
| 110 | +
|
| 111 | + Parameters |
| 112 | + ---------- |
| 113 | + latitude : float |
| 114 | + in decimal degrees, between -90 and 90, north is positive |
| 115 | + longitude : float |
| 116 | + in decimal degrees, between -180 and 180, east is positive |
| 117 | + start : datetime-like |
| 118 | + First day of the requested period |
| 119 | + end : optional, datetime-like |
| 120 | + Last day of the requested period |
| 121 | + duration : optional, default is None |
| 122 | + Must include one of end_date and duration. ISO_8601 compliant duration for the historic data. |
| 123 | + Must be within 31 days of the start_date. |
| 124 | + map_variables: bool, default: True |
| 125 | + When true, renames columns of the DataFrame to pvlib variable names |
| 126 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 127 | + api_key : str |
| 128 | + To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register. |
| 129 | + kwargs: |
| 130 | + Optional parameters passed to the GET request |
| 131 | +
|
| 132 | + See https://docs.solcast.com.au/ for full list of parameters. |
| 133 | +
|
| 134 | + Returns |
| 135 | + ------- |
| 136 | + df : pandas.DataFrame |
| 137 | + containing the values for the parameters requested |
| 138 | +
|
| 139 | + Examples |
| 140 | + -------- |
| 141 | + get_solcast_historic( |
| 142 | + latitude=-33.856784, |
| 143 | + longitude=151.215297, |
| 144 | + start='2007-01-01T00:00Z', |
| 145 | + duration='P1D', |
| 146 | + api_key="your-key" |
| 147 | + ) |
| 148 | +
|
| 149 | + you can pass any of the parameters listed in the API docs, for example using the end parameter instead |
| 150 | +
|
| 151 | + get_solcast_historic( |
| 152 | + latitude=-33.856784, |
| 153 | + longitude=151.215297, |
| 154 | + start='2007-01-01T00:00Z', |
| 155 | + end='2007-01-02T00:00Z', |
| 156 | + api_key="your-key" |
| 157 | + ) |
| 158 | + """ |
| 159 | + |
| 160 | + params = dict( |
| 161 | + latitude=latitude, |
| 162 | + longitude=longitude, |
| 163 | + start=start, |
| 164 | + end=end, |
| 165 | + duration=duration, |
| 166 | + api_key=api_key, |
| 167 | + format="json", |
| 168 | + **kwargs |
| 169 | + ) |
| 170 | + |
| 171 | + return _get_solcast( |
| 172 | + endpoint="historic/radiation_and_weather", |
| 173 | + params=params, |
| 174 | + api_key=api_key, |
| 175 | + map_variables=map_variables |
| 176 | + ) |
| 177 | + |
| 178 | +def get_solcast_forecast( |
| 179 | + latitude, longitude, api_key, map_variables=True, **kwargs |
| 180 | +): |
| 181 | + """Get irradiance and weather forecasts from the present time up to 14 days ahead |
| 182 | +
|
| 183 | + Parameters |
| 184 | + ---------- |
| 185 | + latitude : float |
| 186 | + in decimal degrees, between -90 and 90, north is positive |
| 187 | + longitude : float |
| 188 | + in decimal degrees, between -180 and 180, east is positive |
| 189 | + api_key : str |
| 190 | + To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register. |
| 191 | + map_variables: bool, default: True |
| 192 | + When true, renames columns of the DataFrame to pvlib variable names |
| 193 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 194 | + kwargs: |
| 195 | + Optional parameters passed to the GET request |
| 196 | +
|
| 197 | + See https://docs.solcast.com.au/ for full list of parameters. |
| 198 | +
|
| 199 | + Returns |
| 200 | + ------- |
| 201 | + df : pandas.DataFrame |
| 202 | + containing the values for the parameters requested |
| 203 | +
|
| 204 | + Examples |
| 205 | + -------- |
| 206 | + get_solcast_forecast( |
| 207 | + latitude=-33.856784, |
| 208 | + longitude=151.215297, |
| 209 | + api_key="your-key" |
| 210 | + ) |
| 211 | +
|
| 212 | + you can pass any of the parameters listed in the API docs, like asking for specific variables |
| 213 | + get_solcast_forecast( |
| 214 | + latitude=-33.856784, |
| 215 | + longitude=151.215297, |
| 216 | + output_parameters='dni,clearsky_dni', |
| 217 | + api_key="your-key" |
| 218 | + ) |
| 219 | + """ |
| 220 | + |
| 221 | + params = dict( |
| 222 | + latitude=latitude, |
| 223 | + longitude=longitude, |
| 224 | + format="json", |
| 225 | + **kwargs |
| 226 | + ) |
| 227 | + |
| 228 | + return _get_solcast( |
| 229 | + endpoint="forecast/radiation_and_weather", |
| 230 | + params=params, |
| 231 | + api_key=api_key, |
| 232 | + map_variables=map_variables |
| 233 | + ) |
| 234 | + |
| 235 | +def get_solcast_live( |
| 236 | + latitude, longitude, api_key, map_variables=True, **kwargs |
| 237 | +): |
| 238 | + """Get irradiance and weather estimated actuals for near real-time and past 7 days |
| 239 | +
|
| 240 | + Parameters |
| 241 | + ---------- |
| 242 | + latitude : float |
| 243 | + in decimal degrees, between -90 and 90, north is positive |
| 244 | + longitude : float |
| 245 | + in decimal degrees, between -180 and 180, east is positive |
| 246 | + api_key : str |
| 247 | + To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register. |
| 248 | + map_variables: bool, default: True |
| 249 | + When true, renames columns of the DataFrame to pvlib variable names |
| 250 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 251 | + kwargs: |
| 252 | + Optional parameters passed to the GET request |
| 253 | +
|
| 254 | + See https://docs.solcast.com.au/ for full list of parameters. |
| 255 | +
|
| 256 | + Returns |
| 257 | + ------- |
| 258 | + df : pandas.DataFrame |
| 259 | + containing the values for the parameters requested |
| 260 | +
|
| 261 | + Examples |
| 262 | + -------- |
| 263 | + get_solcast_live( |
| 264 | + latitude=-33.856784, |
| 265 | + longitude=151.215297, |
| 266 | + api_key="your-key" |
| 267 | + ) |
| 268 | +
|
| 269 | + you can pass any of the parameters listed in the API docs, like |
| 270 | +
|
| 271 | + get_solcast_live( |
| 272 | + latitude=-33.856784, |
| 273 | + longitude=151.215297, |
| 274 | + terrain_shading=True, |
| 275 | + api_key="your-key" |
| 276 | + ) |
| 277 | +
|
| 278 | + use map_variables=False to avoid converting the data to PVLib's conventions |
| 279 | +
|
| 280 | + get_solcast_live( |
| 281 | + latitude=-33.856784, |
| 282 | + longitude=151.215297, |
| 283 | + map_variables=False, |
| 284 | + api_key="your-key" |
| 285 | + ) |
| 286 | + """ |
| 287 | + |
| 288 | + params = dict( |
| 289 | + latitude=latitude, |
| 290 | + longitude=longitude, |
| 291 | + format="json", |
| 292 | + **kwargs |
| 293 | + ) |
| 294 | + |
| 295 | + return _get_solcast( |
| 296 | + endpoint="live/radiation_and_weather", |
| 297 | + params=params, |
| 298 | + api_key=api_key, |
| 299 | + map_variables=map_variables |
| 300 | + ) |
| 301 | + |
| 302 | +def solcast2pvlib(df): |
| 303 | + """Formats the data from Solcast to PVLib's conventions. |
| 304 | + """ |
| 305 | + # move from period_end to period_middle as per pvlib convention |
| 306 | + df["period_mid"] = pd.to_datetime(df.period_end) - pd.Timedelta(df.period.values[0]) / 2 |
| 307 | + df = df.set_index("period_mid").drop(columns=["period_end", "period"]) |
| 308 | + |
| 309 | + # rename and convert variables |
| 310 | + for variable in VARIABLE_MAP: |
| 311 | + if variable.solcast_name in df.columns: |
| 312 | + df.rename(columns={variable.solcast_name: variable.pvlib_name}, inplace=True) |
| 313 | + df[variable.pvlib_name] = df[variable.pvlib_name].apply(variable.conversion) |
| 314 | + return df |
| 315 | + |
| 316 | +def _get_solcast( |
| 317 | + endpoint, |
| 318 | + params, |
| 319 | + api_key, |
| 320 | + map_variables |
| 321 | +): |
| 322 | + """retrieves weather, irradiance and power data from the Solcast API |
| 323 | +
|
| 324 | + Parameters |
| 325 | + ---------- |
| 326 | + endpoint : str |
| 327 | + one of Solcast API endpoint: |
| 328 | + - live/radiation_and_weather |
| 329 | + - forecast/radiation_and_weather |
| 330 | + - historic/radiation_and_weather |
| 331 | + - tmy/radiation_and_weather |
| 332 | + params : dict |
| 333 | + parameters to be passed to the API |
| 334 | + api_key : str |
| 335 | + To access Solcast data you will need an API key: https://toolkit.solcast.com.au/register. |
| 336 | + map_variables: bool, default: True |
| 337 | + When true, renames columns of the DataFrame to pvlib variable names |
| 338 | + where applicable. See variable :const:`VARIABLE_MAP`. |
| 339 | +
|
| 340 | + Returns |
| 341 | + ------- |
| 342 | + A pandas.DataFrame with the data if the request is successful, an error message otherwise |
| 343 | + """ |
| 344 | + |
| 345 | + response = requests.get( |
| 346 | + url= '/'.join([BASE_URL, endpoint]), |
| 347 | + params=params, |
| 348 | + headers={"Authorization": f"Bearer {api_key}"} |
| 349 | + ) |
| 350 | + |
| 351 | + if response.status_code == 200: |
| 352 | + j = response.json() |
| 353 | + df = pd.DataFrame.from_dict(j[list(j.keys())[0]]) |
| 354 | + if map_variables: |
| 355 | + return solcast2pvlib(df) |
| 356 | + else: |
| 357 | + return df |
| 358 | + else: |
| 359 | + raise Exception(response.json()) |
0 commit comments