-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add get_solargis iotools function #1969
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
0dda0e4
9d1151c
3a46cfb
ab5a903
0d5835c
12538ba
76edf44
a4c48b6
c4a9e8e
6c034d1
4a48ae1
b9a1862
d727406
8bce845
8260a1e
8fc8340
7bfba0d
982164b
b6ad284
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
"""Functions to retrieve and parse irradiance data from Solargis.""" | ||
|
||
import pandas as pd | ||
import requests | ||
import io | ||
|
||
URL = 'https://solargis.info/ws/rest/datadelivery/request?' | ||
|
||
VARIABLE_MAP = { | ||
'GHI': 'ghi', | ||
'GHI_C': 'ghi_clear', # this is stated in documentation | ||
'GHIc': 'ghi_clear', # this is used in practice | ||
'DNI': 'dni', | ||
'DNI_C': 'dni_clear', | ||
'DNIc': 'dni_clear', | ||
'DIF': 'dhi', | ||
'GTI': 'poa_global', | ||
'GTI_C': 'poa_global_clear', | ||
'GTIc': 'poa_global_clear', | ||
'SE': 'solar_elevation', | ||
'SA': 'solar_azimuth', | ||
'TEMP': 'temp_air', | ||
'TD': 'temp_dew', | ||
'AP': 'pressure', | ||
'RH': 'relative_humidity', | ||
'WS': 'wind_speed', | ||
'WD': 'wind_direction', | ||
'INC': 'aoi', # angle of incidence of direct irradiance | ||
'PWAT': 'precipitable_water', # [kg/m2] | ||
kandersolar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
METADATA_FIELDS = [ | ||
'issued', 'site name', 'latitude', 'longitude', 'elevation', | ||
'summarization type', 'summarization period' | ||
] | ||
|
||
|
||
def get_solargis(latitude, longitude, start, end, variables, api_key, | ||
summarization, timestamp_type='center', tz='GMT+00', | ||
terrain_shading=True, url=URL, map_variables=True, | ||
timeout=30): | ||
""" | ||
Retrieve irradiance time series data from Solargis. | ||
|
||
The Solargis [1]_ API is described in [2]_. | ||
|
||
Parameters | ||
---------- | ||
latitude: float | ||
In decimal degrees, between -90 and 90, north is positive (ISO 19115) | ||
longitude: float | ||
In decimal degrees, between -180 and 180, east is positive (ISO 19115) | ||
start : datetime-like | ||
Start date of time series. | ||
end : datetime-like | ||
End date of time series. | ||
variables : list | ||
List of variables to request, see [2]_ for options. | ||
api_key : str | ||
API key. | ||
summarization : str, {'MIN_5', 'MIN_10', 'MIN_15', 'MIN_30', 'HOURLY', 'DAILY', 'MONTHLY', 'YEARLY'} | ||
DESCRIPTION. | ||
AdamRJensen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
timestamp_type : {'start', 'center', 'end'}, default: 'center' | ||
Labeling of time stamps of the return data. | ||
tz : str, default : 'GMT+00' | ||
Timezone of `start` and `end` in the format "GMT+hh" or "GMT-hh". | ||
terrain_shading : boolean, default: True | ||
Whether to account for horizon shading. | ||
url : str, default : :const:`pvlib.iotools.solargis.URL` | ||
Base url of Solargis API. | ||
map_variables : boolean, default: True | ||
When true, renames columns of the Dataframe to pvlib variable names | ||
where applicable. See variable :const:`VARIABLE_MAP`. | ||
timeout : int or float, default: 30 | ||
Time in seconds to wait for server response before timeout | ||
|
||
Returns | ||
------- | ||
data : DataFrame | ||
DataFrame containing time series data. | ||
meta : dict | ||
Dictionary containing metadata. | ||
|
||
Raises | ||
------ | ||
requests.HTTPError | ||
A message from the Solargis server if the request is rejected | ||
|
||
Notes | ||
----- | ||
Each XMl request is limited to retrieving 31 days of data. | ||
AdamRJensen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
The variable units depends on the time frequency, e.g., the unit for | ||
sub-hourly irradiance data is W/m^2, for hourly data it is Wh/m^2, and for | ||
daily data it is kWh/m^2. | ||
|
||
References | ||
---------- | ||
.. [1] `Solargis <https://solargis.com>`_ | ||
.. [2] `Solargis API User Guide | ||
<https://solargis.atlassian.net/wiki/spaces/public/pages/7602367/Solargis+API+User+Guide>`_ | ||
|
||
Examples | ||
-------- | ||
>>> # Retrieve two days of irradiance data from Solargis | ||
>>> data, neta = response = pvlib.iotools.get_solargis( | ||
AdamRJensen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
>>> latitude=48.61259, longitude=20.827079, | ||
>>> start='2022-01-01', end='2022-01-02', | ||
>>> variables=['GHI', 'DNI'], summarization='MIN_5', api_key='demo') | ||
""" # noqa: E501 | ||
# Use pd.to_datetime so that strings (e.g. '2021-01-01') are accepted | ||
start = pd.to_datetime(start) | ||
end = pd.to_datetime(end) | ||
|
||
headers = {'Content-Type': 'application/xml'} | ||
|
||
# Solargis recommends creating a unique site_id for each location request. | ||
# The site_id does not impact the data retrieval and is used for debugging. | ||
site_id = f"latitude_{latitude}_longitude_{longitude}" | ||
|
||
request_xml = f'''<ws:dataDeliveryRequest | ||
dateFrom="{start.strftime('%Y-%m-%d')}" | ||
dateTo="{end.strftime('%Y-%m-%d')}" | ||
xmlns="http://geomodel.eu/schema/data/request" | ||
xmlns:ws="http://geomodel.eu/schema/ws/data" | ||
xmlns:geo="http://geomodel.eu/schema/common/geo" | ||
xmlns:pv="http://geomodel.eu/schema/common/pv" | ||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> | ||
<site id="{site_id}" name="" lat="{latitude}" lng="{longitude}"> | ||
</site> | ||
<processing key="{' '.join(variables)}" summarization="{summarization}" | ||
terrainShading="{str(terrain_shading).lower()}"> | ||
<timestampType>{timestamp_type.upper()}</timestampType> | ||
<timeZone>{tz}</timeZone> | ||
</processing> | ||
</ws:dataDeliveryRequest>''' | ||
AdamRJensen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
response = requests.post(url + "key=" + api_key, headers=headers, | ||
data=request_xml.encode('utf8'), timeout=timeout) | ||
|
||
if response.ok is False: | ||
raise requests.HTTPError(response.json()) | ||
|
||
# Parse metadata | ||
header = pd.read_xml(io.StringIO(response.text), parser='etree') | ||
meta_lines = header['metadata'].iloc[0].split('#') | ||
meta_lines = [line.strip() for line in meta_lines] | ||
meta = {} | ||
for line in meta_lines: | ||
if ':' in line: | ||
key = line.split(':')[0].lower() | ||
if key in METADATA_FIELDS: | ||
meta[key] = ':'.join(line.split(':')[1:]) | ||
meta['latitude'] = float(meta['latitude']) | ||
meta['longitude'] = float(meta['longitude']) | ||
meta['altitude'] = float(meta.pop('elevation').replace('m a.s.l.', '')) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great to include the data version in the returned metadata too. Unfortunately it can't be extracted based on colons as you've done for the other fields (it looks like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree it would be nice, but judging it to be out of scope for now. |
||
|
||
# Parse data | ||
data = pd.read_xml(io.StringIO(response.text), xpath='.//doc:row', | ||
namespaces={'doc': 'http://geomodel.eu/schema/ws/data'}, | ||
parser='etree') | ||
data.index = pd.to_datetime(data['dateTime']) | ||
data = data['values'].str.split(' ', expand=True) | ||
data = data.astype(float) | ||
data.columns = header['columns'].iloc[0].split() | ||
|
||
if map_variables: | ||
data = data.rename(columns=VARIABLE_MAP) | ||
|
||
data = data.replace(-9, pd.NA) | ||
kandersolar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return data, meta |
Uh oh!
There was an error while loading. Please reload this page.