pyopenmeteo 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyopenmeteo/__init__.py +78 -0
- pyopenmeteo/api/__init__.py +26 -0
- pyopenmeteo/api/air_quality.py +142 -0
- pyopenmeteo/api/climate.py +125 -0
- pyopenmeteo/api/ensemble.py +161 -0
- pyopenmeteo/api/flood.py +123 -0
- pyopenmeteo/api/forecast.py +220 -0
- pyopenmeteo/api/geo.py +213 -0
- pyopenmeteo/api/historical.py +153 -0
- pyopenmeteo/api/marine.py +174 -0
- pyopenmeteo/api/satellite.py +158 -0
- pyopenmeteo/api/seasonal.py +165 -0
- pyopenmeteo/core/__init__.py +27 -0
- pyopenmeteo/core/base_api.py +105 -0
- pyopenmeteo/core/exceptions.py +47 -0
- pyopenmeteo/core/http_client.py +76 -0
- pyopenmeteo/core/request_builder.py +33 -0
- pyopenmeteo/core/response.py +250 -0
- pyopenmeteo/formatters/__init__.py +8 -0
- pyopenmeteo/formatters/csv_fmt.py +39 -0
- pyopenmeteo/formatters/numpy_fmt.py +36 -0
- pyopenmeteo/formatters/pandas_fmt.py +60 -0
- pyopenmeteo/formatters/xarray_fmt.py +111 -0
- pyopenmeteo/params/__init__.py +17 -0
- pyopenmeteo/params/airquality_vars.py +55 -0
- pyopenmeteo/params/archive_vars.py +147 -0
- pyopenmeteo/params/climate_vars.py +31 -0
- pyopenmeteo/params/ensemble_vars.py +222 -0
- pyopenmeteo/params/flood_vars.py +19 -0
- pyopenmeteo/params/forecast_vars.py +236 -0
- pyopenmeteo/params/marine_vars.py +80 -0
- pyopenmeteo/params/pressure_levels.py +98 -0
- pyopenmeteo/params/satellite_vars.py +36 -0
- pyopenmeteo/params/seasonal_vars.py +256 -0
- pyopenmeteo/params/solar.py +57 -0
- pyopenmeteo/params/units.py +34 -0
- pyopenmeteo-0.1.0.dist-info/METADATA +548 -0
- pyopenmeteo-0.1.0.dist-info/RECORD +41 -0
- pyopenmeteo-0.1.0.dist-info/WHEEL +5 -0
- pyopenmeteo-0.1.0.dist-info/licenses/LICENSE.md +7 -0
- pyopenmeteo-0.1.0.dist-info/top_level.txt +1 -0
pyopenmeteo/__init__.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"""pyopenmeteo - a Python wrapper for the Open-Meteo weather API family."""
|
|
2
|
+
|
|
3
|
+
from pyopenmeteo.api.forecast import ForecastAPI
|
|
4
|
+
from pyopenmeteo.api.historical import HistoricalAPI, HistoricalForecastAPI
|
|
5
|
+
from pyopenmeteo.api.geo import GeocodingAPI, ElevationAPI, Location
|
|
6
|
+
from pyopenmeteo.api.marine import MarineAPI
|
|
7
|
+
from pyopenmeteo.api.climate import ClimateAPI
|
|
8
|
+
from pyopenmeteo.api.air_quality import AirQualityAPI
|
|
9
|
+
from pyopenmeteo.api.ensemble import EnsembleAPI
|
|
10
|
+
from pyopenmeteo.api.flood import FloodAPI
|
|
11
|
+
from pyopenmeteo.api.satellite import SatelliteRadiationAPI
|
|
12
|
+
from pyopenmeteo.api.seasonal import SeasonalAPI
|
|
13
|
+
from pyopenmeteo.core.exceptions import (
|
|
14
|
+
MeteoPyError,
|
|
15
|
+
APIError,
|
|
16
|
+
RateLimitError,
|
|
17
|
+
ServerError,
|
|
18
|
+
GeocodingError,
|
|
19
|
+
LocationNotFoundError,
|
|
20
|
+
ValidationError,
|
|
21
|
+
ConnectionError,
|
|
22
|
+
)
|
|
23
|
+
from pyopenmeteo.core.response import WeatherResponse
|
|
24
|
+
from pyopenmeteo.params.units import (
|
|
25
|
+
TemperatureUnit,
|
|
26
|
+
WindSpeedUnit,
|
|
27
|
+
PrecipitationUnit,
|
|
28
|
+
TimeFormat,
|
|
29
|
+
CellSelection,
|
|
30
|
+
LengthUnit,
|
|
31
|
+
)
|
|
32
|
+
from pyopenmeteo.params.pressure_levels import PressureVar, pressure_level, pressure_levels_range
|
|
33
|
+
from pyopenmeteo.params.solar import PanelOrientation
|
|
34
|
+
|
|
35
|
+
__version__ = "0.1.0"
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
# APIs
|
|
39
|
+
"ForecastAPI",
|
|
40
|
+
"HistoricalAPI",
|
|
41
|
+
"HistoricalForecastAPI",
|
|
42
|
+
"GeocodingAPI",
|
|
43
|
+
"ElevationAPI",
|
|
44
|
+
"Location",
|
|
45
|
+
"MarineAPI",
|
|
46
|
+
"ClimateAPI",
|
|
47
|
+
"AirQualityAPI",
|
|
48
|
+
"EnsembleAPI",
|
|
49
|
+
"FloodAPI",
|
|
50
|
+
"SatelliteRadiationAPI",
|
|
51
|
+
"SeasonalAPI",
|
|
52
|
+
# Exceptions
|
|
53
|
+
"MeteoPyError",
|
|
54
|
+
"APIError",
|
|
55
|
+
"RateLimitError",
|
|
56
|
+
"ServerError",
|
|
57
|
+
"GeocodingError",
|
|
58
|
+
"LocationNotFoundError",
|
|
59
|
+
"ValidationError",
|
|
60
|
+
"ConnectionError",
|
|
61
|
+
# Response
|
|
62
|
+
"WeatherResponse",
|
|
63
|
+
# Units
|
|
64
|
+
"TemperatureUnit",
|
|
65
|
+
"WindSpeedUnit",
|
|
66
|
+
"PrecipitationUnit",
|
|
67
|
+
"TimeFormat",
|
|
68
|
+
"CellSelection",
|
|
69
|
+
"LengthUnit",
|
|
70
|
+
# Pressure levels
|
|
71
|
+
"PressureVar",
|
|
72
|
+
"pressure_level",
|
|
73
|
+
"pressure_levels_range",
|
|
74
|
+
# Solar
|
|
75
|
+
"PanelOrientation",
|
|
76
|
+
# Version
|
|
77
|
+
"__version__",
|
|
78
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from pyopenmeteo.api.forecast import ForecastAPI
|
|
2
|
+
from pyopenmeteo.api.historical import HistoricalAPI, HistoricalForecastAPI
|
|
3
|
+
from pyopenmeteo.api.geo import GeocodingAPI, ElevationAPI, Location
|
|
4
|
+
from pyopenmeteo.api.marine import MarineAPI
|
|
5
|
+
from pyopenmeteo.api.climate import ClimateAPI
|
|
6
|
+
from pyopenmeteo.api.air_quality import AirQualityAPI
|
|
7
|
+
from pyopenmeteo.api.ensemble import EnsembleAPI
|
|
8
|
+
from pyopenmeteo.api.flood import FloodAPI
|
|
9
|
+
from pyopenmeteo.api.satellite import SatelliteRadiationAPI
|
|
10
|
+
from pyopenmeteo.api.seasonal import SeasonalAPI
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
"ForecastAPI",
|
|
14
|
+
"HistoricalAPI",
|
|
15
|
+
"HistoricalForecastAPI",
|
|
16
|
+
"GeocodingAPI",
|
|
17
|
+
"ElevationAPI",
|
|
18
|
+
"Location",
|
|
19
|
+
"MarineAPI",
|
|
20
|
+
"ClimateAPI",
|
|
21
|
+
"AirQualityAPI",
|
|
22
|
+
"EnsembleAPI",
|
|
23
|
+
"FloodAPI",
|
|
24
|
+
"SatelliteRadiationAPI",
|
|
25
|
+
"SeasonalAPI",
|
|
26
|
+
]
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pyopenmeteo.core.base_api import BaseAPI
|
|
4
|
+
from pyopenmeteo.core.exceptions import ValidationError
|
|
5
|
+
from pyopenmeteo.core.request_builder import RequestBuilder
|
|
6
|
+
from pyopenmeteo.core.response import WeatherResponse
|
|
7
|
+
from pyopenmeteo.params.airquality_vars import CurrentAirQualityVars, HourlyAirQualityVars
|
|
8
|
+
from pyopenmeteo.params.units import TimeFormat
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class AirQualityAPI(BaseAPI):
|
|
12
|
+
"""Wraps the OpenMeteo Air Quality API.
|
|
13
|
+
|
|
14
|
+
Provides hourly air quality forecasts and current conditions including
|
|
15
|
+
pollutant concentrations, pollen counts, and air quality indices for
|
|
16
|
+
any location worldwide.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> from pyopenmeteo.params.airquality_vars import HourlyAirQualityVars
|
|
20
|
+
>>> api = AirQualityAPI()
|
|
21
|
+
>>> resp = api.get(
|
|
22
|
+
... (48.85, 2.35),
|
|
23
|
+
... hourly=[HourlyAirQualityVars.PM2_5],
|
|
24
|
+
... forecast_days=3,
|
|
25
|
+
... )
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
BASE_URL = "https://air-quality-api.open-meteo.com/v1/air-quality"
|
|
29
|
+
|
|
30
|
+
def get(
|
|
31
|
+
self,
|
|
32
|
+
location: tuple[float, float] | list[float] | str,
|
|
33
|
+
*,
|
|
34
|
+
hourly: list[HourlyAirQualityVars | str] | None = None,
|
|
35
|
+
current: list[CurrentAirQualityVars | str] | None = None,
|
|
36
|
+
timeformat: TimeFormat | str | None = None,
|
|
37
|
+
timezone: str | None = None,
|
|
38
|
+
past_days: int | None = None,
|
|
39
|
+
forecast_days: int | None = None,
|
|
40
|
+
past_hours: int | None = None,
|
|
41
|
+
forecast_hours: int | None = None,
|
|
42
|
+
start_date: str | date | None = None,
|
|
43
|
+
end_date: str | date | None = None,
|
|
44
|
+
domains: str | None = None,
|
|
45
|
+
) -> WeatherResponse:
|
|
46
|
+
"""Fetch air quality forecast data.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
location: ``(latitude, longitude)`` tuple/list or a place-name
|
|
50
|
+
string resolved via the Geocoding API.
|
|
51
|
+
hourly: Hourly air quality variables to request.
|
|
52
|
+
current: Current-conditions air quality variables to request.
|
|
53
|
+
Accepts the same variable set as ``hourly``.
|
|
54
|
+
timeformat: ``iso8601`` (default) or ``unixtime``.
|
|
55
|
+
timezone: tz database string like ``"Europe/Paris"`` or
|
|
56
|
+
``"auto"`` to infer from location.
|
|
57
|
+
past_days: Include this many past days in the response (0–92).
|
|
58
|
+
forecast_days: Days ahead to forecast (1–7).
|
|
59
|
+
past_hours: Past hours to include (alternative to ``past_days``).
|
|
60
|
+
forecast_hours: Hours ahead to forecast (alternative to
|
|
61
|
+
``forecast_days``).
|
|
62
|
+
start_date: Start of an explicit date range (``YYYY-MM-DD``).
|
|
63
|
+
end_date: End of an explicit date range (``YYYY-MM-DD``).
|
|
64
|
+
Required when ``start_date`` is given.
|
|
65
|
+
domains: Air quality domain to use. One of ``"auto"``,
|
|
66
|
+
``"cams_europe"``, ``"cams_global_reanalysis"``, or
|
|
67
|
+
``"cams_global_forecast"``.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
A WeatherResponse wrapping the raw API JSON.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ValidationError: If parameters fail pre-flight checks.
|
|
74
|
+
LocationNotFoundError: If a place-name string cannot be geocoded.
|
|
75
|
+
APIError: On an HTTP-level error from the server.
|
|
76
|
+
RateLimitError: On HTTP 429.
|
|
77
|
+
ServerError: On HTTP 5xx.
|
|
78
|
+
ConnectionError: If the server is unreachable.
|
|
79
|
+
"""
|
|
80
|
+
self._validate(
|
|
81
|
+
hourly=hourly,
|
|
82
|
+
current=current,
|
|
83
|
+
past_days=past_days,
|
|
84
|
+
forecast_days=forecast_days,
|
|
85
|
+
start_date=start_date,
|
|
86
|
+
end_date=end_date,
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
lat, lon = self.resolve_location(location)
|
|
90
|
+
|
|
91
|
+
params = RequestBuilder(
|
|
92
|
+
latitude=lat,
|
|
93
|
+
longitude=lon,
|
|
94
|
+
hourly=hourly,
|
|
95
|
+
current=current,
|
|
96
|
+
timeformat=timeformat,
|
|
97
|
+
timezone=timezone,
|
|
98
|
+
past_days=past_days,
|
|
99
|
+
forecast_days=forecast_days,
|
|
100
|
+
past_hours=past_hours,
|
|
101
|
+
forecast_hours=forecast_hours,
|
|
102
|
+
start_date=str(start_date) if start_date is not None else None,
|
|
103
|
+
end_date=str(end_date) if end_date is not None else None,
|
|
104
|
+
domains=domains,
|
|
105
|
+
).build()
|
|
106
|
+
|
|
107
|
+
return self._get(params)
|
|
108
|
+
|
|
109
|
+
def _validate(
|
|
110
|
+
self,
|
|
111
|
+
hourly,
|
|
112
|
+
current,
|
|
113
|
+
past_days,
|
|
114
|
+
forecast_days,
|
|
115
|
+
start_date,
|
|
116
|
+
end_date,
|
|
117
|
+
) -> None:
|
|
118
|
+
"""Validate parameters before making the API request.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
hourly: Hourly variables requested.
|
|
122
|
+
current: Current-conditions variables requested.
|
|
123
|
+
past_days: Number of past days to include.
|
|
124
|
+
forecast_days: Number of forecast days.
|
|
125
|
+
start_date: Explicit start date.
|
|
126
|
+
end_date: Explicit end date.
|
|
127
|
+
|
|
128
|
+
Raises:
|
|
129
|
+
ValidationError: If any parameter is invalid.
|
|
130
|
+
"""
|
|
131
|
+
if not any([hourly, current]):
|
|
132
|
+
raise ValidationError(
|
|
133
|
+
"At least one of hourly or current must be specified."
|
|
134
|
+
)
|
|
135
|
+
if past_days is not None and not (0 <= past_days <= 92):
|
|
136
|
+
raise ValidationError("past_days must be between 0 and 92.")
|
|
137
|
+
if forecast_days is not None and not (1 <= forecast_days <= 7):
|
|
138
|
+
raise ValidationError("forecast_days must be between 1 and 7.")
|
|
139
|
+
if (start_date is None) != (end_date is None):
|
|
140
|
+
raise ValidationError(
|
|
141
|
+
"start_date and end_date must both be provided or both omitted."
|
|
142
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pyopenmeteo.core.base_api import BaseAPI
|
|
4
|
+
from pyopenmeteo.core.exceptions import ValidationError
|
|
5
|
+
from pyopenmeteo.core.request_builder import RequestBuilder
|
|
6
|
+
from pyopenmeteo.core.response import WeatherResponse
|
|
7
|
+
from pyopenmeteo.params.climate_vars import ClimateModels, DailyClimateVars
|
|
8
|
+
from pyopenmeteo.params.units import CellSelection, PrecipitationUnit, TemperatureUnit, TimeFormat, WindSpeedUnit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ClimateAPI(BaseAPI):
|
|
12
|
+
"""Wraps the OpenMeteo Climate Change API.
|
|
13
|
+
|
|
14
|
+
Provides daily climate projections (1950–2100) based on CMIP6 models
|
|
15
|
+
downscaled to 10 km resolution. Requires an explicit date range and
|
|
16
|
+
daily variables.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> from pyopenmeteo.params.climate_vars import DailyClimateVars, ClimateModels
|
|
20
|
+
>>> api = ClimateAPI()
|
|
21
|
+
>>> resp = api.get(
|
|
22
|
+
... (40.71, -74.01),
|
|
23
|
+
... daily=[DailyClimateVars.TEMPERATURE_2M_MAX],
|
|
24
|
+
... models=[ClimateModels.MRI_AGCM3_2_S],
|
|
25
|
+
... start_date="2050-01-01",
|
|
26
|
+
... end_date="2050-12-31",
|
|
27
|
+
... )
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
BASE_URL = "https://climate-api.open-meteo.com/v1/climate"
|
|
31
|
+
|
|
32
|
+
def get(
|
|
33
|
+
self,
|
|
34
|
+
location: tuple[float, float] | list[float] | str,
|
|
35
|
+
*,
|
|
36
|
+
daily: list[DailyClimateVars | str],
|
|
37
|
+
start_date: str | date,
|
|
38
|
+
end_date: str | date,
|
|
39
|
+
models: list[ClimateModels | str] | ClimateModels | str | None = None,
|
|
40
|
+
temperature_unit: TemperatureUnit | str | None = None,
|
|
41
|
+
wind_speed_unit: WindSpeedUnit | str | None = None,
|
|
42
|
+
precipitation_unit: PrecipitationUnit | str | None = None,
|
|
43
|
+
timeformat: TimeFormat | str | None = None,
|
|
44
|
+
timezone: str | None = None,
|
|
45
|
+
cell_selection: CellSelection | str | None = None,
|
|
46
|
+
) -> WeatherResponse:
|
|
47
|
+
"""Fetch climate projection data.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
location: ``(latitude, longitude)`` tuple/list or a place-name
|
|
51
|
+
string resolved via the Geocoding API.
|
|
52
|
+
daily: Daily variables to request. Required.
|
|
53
|
+
start_date: Start of the date range (``YYYY-MM-DD``). Required.
|
|
54
|
+
end_date: End of the date range (``YYYY-MM-DD``). Required.
|
|
55
|
+
models: One or more CMIP6 climate models to use. Accepts a single
|
|
56
|
+
value or a list.
|
|
57
|
+
temperature_unit: ``celsius`` (default) or ``fahrenheit``.
|
|
58
|
+
wind_speed_unit: ``kmh`` (default), ``ms``, ``mph``, or ``kn``.
|
|
59
|
+
precipitation_unit: ``mm`` (default) or ``inch``.
|
|
60
|
+
timeformat: ``iso8601`` (default) or ``unixtime``.
|
|
61
|
+
timezone: tz database string like ``"America/New_York"`` or
|
|
62
|
+
``"auto"`` to infer from location.
|
|
63
|
+
cell_selection: ``land`` (default), ``sea``, or ``nearest``.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
A WeatherResponse wrapping the raw API JSON.
|
|
67
|
+
|
|
68
|
+
Raises:
|
|
69
|
+
ValidationError: If parameters fail pre-flight checks.
|
|
70
|
+
LocationNotFoundError: If a place-name string cannot be geocoded.
|
|
71
|
+
APIError: On an HTTP-level error from the server.
|
|
72
|
+
RateLimitError: On HTTP 429.
|
|
73
|
+
ServerError: On HTTP 5xx.
|
|
74
|
+
ConnectionError: If the server is unreachable.
|
|
75
|
+
"""
|
|
76
|
+
self._validate(
|
|
77
|
+
daily=daily,
|
|
78
|
+
start_date=start_date,
|
|
79
|
+
end_date=end_date,
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
lat, lon = self.resolve_location(location)
|
|
83
|
+
|
|
84
|
+
if isinstance(models, (str, ClimateModels)):
|
|
85
|
+
models = [models]
|
|
86
|
+
|
|
87
|
+
params = RequestBuilder(
|
|
88
|
+
latitude=lat,
|
|
89
|
+
longitude=lon,
|
|
90
|
+
daily=daily,
|
|
91
|
+
start_date=str(start_date) if start_date is not None else None,
|
|
92
|
+
end_date=str(end_date) if end_date is not None else None,
|
|
93
|
+
models=models,
|
|
94
|
+
temperature_unit=temperature_unit,
|
|
95
|
+
wind_speed_unit=wind_speed_unit,
|
|
96
|
+
precipitation_unit=precipitation_unit,
|
|
97
|
+
timeformat=timeformat,
|
|
98
|
+
timezone=timezone,
|
|
99
|
+
cell_selection=cell_selection,
|
|
100
|
+
).build()
|
|
101
|
+
|
|
102
|
+
return self._get(params)
|
|
103
|
+
|
|
104
|
+
def _validate(
|
|
105
|
+
self,
|
|
106
|
+
daily,
|
|
107
|
+
start_date,
|
|
108
|
+
end_date,
|
|
109
|
+
) -> None:
|
|
110
|
+
"""Validate parameters before making the API request.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
daily: Daily variables requested.
|
|
114
|
+
start_date: Explicit start date.
|
|
115
|
+
end_date: Explicit end date.
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
ValidationError: If any parameter is invalid.
|
|
119
|
+
"""
|
|
120
|
+
if not daily:
|
|
121
|
+
raise ValidationError("daily variables are required for the Climate API.")
|
|
122
|
+
if start_date is None or end_date is None:
|
|
123
|
+
raise ValidationError(
|
|
124
|
+
"start_date and end_date are both required for the Climate API."
|
|
125
|
+
)
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pyopenmeteo.core.base_api import BaseAPI
|
|
4
|
+
from pyopenmeteo.core.exceptions import ValidationError
|
|
5
|
+
from pyopenmeteo.core.request_builder import RequestBuilder
|
|
6
|
+
from pyopenmeteo.core.response import WeatherResponse
|
|
7
|
+
from pyopenmeteo.params.ensemble_vars import DailyEnsembleVars, EnsembleModels, HourlyEnsembleVars
|
|
8
|
+
from pyopenmeteo.params.units import CellSelection, PrecipitationUnit, TemperatureUnit, TimeFormat, WindSpeedUnit
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class EnsembleAPI(BaseAPI):
|
|
12
|
+
"""Wraps the OpenMeteo Ensemble Forecast API.
|
|
13
|
+
|
|
14
|
+
Provides probabilistic weather forecasts from ensemble model runs,
|
|
15
|
+
with up to 35 forecast days of hourly or daily data. A ``models``
|
|
16
|
+
selection is required.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> from pyopenmeteo.params.ensemble_vars import HourlyEnsembleVars, EnsembleModels
|
|
20
|
+
>>> api = EnsembleAPI()
|
|
21
|
+
>>> resp = api.get(
|
|
22
|
+
... (48.85, 2.35),
|
|
23
|
+
... hourly=[HourlyEnsembleVars.TEMPERATURE_2M],
|
|
24
|
+
... models=EnsembleModels.ICON_EU_EPS,
|
|
25
|
+
... forecast_days=7,
|
|
26
|
+
... )
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
BASE_URL = "https://ensemble-api.open-meteo.com/v1/ensemble"
|
|
30
|
+
|
|
31
|
+
def get(
|
|
32
|
+
self,
|
|
33
|
+
location: tuple[float, float] | list[float] | str,
|
|
34
|
+
*,
|
|
35
|
+
hourly: list[HourlyEnsembleVars | str] | None = None,
|
|
36
|
+
daily: list[DailyEnsembleVars | str] | None = None,
|
|
37
|
+
models: list[EnsembleModels | str] | EnsembleModels | str,
|
|
38
|
+
temperature_unit: TemperatureUnit | str | None = None,
|
|
39
|
+
wind_speed_unit: WindSpeedUnit | str | None = None,
|
|
40
|
+
precipitation_unit: PrecipitationUnit | str | None = None,
|
|
41
|
+
timeformat: TimeFormat | str | None = None,
|
|
42
|
+
timezone: str | None = None,
|
|
43
|
+
past_days: int | None = None,
|
|
44
|
+
forecast_days: int | None = None,
|
|
45
|
+
past_hours: int | None = None,
|
|
46
|
+
forecast_hours: int | None = None,
|
|
47
|
+
start_date: str | date | None = None,
|
|
48
|
+
end_date: str | date | None = None,
|
|
49
|
+
cell_selection: CellSelection | str | None = None,
|
|
50
|
+
) -> WeatherResponse:
|
|
51
|
+
"""Fetch ensemble weather forecast data.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
location: ``(latitude, longitude)`` tuple/list or a place-name
|
|
55
|
+
string resolved via the Geocoding API.
|
|
56
|
+
hourly: Hourly variables to request.
|
|
57
|
+
daily: Daily aggregate variables to request.
|
|
58
|
+
models: One or more ensemble models to use. Required. Accepts a
|
|
59
|
+
single value or a list.
|
|
60
|
+
temperature_unit: ``celsius`` (default) or ``fahrenheit``.
|
|
61
|
+
wind_speed_unit: ``kmh`` (default), ``ms``, ``mph``, or ``kn``.
|
|
62
|
+
precipitation_unit: ``mm`` (default) or ``inch``.
|
|
63
|
+
timeformat: ``iso8601`` (default) or ``unixtime``.
|
|
64
|
+
timezone: tz database string like ``"Europe/Paris"`` or ``"auto"``
|
|
65
|
+
to infer from location.
|
|
66
|
+
past_days: Include this many past days in the response (0–92).
|
|
67
|
+
forecast_days: Days ahead to forecast (1–35).
|
|
68
|
+
past_hours: Past hours to include (alternative to ``past_days``).
|
|
69
|
+
forecast_hours: Hours ahead to forecast (alternative to
|
|
70
|
+
``forecast_days``).
|
|
71
|
+
start_date: Start of an explicit date range (``YYYY-MM-DD``).
|
|
72
|
+
end_date: End of an explicit date range (``YYYY-MM-DD``).
|
|
73
|
+
Required when ``start_date`` is given.
|
|
74
|
+
cell_selection: ``land`` (default), ``sea``, or ``nearest``.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A WeatherResponse wrapping the raw API JSON.
|
|
78
|
+
|
|
79
|
+
Raises:
|
|
80
|
+
ValidationError: If parameters fail pre-flight checks.
|
|
81
|
+
LocationNotFoundError: If a place-name string cannot be geocoded.
|
|
82
|
+
APIError: On an HTTP-level error from the server.
|
|
83
|
+
RateLimitError: On HTTP 429.
|
|
84
|
+
ServerError: On HTTP 5xx.
|
|
85
|
+
ConnectionError: If the server is unreachable.
|
|
86
|
+
"""
|
|
87
|
+
self._validate(
|
|
88
|
+
hourly=hourly,
|
|
89
|
+
daily=daily,
|
|
90
|
+
models=models,
|
|
91
|
+
past_days=past_days,
|
|
92
|
+
forecast_days=forecast_days,
|
|
93
|
+
start_date=start_date,
|
|
94
|
+
end_date=end_date,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
lat, lon = self.resolve_location(location)
|
|
98
|
+
|
|
99
|
+
if isinstance(models, (str, EnsembleModels)):
|
|
100
|
+
models = [models]
|
|
101
|
+
|
|
102
|
+
params = RequestBuilder(
|
|
103
|
+
latitude=lat,
|
|
104
|
+
longitude=lon,
|
|
105
|
+
hourly=hourly,
|
|
106
|
+
daily=daily,
|
|
107
|
+
models=models,
|
|
108
|
+
temperature_unit=temperature_unit,
|
|
109
|
+
wind_speed_unit=wind_speed_unit,
|
|
110
|
+
precipitation_unit=precipitation_unit,
|
|
111
|
+
timeformat=timeformat,
|
|
112
|
+
timezone=timezone,
|
|
113
|
+
past_days=past_days,
|
|
114
|
+
forecast_days=forecast_days,
|
|
115
|
+
past_hours=past_hours,
|
|
116
|
+
forecast_hours=forecast_hours,
|
|
117
|
+
start_date=str(start_date) if start_date is not None else None,
|
|
118
|
+
end_date=str(end_date) if end_date is not None else None,
|
|
119
|
+
cell_selection=cell_selection,
|
|
120
|
+
).build()
|
|
121
|
+
|
|
122
|
+
return self._get(params)
|
|
123
|
+
|
|
124
|
+
def _validate(
|
|
125
|
+
self,
|
|
126
|
+
hourly,
|
|
127
|
+
daily,
|
|
128
|
+
models,
|
|
129
|
+
past_days,
|
|
130
|
+
forecast_days,
|
|
131
|
+
start_date,
|
|
132
|
+
end_date,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Validate parameters before making the API request.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
hourly: Hourly variables requested.
|
|
138
|
+
daily: Daily variables requested.
|
|
139
|
+
models: Ensemble model(s) requested.
|
|
140
|
+
past_days: Number of past days to include.
|
|
141
|
+
forecast_days: Number of forecast days.
|
|
142
|
+
start_date: Explicit start date.
|
|
143
|
+
end_date: Explicit end date.
|
|
144
|
+
|
|
145
|
+
Raises:
|
|
146
|
+
ValidationError: If any parameter is invalid.
|
|
147
|
+
"""
|
|
148
|
+
if not any([hourly, daily]):
|
|
149
|
+
raise ValidationError(
|
|
150
|
+
"At least one of hourly or daily must be specified."
|
|
151
|
+
)
|
|
152
|
+
if not models:
|
|
153
|
+
raise ValidationError("models is required for the Ensemble API.")
|
|
154
|
+
if past_days is not None and not (0 <= past_days <= 92):
|
|
155
|
+
raise ValidationError("past_days must be between 0 and 92.")
|
|
156
|
+
if forecast_days is not None and not (1 <= forecast_days <= 35):
|
|
157
|
+
raise ValidationError("forecast_days must be between 1 and 35.")
|
|
158
|
+
if (start_date is None) != (end_date is None):
|
|
159
|
+
raise ValidationError(
|
|
160
|
+
"start_date and end_date must both be provided or both omitted."
|
|
161
|
+
)
|
pyopenmeteo/api/flood.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from datetime import date
|
|
2
|
+
|
|
3
|
+
from pyopenmeteo.core.base_api import BaseAPI
|
|
4
|
+
from pyopenmeteo.core.exceptions import ValidationError
|
|
5
|
+
from pyopenmeteo.core.request_builder import RequestBuilder
|
|
6
|
+
from pyopenmeteo.core.response import WeatherResponse
|
|
7
|
+
from pyopenmeteo.params.flood_vars import DailyFloodVars, FloodModels
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class FloodAPI(BaseAPI):
|
|
11
|
+
"""Wraps the OpenMeteo Global Flood API.
|
|
12
|
+
|
|
13
|
+
Provides daily river discharge forecasts with up to 210 forecast days.
|
|
14
|
+
Ensemble mode returns individual ensemble member columns in the response.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> from pyopenmeteo.params.flood_vars import DailyFloodVars
|
|
18
|
+
>>> api = FloodAPI()
|
|
19
|
+
>>> resp = api.get(
|
|
20
|
+
... (48.20, 16.37),
|
|
21
|
+
... daily=[DailyFloodVars.RIVER_DISCHARGE],
|
|
22
|
+
... forecast_days=30,
|
|
23
|
+
... )
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
BASE_URL = "https://flood-api.open-meteo.com/v1/flood"
|
|
27
|
+
|
|
28
|
+
def get(
|
|
29
|
+
self,
|
|
30
|
+
location: tuple[float, float] | list[float] | str,
|
|
31
|
+
*,
|
|
32
|
+
daily: list[DailyFloodVars | str],
|
|
33
|
+
past_days: int | None = None,
|
|
34
|
+
forecast_days: int | None = None,
|
|
35
|
+
start_date: str | date | None = None,
|
|
36
|
+
end_date: str | date | None = None,
|
|
37
|
+
models: list[FloodModels | str] | FloodModels | str | None = None,
|
|
38
|
+
ensemble: bool | None = None,
|
|
39
|
+
) -> WeatherResponse:
|
|
40
|
+
"""Fetch river discharge flood forecast data.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
location: ``(latitude, longitude)`` tuple/list or a place-name
|
|
44
|
+
string resolved via the Geocoding API.
|
|
45
|
+
daily: Daily flood variables to request. Required.
|
|
46
|
+
past_days: Include this many past days in the response (0–92).
|
|
47
|
+
forecast_days: Days ahead to forecast (1–210).
|
|
48
|
+
start_date: Start of an explicit date range (``YYYY-MM-DD``).
|
|
49
|
+
end_date: End of an explicit date range (``YYYY-MM-DD``).
|
|
50
|
+
Required when ``start_date`` is given.
|
|
51
|
+
models: One or more hydrological models to use. Accepts a single
|
|
52
|
+
value or a list.
|
|
53
|
+
ensemble: When ``True``, returns all ensemble members as separate
|
|
54
|
+
columns in the response.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
A WeatherResponse wrapping the raw API JSON.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
ValidationError: If parameters fail pre-flight checks.
|
|
61
|
+
LocationNotFoundError: If a place-name string cannot be geocoded.
|
|
62
|
+
APIError: On an HTTP-level error from the server.
|
|
63
|
+
RateLimitError: On HTTP 429.
|
|
64
|
+
ServerError: On HTTP 5xx.
|
|
65
|
+
ConnectionError: If the server is unreachable.
|
|
66
|
+
"""
|
|
67
|
+
self._validate(
|
|
68
|
+
daily=daily,
|
|
69
|
+
past_days=past_days,
|
|
70
|
+
forecast_days=forecast_days,
|
|
71
|
+
start_date=start_date,
|
|
72
|
+
end_date=end_date,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
lat, lon = self.resolve_location(location)
|
|
76
|
+
|
|
77
|
+
if isinstance(models, (str, FloodModels)):
|
|
78
|
+
models = [models]
|
|
79
|
+
|
|
80
|
+
params = RequestBuilder(
|
|
81
|
+
latitude=lat,
|
|
82
|
+
longitude=lon,
|
|
83
|
+
daily=daily,
|
|
84
|
+
past_days=past_days,
|
|
85
|
+
forecast_days=forecast_days,
|
|
86
|
+
start_date=str(start_date) if start_date is not None else None,
|
|
87
|
+
end_date=str(end_date) if end_date is not None else None,
|
|
88
|
+
models=models,
|
|
89
|
+
ensemble=ensemble,
|
|
90
|
+
).build()
|
|
91
|
+
|
|
92
|
+
return self._get(params)
|
|
93
|
+
|
|
94
|
+
def _validate(
|
|
95
|
+
self,
|
|
96
|
+
daily,
|
|
97
|
+
past_days,
|
|
98
|
+
forecast_days,
|
|
99
|
+
start_date,
|
|
100
|
+
end_date,
|
|
101
|
+
) -> None:
|
|
102
|
+
"""Validate parameters before making the API request.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
daily: Daily variables requested.
|
|
106
|
+
past_days: Number of past days to include.
|
|
107
|
+
forecast_days: Number of forecast days.
|
|
108
|
+
start_date: Explicit start date.
|
|
109
|
+
end_date: Explicit end date.
|
|
110
|
+
|
|
111
|
+
Raises:
|
|
112
|
+
ValidationError: If any parameter is invalid.
|
|
113
|
+
"""
|
|
114
|
+
if not daily:
|
|
115
|
+
raise ValidationError("daily variables are required for the Flood API.")
|
|
116
|
+
if past_days is not None and not (0 <= past_days <= 92):
|
|
117
|
+
raise ValidationError("past_days must be between 0 and 92.")
|
|
118
|
+
if forecast_days is not None and not (1 <= forecast_days <= 210):
|
|
119
|
+
raise ValidationError("forecast_days must be between 1 and 210.")
|
|
120
|
+
if (start_date is None) != (end_date is None):
|
|
121
|
+
raise ValidationError(
|
|
122
|
+
"start_date and end_date must both be provided or both omitted."
|
|
123
|
+
)
|