Simple Python Web Service Client
Marek Caltík Miroslav Majtáz Peter Orosi
Data Delivery Web service (the API for retrieving time series data)
if __name__ == '__main__':
import requests
import xmltodict # https://pypi.org/project/xmltodict/
import pandas as pd
api_key = 'demo'
dateFrom = '2024-05-01'
dateTo = '2024-05-01'
latitude = 48.61259
longitude = 20.827079
tilt = 40
azimuth = 180
request_xml = f'''<ws:dataDeliveryRequest dateFrom="{dateFrom}" dateTo="{dateTo}"
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="demo" lat="{latitude}" lng="{longitude}">
<pv:geometry xsi:type="pv:GeometryFixedOneAngle" tilt="{tilt}" azimuth="{azimuth}"/>
<pv:system installedPower="1" installationType="ROOF_MOUNTED">
<pv:module type="CSI"/>
<pv:inverter/>
<pv:losses/>
</pv:system>
</site>
<processing key="GHI DNI DIF GTI TEMP WS WD RH" summarization="HOURLY">
<timeZone>GMT+02</timeZone>
<timestampType>START</timestampType>
</processing>
</ws:dataDeliveryRequest>'''
url = f'https://solargis.info/ws/rest/datadelivery/request?key={api_key}'
headers = {'Content-Type': 'application/xml'}
with requests.post(url, data=request_xml, headers=headers) as response:
if not response.ok:
print(f'Failed API request with code {response.status_code}:\n{response.text}')
else:
datadict = xmltodict.parse(response.text) # transform XML document into Python dictionary
columns = datadict['dataDeliveryResponse']['site']['columns'].split() # get names of columns (GHI, DNI, etc.)
rows = datadict['dataDeliveryResponse']['site']['row'] # get rows or records from XML
rows = rows if isinstance(rows, list) else [rows]
timestamps = [r['@dateTime'] for r in
rows] # use '@dateTime' for sub/hourly data, '@date' for daily data, '@yearMonth' for monthly data
data_values = [r['@values'].split() for r in rows]
# create pandas dataframe from the response data:
df = pd.DataFrame(data_values, index=pd.to_datetime(timestamps), columns=columns)
df = df.apply(pd.to_numeric, errors='coerce') # make numbers from original strings in XML
print(df)
print(df.describe())
# plot the response:
# import matplotlib.pyplot as plt
# df.plot()
# plt.savefig('solargi_api_response.png')
LTA Web service (the API for retrieving long-term average data)
if __name__ == '__main__':
import requests
import xmltodict # https://pypi.org/project/xmltodict/
api_key = 'demo'
latitude = 48.61259
longitude = 20.827079
tilt = 40
azimuth = 180
request_xml = f'''<calculateRequest
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:geo="http://geomodel.eu/schema/common/geo"
xmlns:pv="http://geomodel.eu/schema/common/pv"
xmlns="http://geomodel.eu/schema/ws/pvplanner">
<site lat="{latitude}" lng="{longitude}">
<!-- optional terrain data -->
<geo:terrain elevation="246" azimuth="176" tilt="3.1" />
<!-- optional custom horizon data can override the natural terrain horizon -->
<!--<geo:horizon>11.11:18.0 7.5:15.53 15.0:10.94 352.5:17.29</geo:horizon>-->
<pv:geometry xsi:type="pv:GeometryFixedOneAngle" azimuth="{azimuth}" tilt="{tilt}"/>
<pv:system installedPower="1" installationType="ROOF_MOUNTED" availability="99">
<pv:module type="CSI">
</pv:module>
<pv:inverter>
<pv:efficiency xsi:type="pv:EfficiencyConstant" percent="97.5"/>
</pv:inverter>
<pv:losses dc="5.5" ac="1.5"/>
</pv:system>
</site>
</calculateRequest>'''
url = f'https://solargis.info/ws/rest/pvplanner/calculate?key={api_key}'
headers = {'Content-Type': 'application/xml'}
with requests.post(url, data=request_xml, headers=headers) as response:
if not response.ok:
print(f'Failed API request with code {response.status_code}:\n{response.text}')
else:
datadict = xmltodict.parse(response.text) # transform XML document into Python dictionary
response_dict = datadict['ns3:calculateResponse']
# explore solar and climate data in the response:
climate_data = response_dict['ns3:irradiation']
climate_data_reference = climate_data['ns3:reference'] # general climate data
solar_data_inplane = climate_data['ns3:inplane'] # POA irradiance components
geometry_comparison = climate_data['ns3:comparison'] # comparison of the various geometry options with selected one
optimum_angle = climate_data.get('ns3:optimum') # optimum fixed angle for the selected location
# get PV calculation data:
pv_data = response_dict['ns3:calculation']
pv_data_output = pv_data['ns3:output'] # PV system output total and specific
pv_data_losses = pv_data['ns3:losses'] # PV system losses breakdown
print('Climate data reference:', climate_data_reference)
print('Solar data in-plane:', solar_data_inplane)
print('Geometry comparison:', geometry_comparison)
print('Optimum angle:', optimum_angle)
print('PV data output:', pv_data_output)
print('PV data losses:', pv_data_losses)
{"serverDuration": 39, "requestCorrelationId": "421c962fdf3c44dcb6eff50ec9bebfc0"}