Solar position forecasting via the astral library.
Computes the sun's altitude and azimuth for a location and time using the
astral library. The main entry point, solar_position, is vectorized so
cg.Array arguments broadcast element-wise.
SolarPosition
dataclass
Solar position with altitude (elevation) and azimuth.
Attributes:
| Name |
Type |
Description |
altitude |
float
|
Sun elevation in degrees above the horizon (-90° to 90°;
negative is below the horizon).
|
azimuth |
float
|
Sun azimuth in degrees clockwise from North (0°=N, 90°=E,
180°=S, 270°=W).
|
Source code in capturegraph-lib/capturegraph/scheduling/forecast/solar.py
| @dataclass
class SolarPosition:
"""Solar position with altitude (elevation) and azimuth.
Attributes:
altitude: Sun elevation in degrees above the horizon (-90° to 90°;
negative is below the horizon).
azimuth: Sun azimuth in degrees clockwise from North (0°=N, 90°=E,
180°=S, 270°=W).
"""
altitude: float
azimuth: float
def __repr__(self) -> str:
"""Render as ``SolarPosition(alt=…°, az=…°)`` with one decimal place."""
return f"SolarPosition(alt={self.altitude:.1f}°, az={self.azimuth:.1f}°)"
def to_json(self) -> dict:
"""Convert to a dict with 'altitude' and 'azimuth' keys."""
return {"altitude": self.altitude, "azimuth": self.azimuth}
@classmethod
def from_json(cls, obj: dict) -> SolarPosition:
"""Create a SolarPosition from a dict with 'altitude' and 'azimuth' keys."""
return cls(altitude=obj["altitude"], azimuth=obj["azimuth"])
|
__repr__()
Render as SolarPosition(alt=…°, az=…°) with one decimal place.
Source code in capturegraph-lib/capturegraph/scheduling/forecast/solar.py
| def __repr__(self) -> str:
"""Render as ``SolarPosition(alt=…°, az=…°)`` with one decimal place."""
return f"SolarPosition(alt={self.altitude:.1f}°, az={self.azimuth:.1f}°)"
|
from_json(obj)
classmethod
Create a SolarPosition from a dict with 'altitude' and 'azimuth' keys.
Source code in capturegraph-lib/capturegraph/scheduling/forecast/solar.py
| @classmethod
def from_json(cls, obj: dict) -> SolarPosition:
"""Create a SolarPosition from a dict with 'altitude' and 'azimuth' keys."""
return cls(altitude=obj["altitude"], azimuth=obj["azimuth"])
|
to_json()
Convert to a dict with 'altitude' and 'azimuth' keys.
Source code in capturegraph-lib/capturegraph/scheduling/forecast/solar.py
| def to_json(self) -> dict:
"""Convert to a dict with 'altitude' and 'azimuth' keys."""
return {"altitude": self.altitude, "azimuth": self.azimuth}
|
solar_position(location, time)
Calculate solar position for a single datetime and location.
Uses the astral library for accurate calculations. Vectorized: passing
cg.Array arguments broadcasts element-wise.
Parameters:
| Name |
Type |
Description |
Default |
location
|
Location
|
Location with latitude, longitude, and altitude.
|
required
|
time
|
datetime
|
Date and time. Timezone-aware datetimes are converted to UTC;
naive datetimes are interpreted as system-local time.
|
required
|
Returns:
| Type |
Description |
SolarPosition
|
SolarPosition with altitude and azimuth in degrees.
|
Raises:
| Type |
Description |
ImportError
|
If astral is not installed.
|
Source code in capturegraph-lib/capturegraph/scheduling/forecast/solar.py
| @vectorize
def solar_position(
location: Location,
time: datetime,
) -> SolarPosition:
"""Calculate solar position for a single datetime and location.
Uses the astral library for accurate calculations. Vectorized: passing
cg.Array arguments broadcasts element-wise.
Args:
location: Location with latitude, longitude, and altitude.
time: Date and time. Timezone-aware datetimes are converted to UTC;
naive datetimes are interpreted as system-local time.
Returns:
SolarPosition with altitude and azimuth in degrees.
Raises:
ImportError: If astral is not installed.
"""
try:
from astral import Observer
from astral.sun import azimuth, elevation
except ImportError as e:
raise ImportError("solar_position requires astral. Install with: pip install astral") from e
observer = Observer(
latitude=location.latitude,
longitude=location.longitude,
elevation=location.altitude_meters,
)
# check for numpy dt and convert to datetime
if isinstance(time, np.datetime64):
time = time.item()
# normalize dt to UTC
time = time.astimezone(UTC)
alt = elevation(observer, time)
az = azimuth(observer, time)
return SolarPosition(altitude=alt, azimuth=az)
|