Skip to content

Solar Position

PVGIS features API functions for each and every elementary solar positioning angle. To start we launch an interactive i/python session

ipython

Where ?

Define the geographic location and the positioning of our solar surface

>>> latitude = 45.812
>>> longitude = 8.628
>>> surface_orientation = 177
>>> surface_tilt = 35

Native objects for coordinates

We can also use PVGIS' native classes Longitude and Latitude for the coordinates. We can import them as every other Python module and create objects

>>> from pvgis import Longitude, Latitude

inspect them

>>> longitude = Longitude()
>>> dir(longitude)

and use them as in the following example

>>> from pvgis import Longitude
>>> Longitude(value=8.628)
>>> longitude = Longitude(value=8.628, unit="degrees")

Let's see what is in the longitude variable

>>> print(longitude)

value=8.628 unit='degrees' min_radians=-3.141592653589793 max_radians=3.141592653589793 min_degrees=-180 max_degrees=180 symbol='Λ' desription="The angle between a point on the Earth's surface and the meridian plane, with its value ranging from 0° at the Prime Meridian in Greenwich, England, to 180° east or west."

Tip

The Longitude and Latitude data classes have some functionality such as converting from degrees to radians and vice versa just by calling its attribute .radians or .degrees. Such methods make it easier to write programs on top of PVGIS and not only.

When ?

Prepare a series of timestamps as a Pandas DatetimeIndex, using our helper function generate_datetime_series

>>> from pvgis.api.datetime.datetimeindex import generate_datetime_series
>>> timestamps = generate_datetime_series(start_time="2010-01-27 08:00:00", end_time="2010-01-27 18:00:00", frequency="h")

and the default UTC timezone as a ZoneInfo object

>>> from zoneinfo import ZoneInfo
>>> utc_zone = ZoneInfo("UTC")

Let's confirm the generation of the timestamps and the timezone :

>>> print(f'{timestamps=}')
>>> print(f'\n{utc_zone=}')

timestamps=DatetimeIndex(['2010-01-27 08:00:00', '2010-01-27 09:00:00', '2010-01-27 10:00:00', '2010-01-27 11:00:00', '2010-01-27 12:00:00', '2010-01-27 13:00:00', '2010-01-27 14:00:00', '2010-01-27 15:00:00', '2010-01-27 16:00:00', '2010-01-27 17:00:00', '2010-01-27 18:00:00'], dtype='datetime64[us]', freq='h')

utc_zone=zoneinfo.ZoneInfo(key='UTC')

Solar altitude

>>> from pvgis.api.position.altitude import calculate_solar_altitude_series
>>> calculate_solar_altitude_series

We can see the required arguments to run the command

Error Handling

Let's give it a first try with some reasonable input values for longitude, latitude and timestamps plus timezone

Failure

>>> from pydantic import ValidationError
>>> try:
...     calculate_solar_altitude_series(8, 45, '2001-01-01 10:00:00', 'UTC')
... except ValidationError as exc:
...     print(exc)
6 validation errors for ModelSolarAltitudeTimeSeriesInputModel
timestamps.is-instance[DatetimeIndex]
  Input should be an instance of DatetimeIndex [type=is_instance_of, input_value='2001-01-01 10:00:00', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/is_instance_of
timestamps.is-instance[Timestamp]
  Input should be an instance of Timestamp [type=is_instance_of, input_value='2001-01-01 10:00:00', input_type=str]
    For further information visit https://errors.pydantic.dev/2.12/v/is_instance_of
latitude.constrained-float
  Input should be less than or equal to 1.5707963267948966 [type=less_than_equal, input_value=45, input_type=int]
    For further information visit https://errors.pydantic.dev/2.12/v/less_than_equal
latitude.Latitude
  Input should be a valid dictionary or instance of Latitude [type=model_type, input_value=45, input_type=int]
    For further information visit https://errors.pydantic.dev/2.12/v/model_type
longitude.constrained-float
  Input should be less than or equal to 3.141592653589793 [type=less_than_equal, input_value=8, input_type=int]
    For further information visit https://errors.pydantic.dev/2.12/v/less_than_equal
longitude.Longitude
  Input should be a valid dictionary or instance of Longitude [type=model_type, input_value=8, input_type=int]
    For further information visit https://errors.pydantic.dev/2.12/v/model_type

PVGIS' API is indeed idiomatic and our reasonable inputs won't work ! However, the input arguments are validated via Pydantic and thus we receive informative error messages.

Speaking PVGIS' API language

Let's import the required modules

>>> from pandas import Timestamp
>>> from zoneinfo import ZoneInfo
>>> from pvgis.api.utilities.conversions import convert_float_to_radians_if_requested

Setting the input parameters

>>> longitude = convert_float_to_radians_if_requested(8, 'radians')
>>> latitude = convert_float_to_radians_if_requested(45, 'radians')
>>> timestamps=Timestamp('2001-01-01 10:00:00+00:00')
>>> timezone=ZoneInfo('UTC')

And re-run the calculation

>>> solar_altitude = calculate_solar_altitude_series(longitude=longitude, latitude=latitude, timestamps=timestamps, timezone=timezone)
>>> print(f"Solar altitude from PVGIS' API : {solar_altitude}")
Solar altitude from PVGIS' API : {'noaa': {<SolarPositionParameter.timing: 'time_algorithm'>: 'NOAA', <SolarPositionParameter.positioning: 'position_algorithm'>: 'NOAA', <SolarPositionParameter.altitude: 'altitude'>: array([0.32986832], dtype=float32), 'unit': None}}

Solar Azimuth

We can calculate solar azimuth angles for a specific geographic location and over a time series with the API function model_solar_azimuth_series().

>>> from pvgis.api.position.azimuth import model_solar_azimuth_series

Help

What exactly can we do with it ? We can use the Python built-in function help to find out

>>> help(model_solar_azimuth_series)

Or indeed, in an interactive ipython session, it suffices to use a ? right after the function name and without space

>>> model_solar_azimuth_series?

More Imports

>>> from math import radians
>>> from pvgis.api.datetime.datetimeindex import generate_datetime_series
>>> timestamps = generate_datetime_series(start_time='2010-01-27', end_time='2010-01-28')
>>> from zoneinfo import ZoneInfo

Calculate solar azimuth time series for the 27th January 2010

>>> model_solar_azimuth_series(
... longitude=radians(longitude),
... latitude=radians(latitude),
... timestamps=timestamps,
... timezone=ZoneInfo("UTC"),
... apply_atmospheric_refraction=True
... )

The above command returns a PVGIS-native data class SolarAzimuth. Of course we can feed the result to a new variable and print or re-use it for further processing

>>> solar_azimuth_series = model_solar_azimuth_series(
... longitude=radians(longitude),
... latitude=radians(latitude),
... timestamps=timestamps,
... timezone=ZoneInfo("UTC"),
... apply_atmospheric_refraction=True
... )
>>> print(solar_azimuth_series)

value=array([0.20324278, 0.68162394, 1.0347457 , 1.2956886 , 1.5057368 , 1.6915469 , 1.8700204 , 2.0612493 , 2.250216 , 2.466558 , 2.7086406 , 2.975184 , 3.2346225 , 3.5088406 , 3.7576592 , 3.9807858 , 4.1786404 , 4.3685203 , 4.547683 , 4.73073 , 4.933111 , 5.178298 , 5.503539 , 5.949113 , 0.19983101], dtype=float32) unit='radians' position_algorithm='NOAA' timing_algorithm='NOAA' definition='Solar azimuth angle' description='Solar azimuth angle' origin='North' min_radians=0 max_radians=6.283185307179586 min_degrees=0 max_degrees=360

Convert output to degrees

Note, most PVGIS data classes feature a standard .degrees method -- it'll convert the values to geographic degrees :

>>> print(solar_azimuth_series.degrees)

[ 11.644953 39.054173 59.286556 74.23749 86.27236 96.918495 107.14427 118.100876 128.92787 141.32335 155.19366 170.46547 185.3302 201.04175 215.298 228.08221 239.41844 250.29776 260.56302 271.05084 282.64642 296.6946 315.32956 340.85904 11.449472]