pyopenmeteo 0.1.0__tar.gz
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-0.1.0/LICENSE.md +7 -0
- pyopenmeteo-0.1.0/PKG-INFO +548 -0
- pyopenmeteo-0.1.0/README.md +505 -0
- pyopenmeteo-0.1.0/pyopenmeteo/__init__.py +78 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/__init__.py +26 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/air_quality.py +142 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/climate.py +125 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/ensemble.py +161 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/flood.py +123 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/forecast.py +220 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/geo.py +213 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/historical.py +153 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/marine.py +174 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/satellite.py +158 -0
- pyopenmeteo-0.1.0/pyopenmeteo/api/seasonal.py +165 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/__init__.py +27 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/base_api.py +105 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/exceptions.py +47 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/http_client.py +76 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/request_builder.py +33 -0
- pyopenmeteo-0.1.0/pyopenmeteo/core/response.py +250 -0
- pyopenmeteo-0.1.0/pyopenmeteo/formatters/__init__.py +8 -0
- pyopenmeteo-0.1.0/pyopenmeteo/formatters/csv_fmt.py +39 -0
- pyopenmeteo-0.1.0/pyopenmeteo/formatters/numpy_fmt.py +36 -0
- pyopenmeteo-0.1.0/pyopenmeteo/formatters/pandas_fmt.py +60 -0
- pyopenmeteo-0.1.0/pyopenmeteo/formatters/xarray_fmt.py +111 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/__init__.py +17 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/airquality_vars.py +55 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/archive_vars.py +147 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/climate_vars.py +31 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/ensemble_vars.py +222 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/flood_vars.py +19 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/forecast_vars.py +236 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/marine_vars.py +80 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/pressure_levels.py +98 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/satellite_vars.py +36 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/seasonal_vars.py +256 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/solar.py +57 -0
- pyopenmeteo-0.1.0/pyopenmeteo/params/units.py +34 -0
- pyopenmeteo-0.1.0/pyopenmeteo.egg-info/PKG-INFO +548 -0
- pyopenmeteo-0.1.0/pyopenmeteo.egg-info/SOURCES.txt +53 -0
- pyopenmeteo-0.1.0/pyopenmeteo.egg-info/dependency_links.txt +1 -0
- pyopenmeteo-0.1.0/pyopenmeteo.egg-info/requires.txt +25 -0
- pyopenmeteo-0.1.0/pyopenmeteo.egg-info/top_level.txt +2 -0
- pyopenmeteo-0.1.0/pyproject.toml +52 -0
- pyopenmeteo-0.1.0/setup.cfg +4 -0
- pyopenmeteo-0.1.0/tests/test_exceptions.py +120 -0
- pyopenmeteo-0.1.0/tests/test_forecast.py +234 -0
- pyopenmeteo-0.1.0/tests/test_geo.py +178 -0
- pyopenmeteo-0.1.0/tests/test_historical.py +144 -0
- pyopenmeteo-0.1.0/tests/test_http_client.py +182 -0
- pyopenmeteo-0.1.0/tests/test_pressure_levels.py +85 -0
- pyopenmeteo-0.1.0/tests/test_request_builder.py +82 -0
- pyopenmeteo-0.1.0/tests/test_response.py +240 -0
- pyopenmeteo-0.1.0/tests/test_solar.py +90 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright (c) 2026
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1,548 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyopenmeteo
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python wrapper for the Open-Meteo weather API family
|
|
5
|
+
Author: jania
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/jania/pyopenmeteo
|
|
8
|
+
Project-URL: Issues, https://github.com/jania/pyopenmeteo/issues
|
|
9
|
+
Keywords: weather,forecast,open-meteo,climate,meteorology
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
|
|
18
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE.md
|
|
22
|
+
Requires-Dist: requests>=2.28
|
|
23
|
+
Provides-Extra: pandas
|
|
24
|
+
Requires-Dist: pandas>=1.5; extra == "pandas"
|
|
25
|
+
Requires-Dist: openpyxl>=3.0; extra == "pandas"
|
|
26
|
+
Provides-Extra: numpy
|
|
27
|
+
Requires-Dist: numpy>=1.24; extra == "numpy"
|
|
28
|
+
Provides-Extra: xarray
|
|
29
|
+
Requires-Dist: xarray>=2023.1; extra == "xarray"
|
|
30
|
+
Requires-Dist: numpy>=1.24; extra == "xarray"
|
|
31
|
+
Provides-Extra: all
|
|
32
|
+
Requires-Dist: pandas>=1.5; extra == "all"
|
|
33
|
+
Requires-Dist: openpyxl>=3.0; extra == "all"
|
|
34
|
+
Requires-Dist: numpy>=1.24; extra == "all"
|
|
35
|
+
Requires-Dist: xarray>=2023.1; extra == "all"
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
38
|
+
Requires-Dist: pandas>=1.5; extra == "dev"
|
|
39
|
+
Requires-Dist: numpy>=1.24; extra == "dev"
|
|
40
|
+
Requires-Dist: xarray>=2023.1; extra == "dev"
|
|
41
|
+
Requires-Dist: openpyxl>=3.0; extra == "dev"
|
|
42
|
+
Dynamic: license-file
|
|
43
|
+
|
|
44
|
+
# pyopenmeteo
|
|
45
|
+
|
|
46
|
+
A Python wrapper for the [Open-Meteo](https://open-meteo.com/) family of weather APIs.
|
|
47
|
+
|
|
48
|
+
Open-Meteo is one of the best free weather data sources available. It has global coverage, hourly resolution, 80+ years of historical data, ensemble forecasts, climate projections, air quality, marine data, and more -- all with no API key.
|
|
49
|
+
|
|
50
|
+
pyopenmeteo wraps the API so you don't have to hand-build query strings, deal with raw JSON, or look up parameter names every time. Pass a city name or coordinates, get back a pandas DataFrame.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Contents
|
|
55
|
+
|
|
56
|
+
- [What's included](#whats-included)
|
|
57
|
+
- [Installation](#installation)
|
|
58
|
+
- [Quick start](#quick-start)
|
|
59
|
+
- [APIs included](#apis-included)
|
|
60
|
+
- [ForecastAPI](#forecastapi)
|
|
61
|
+
- [HistoricalAPI](#historicalapi)
|
|
62
|
+
- [MarineAPI](#marineapi)
|
|
63
|
+
- [AirQualityAPI](#airqualityapi)
|
|
64
|
+
- [ClimateAPI](#climateapi)
|
|
65
|
+
- [EnsembleAPI](#ensembleapi)
|
|
66
|
+
- [FloodAPI](#floodapi)
|
|
67
|
+
- [SeasonalAPI](#seasonalapi)
|
|
68
|
+
- [GeocodingAPI & ElevationAPI](#geocodingapi--elevationapi)
|
|
69
|
+
- [Working with responses](#working-with-responses)
|
|
70
|
+
- [Params](#params)
|
|
71
|
+
- [Units and options](#units-and-options)
|
|
72
|
+
- [Error handling](#error-handling)
|
|
73
|
+
- [Commercial API key](#commercial-api-key)
|
|
74
|
+
- [License](#license)
|
|
75
|
+
|
|
76
|
+
### Full documentation
|
|
77
|
+
|
|
78
|
+
| Doc | Description |
|
|
79
|
+
|-----|-------------|
|
|
80
|
+
| [Quick start](docs/quickstart.md) | Minimal working examples for each API |
|
|
81
|
+
| [Forecast](docs/forecast.md) | ForecastAPI reference |
|
|
82
|
+
| [Historical](docs/historical.md) | HistoricalAPI reference |
|
|
83
|
+
| [Marine](docs/marine.md) | MarineAPI reference |
|
|
84
|
+
| [Air quality](docs/air_quality.md) | AirQualityAPI reference |
|
|
85
|
+
| [Climate](docs/climate.md) | ClimateAPI reference |
|
|
86
|
+
| [Ensemble](docs/ensemble.md) | EnsembleAPI reference |
|
|
87
|
+
| [Flood](docs/flood.md) | FloodAPI reference |
|
|
88
|
+
| [Seasonal](docs/seasonal.md) | SeasonalAPI reference |
|
|
89
|
+
| [Satellite](docs/satellite.md) | SatelliteRadiationAPI reference |
|
|
90
|
+
| [Geocoding & elevation](docs/geo.md) | GeocodingAPI and ElevationAPI reference |
|
|
91
|
+
| [Response](docs/response.md) | WeatherResponse methods and formatters |
|
|
92
|
+
| [Params](docs/params.md) | Variable enums and units |
|
|
93
|
+
| [Exceptions](docs/exceptions.md) | Exception hierarchy |
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## What's included
|
|
98
|
+
|
|
99
|
+
| API | What it gives you | Horizon |
|
|
100
|
+
|-----|-------------------|---------|
|
|
101
|
+
| `ForecastAPI` | Hourly, 15-min, daily, current-conditions forecasts | 16 days |
|
|
102
|
+
| `HistoricalAPI` | Reanalysis data back to 1940 | any date range |
|
|
103
|
+
| `HistoricalForecastAPI` | Archived model runs | any date range |
|
|
104
|
+
| `MarineAPI` | Wave height, swell, currents | 16 days |
|
|
105
|
+
| `ClimateAPI` | CMIP6 climate projections | 1950–2100 |
|
|
106
|
+
| `AirQualityAPI` | Pollutants, pollen, AQI | 7 days |
|
|
107
|
+
| `EnsembleAPI` | Probabilistic ensemble runs | 35 days |
|
|
108
|
+
| `FloodAPI` | River discharge forecasts | 210 days |
|
|
109
|
+
| `SatelliteRadiationAPI` | Solar irradiance from satellites | historical + near-realtime |
|
|
110
|
+
| `SeasonalAPI` | Long-range seasonal forecasts | 274 days |
|
|
111
|
+
| `GeocodingAPI` | Fuzzy place-name → coordinates | - |
|
|
112
|
+
| `ElevationAPI` | Coordinates → elevation (Copernicus DEM) | - |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Installation
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
pip install pyopenmeteo
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Optional extras for data conversion:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
pip install pyopenmeteo[pandas] # to_pandas(), to_csv(), to_excel()
|
|
126
|
+
pip install pyopenmeteo[numpy] # to_numpy()
|
|
127
|
+
pip install pyopenmeteo[xarray] # formatters.to_dataset()
|
|
128
|
+
pip install pyopenmeteo[all] # everything above
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
## Quick start
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from pyopenmeteo.api.forecast import ForecastAPI
|
|
137
|
+
from pyopenmeteo.params.forecast_vars import HourlyForecastVar, DailyForecastVar
|
|
138
|
+
|
|
139
|
+
api = ForecastAPI()
|
|
140
|
+
|
|
141
|
+
# Pass a place name - geocoding is handled automatically
|
|
142
|
+
resp = api.get(
|
|
143
|
+
"Toronto",
|
|
144
|
+
hourly=[HourlyForecastVar.TEMPERATURE_2M, HourlyForecastVar.PRECIPITATION],
|
|
145
|
+
daily=[DailyForecastVar.TEMPERATURE_2M_MAX, DailyForecastVar.PRECIPITATION_SUM],
|
|
146
|
+
timezone="America/Toronto",
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
print(resp)
|
|
150
|
+
# WeatherResponse(loc=43.7001,-79.4163; hourly×168, daily×7)
|
|
151
|
+
|
|
152
|
+
df = resp.to_pandas("hourly")
|
|
153
|
+
print(df.head())
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Or pass coordinates directly:
|
|
157
|
+
|
|
158
|
+
```python
|
|
159
|
+
resp = api.get(
|
|
160
|
+
(43.7001, -79.4163),
|
|
161
|
+
hourly=[HourlyForecastVar.WIND_SPEED_10M],
|
|
162
|
+
forecast_days=3,
|
|
163
|
+
)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
## APIs included
|
|
169
|
+
|
|
170
|
+
### ForecastAPI
|
|
171
|
+
|
|
172
|
+
Up to 16 days of forecast data at any location.
|
|
173
|
+
|
|
174
|
+
```python
|
|
175
|
+
from pyopenmeteo.api.forecast import ForecastAPI
|
|
176
|
+
from pyopenmeteo.params.forecast_vars import HourlyForecastVar, DailyForecastVar, CurrentForecastVar
|
|
177
|
+
from pyopenmeteo.params.units import TemperatureUnit, WindSpeedUnit
|
|
178
|
+
|
|
179
|
+
api = ForecastAPI()
|
|
180
|
+
|
|
181
|
+
resp = api.get(
|
|
182
|
+
"London",
|
|
183
|
+
hourly=[
|
|
184
|
+
HourlyForecastVar.TEMPERATURE_2M,
|
|
185
|
+
HourlyForecastVar.RELATIVE_HUMIDITY_2M,
|
|
186
|
+
HourlyForecastVar.WIND_SPEED_10M,
|
|
187
|
+
HourlyForecastVar.PRECIPITATION_PROBABILITY,
|
|
188
|
+
],
|
|
189
|
+
daily=[
|
|
190
|
+
DailyForecastVar.TEMPERATURE_2M_MAX,
|
|
191
|
+
DailyForecastVar.TEMPERATURE_2M_MIN,
|
|
192
|
+
DailyForecastVar.PRECIPITATION_SUM,
|
|
193
|
+
DailyForecastVar.SUNRISE,
|
|
194
|
+
DailyForecastVar.SUNSET,
|
|
195
|
+
],
|
|
196
|
+
current=[CurrentForecastVar.TEMPERATURE_2M],
|
|
197
|
+
temperature_unit=TemperatureUnit.FAHRENHEIT,
|
|
198
|
+
wind_speed_unit=WindSpeedUnit.MPH,
|
|
199
|
+
timezone="Europe/London",
|
|
200
|
+
forecast_days=7,
|
|
201
|
+
)
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
**Pressure-level variables** (upper atmosphere data):
|
|
205
|
+
|
|
206
|
+
```python
|
|
207
|
+
from pyopenmeteo.params.pressure_levels import PressureVar, pressure_level, pressure_levels_range
|
|
208
|
+
|
|
209
|
+
resp = api.get(
|
|
210
|
+
(43.65, -79.38),
|
|
211
|
+
hourly=[
|
|
212
|
+
pressure_level(PressureVar.TEMPERATURE, 850), # "temperature_850hPa"
|
|
213
|
+
pressure_level(PressureVar.WIND_SPEED, 500), # "wind_speed_500hPa"
|
|
214
|
+
*pressure_levels_range(PressureVar.GEOPOTENTIAL_HEIGHT, 500, 1000),
|
|
215
|
+
],
|
|
216
|
+
)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Valid pressure levels: 30, 50, 70, 100, 150, 200, 250, 300, 400, 500, 600, 700, 800, 850, 900, 925, 950, 975, 1000 hPa.
|
|
220
|
+
|
|
221
|
+
**Solar panel irradiance** (GTI variables require a panel orientation):
|
|
222
|
+
|
|
223
|
+
```python
|
|
224
|
+
from pyopenmeteo.params.solar import PanelOrientation
|
|
225
|
+
from pyopenmeteo.params.forecast_vars import HourlyForecastVar
|
|
226
|
+
|
|
227
|
+
resp = api.get(
|
|
228
|
+
(43.65, -79.38),
|
|
229
|
+
hourly=[HourlyForecastVar.GLOBAL_TILTED_IRRADIANCE],
|
|
230
|
+
panel=PanelOrientation(tilt=35, azimuth=0), # south-facing, 35° tilt
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
# Single-axis tracker: pass float("nan") for the tracked axis
|
|
234
|
+
panel = PanelOrientation(tilt=35, azimuth=float("nan"))
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### HistoricalAPI
|
|
240
|
+
|
|
241
|
+
Reanalysis data from 1940 onward. Requires explicit date range and at least one variable group.
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from pyopenmeteo.api.historical import HistoricalAPI
|
|
245
|
+
from pyopenmeteo.params.archive_vars import HourlyArchiveVars, DailyArchiveVars
|
|
246
|
+
|
|
247
|
+
api = HistoricalAPI()
|
|
248
|
+
resp = api.get(
|
|
249
|
+
"Berlin",
|
|
250
|
+
start_date="2020-01-01",
|
|
251
|
+
end_date="2020-12-31",
|
|
252
|
+
hourly=[HourlyArchiveVars.TEMPERATURE_2M, HourlyArchiveVars.PRECIPITATION],
|
|
253
|
+
daily=[DailyArchiveVars.TEMPERATURE_2M_MAX],
|
|
254
|
+
timezone="Europe/Berlin",
|
|
255
|
+
)
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
---
|
|
259
|
+
|
|
260
|
+
### MarineAPI
|
|
261
|
+
|
|
262
|
+
Wave and swell forecasts up to 16 days.
|
|
263
|
+
|
|
264
|
+
```python
|
|
265
|
+
from pyopenmeteo.api.marine import MarineAPI
|
|
266
|
+
from pyopenmeteo.params.marine_vars import HourlyMarineVars, DailyMarineVars
|
|
267
|
+
|
|
268
|
+
api = MarineAPI()
|
|
269
|
+
resp = api.get(
|
|
270
|
+
(51.5, -1.8), # English Channel
|
|
271
|
+
hourly=[HourlyMarineVars.WAVE_HEIGHT, HourlyMarineVars.WAVE_DIRECTION],
|
|
272
|
+
daily=[DailyMarineVars.WAVE_HEIGHT_MAX],
|
|
273
|
+
)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
### AirQualityAPI
|
|
279
|
+
|
|
280
|
+
Pollutant concentrations, pollen levels, and AQI up to 7 days ahead.
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
from pyopenmeteo.api.air_quality import AirQualityAPI
|
|
284
|
+
from pyopenmeteo.params.airquality_vars import HourlyAirQualityVars
|
|
285
|
+
|
|
286
|
+
api = AirQualityAPI()
|
|
287
|
+
resp = api.get(
|
|
288
|
+
"Beijing",
|
|
289
|
+
hourly=[
|
|
290
|
+
HourlyAirQualityVars.PM2_5,
|
|
291
|
+
HourlyAirQualityVars.PM10,
|
|
292
|
+
HourlyAirQualityVars.EUROPEAN_AQI,
|
|
293
|
+
],
|
|
294
|
+
timezone="Asia/Shanghai",
|
|
295
|
+
)
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
---
|
|
299
|
+
|
|
300
|
+
### ClimateAPI
|
|
301
|
+
|
|
302
|
+
CMIP6 climate projections at 10 km resolution from 1950 to 2100. Requires a date range and daily variables.
|
|
303
|
+
|
|
304
|
+
```python
|
|
305
|
+
from pyopenmeteo.api.climate import ClimateAPI
|
|
306
|
+
from pyopenmeteo.params.climate_vars import DailyClimateVars, ClimateModels
|
|
307
|
+
|
|
308
|
+
api = ClimateAPI()
|
|
309
|
+
resp = api.get(
|
|
310
|
+
"New York",
|
|
311
|
+
start_date="2050-01-01",
|
|
312
|
+
end_date="2050-12-31",
|
|
313
|
+
daily=[DailyClimateVars.TEMPERATURE_2M_MAX, DailyClimateVars.PRECIPITATION_SUM],
|
|
314
|
+
models=[ClimateModels.MRI_AGCM3_2_S, ClimateModels.EC_EARTH3P_HR],
|
|
315
|
+
)
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
### EnsembleAPI
|
|
321
|
+
|
|
322
|
+
Probabilistic forecasts from multiple ensemble members, up to 35 days.
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from pyopenmeteo.api.ensemble import EnsembleAPI
|
|
326
|
+
from pyopenmeteo.params.ensemble_vars import HourlyEnsembleVars, EnsembleModels
|
|
327
|
+
|
|
328
|
+
api = EnsembleAPI()
|
|
329
|
+
resp = api.get(
|
|
330
|
+
"Tokyo",
|
|
331
|
+
hourly=[HourlyEnsembleVars.TEMPERATURE_2M, HourlyEnsembleVars.PRECIPITATION],
|
|
332
|
+
models=[EnsembleModels.ICON_EU_EPS, EnsembleModels.NCEP_GEFS_SEAMLESS],
|
|
333
|
+
)
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### FloodAPI
|
|
339
|
+
|
|
340
|
+
River discharge forecasts up to 210 days.
|
|
341
|
+
|
|
342
|
+
```python
|
|
343
|
+
from pyopenmeteo.api.flood import FloodAPI
|
|
344
|
+
from pyopenmeteo.params.flood_vars import DailyFloodVars
|
|
345
|
+
|
|
346
|
+
api = FloodAPI()
|
|
347
|
+
resp = api.get(
|
|
348
|
+
(48.2, 16.4), # near Vienna on the Danube
|
|
349
|
+
daily=[DailyFloodVars.RIVER_DISCHARGE],
|
|
350
|
+
ensemble=True,
|
|
351
|
+
)
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
---
|
|
355
|
+
|
|
356
|
+
### SeasonalAPI
|
|
357
|
+
|
|
358
|
+
Long-range seasonal forecasts at weekly or monthly resolution up to 274 days.
|
|
359
|
+
|
|
360
|
+
```python
|
|
361
|
+
from pyopenmeteo.api.seasonal import SeasonalAPI
|
|
362
|
+
from pyopenmeteo.params.seasonal_vars import WeeklySeasonalVars
|
|
363
|
+
|
|
364
|
+
api = SeasonalAPI()
|
|
365
|
+
resp = api.get(
|
|
366
|
+
"Lagos",
|
|
367
|
+
weekly=[WeeklySeasonalVars.TEMPERATURE_2M_MEAN],
|
|
368
|
+
timezone="Africa/Lagos",
|
|
369
|
+
)
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
---
|
|
373
|
+
|
|
374
|
+
### GeocodingAPI & ElevationAPI
|
|
375
|
+
|
|
376
|
+
```python
|
|
377
|
+
from pyopenmeteo.api.geo import GeocodingAPI, ElevationAPI
|
|
378
|
+
|
|
379
|
+
geo = GeocodingAPI()
|
|
380
|
+
|
|
381
|
+
# Fuzzy search - returns the top result
|
|
382
|
+
loc = geo.search("paris")
|
|
383
|
+
print(loc.name, loc.latitude, loc.longitude, loc.country)
|
|
384
|
+
# Paris 48.8534 2.3488 France
|
|
385
|
+
|
|
386
|
+
# All results
|
|
387
|
+
results = geo.search_all("paris", count=5)
|
|
388
|
+
|
|
389
|
+
# Look up by GeoNames ID
|
|
390
|
+
loc = geo.get_by_id(6455259)
|
|
391
|
+
|
|
392
|
+
# Elevation lookup
|
|
393
|
+
elev = ElevationAPI()
|
|
394
|
+
altitude = elev.get(48.85, 2.35) # scalar → float
|
|
395
|
+
altitudes = elev.get([48.85, 51.5], [2.35, -0.12]) # lists → list[float]
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
400
|
+
## Working with responses
|
|
401
|
+
|
|
402
|
+
Every API returns a `WeatherResponse` object.
|
|
403
|
+
|
|
404
|
+
```python
|
|
405
|
+
resp = api.get(...)
|
|
406
|
+
|
|
407
|
+
# Inspect what's available
|
|
408
|
+
resp.has_hourly() # True / False
|
|
409
|
+
resp.has_daily()
|
|
410
|
+
resp.has_minutely_15()
|
|
411
|
+
resp.has_current()
|
|
412
|
+
|
|
413
|
+
resp.get_hourly() # ["temperature_2m", "precipitation", ...]
|
|
414
|
+
resp.get_daily()
|
|
415
|
+
|
|
416
|
+
# Metadata
|
|
417
|
+
resp.latitude
|
|
418
|
+
resp.longitude
|
|
419
|
+
resp.elevation
|
|
420
|
+
resp.timezone
|
|
421
|
+
|
|
422
|
+
# Raw JSON
|
|
423
|
+
resp.to_dict()
|
|
424
|
+
|
|
425
|
+
# pandas DataFrame (indexed by DatetimeIndex)
|
|
426
|
+
df = resp.to_pandas("hourly")
|
|
427
|
+
df = resp.to_pandas("daily")
|
|
428
|
+
df = resp.to_pandas("current")
|
|
429
|
+
|
|
430
|
+
# NumPy arrays
|
|
431
|
+
arrays = resp.to_numpy("hourly")
|
|
432
|
+
# {"time": array(['2024-01-01T00:00:00', ...], dtype='datetime64[s]'),
|
|
433
|
+
# "temperature_2m": array([...], dtype=float64)}
|
|
434
|
+
|
|
435
|
+
# Write to disk
|
|
436
|
+
resp.to_csv("weather.csv", section="hourly")
|
|
437
|
+
resp.to_excel("weather.xlsx", section="daily")
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
### Standalone formatters
|
|
441
|
+
|
|
442
|
+
The `formatters` module provides functions that work with any response:
|
|
443
|
+
|
|
444
|
+
```python
|
|
445
|
+
from pyopenmeteo.formatters import to_dataframe, to_arrays, to_csv, to_dataset
|
|
446
|
+
|
|
447
|
+
df = to_dataframe(resp, section="hourly")
|
|
448
|
+
arrays = to_arrays(resp, section="daily")
|
|
449
|
+
to_csv(resp, "out.csv", section="hourly")
|
|
450
|
+
ds = to_dataset(resp, section="hourly") # xarray Dataset
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
---
|
|
454
|
+
|
|
455
|
+
## Params
|
|
456
|
+
|
|
457
|
+
Every variable you can request from an API is defined as a `StrEnum` in `pyopenmeteo.params`. Using these instead of raw strings gives you IDE autocomplete, catches typos at import time, and makes it easy to discover what's available without reading the Open-Meteo docs.
|
|
458
|
+
|
|
459
|
+
Each enum member's value is exactly the string the API expects, so you can always drop down to a raw string if you need a variable that isn't in the enum yet.
|
|
460
|
+
|
|
461
|
+
| Module | Classes |
|
|
462
|
+
|--------|---------|
|
|
463
|
+
| `params.forecast_vars` | `HourlyForecastVar`, `Minutely15ForecastVar`, `DailyForecastVar`, `CurrentForecastVar`, `ForecastModels` |
|
|
464
|
+
| `params.archive_vars` | `HourlyArchiveVars`, `DailyArchiveVars`, `ArchiveModels` |
|
|
465
|
+
| `params.marine_vars` | `HourlyMarineVars`, `Minutely15MarineVars`, `DailyMarineVars`, `CurrentMarineVars`, `MarineModels` |
|
|
466
|
+
| `params.airquality_vars` | `HourlyAirQualityVars`, `CurrentAirQualityVars` |
|
|
467
|
+
| `params.climate_vars` | `DailyClimateVars`, `ClimateModels` |
|
|
468
|
+
| `params.ensemble_vars` | `HourlyEnsembleVars`, `DailyEnsembleVars`, `EnsembleModels` |
|
|
469
|
+
| `params.flood_vars` | `DailyFloodVars`, `FloodModels` |
|
|
470
|
+
| `params.satellite_vars` | `HourlySatelliteVars`, `DailySatelliteVars`, `SatelliteModels` |
|
|
471
|
+
| `params.seasonal_vars` | `HourlyVars`, `DailySeasonalVars`, `WeeklySeasonalVars`, `MonthlySeasonalVars`, `SeasonalModels` |
|
|
472
|
+
| `params.pressure_levels` | `PressureVar`, `pressure_level()`, `pressure_levels_range()` |
|
|
473
|
+
| `params.solar` | `PanelOrientation` |
|
|
474
|
+
| `params.units` | `TemperatureUnit`, `WindSpeedUnit`, `PrecipitationUnit`, `TimeFormat`, `CellSelection`, `LengthUnit` |
|
|
475
|
+
|
|
476
|
+
```python
|
|
477
|
+
from pyopenmeteo.params.forecast_vars import HourlyForecastVar
|
|
478
|
+
|
|
479
|
+
# Autocomplete shows every available variable
|
|
480
|
+
HourlyForecastVar.TEMPERATURE_2M # "temperature_2m"
|
|
481
|
+
HourlyForecastVar.WIND_SPEED_10M # "wind_speed_10m"
|
|
482
|
+
HourlyForecastVar.PRECIPITATION # "precipitation"
|
|
483
|
+
|
|
484
|
+
# Raw strings work anywhere an enum is accepted
|
|
485
|
+
resp = api.get("Oslo", hourly=["temperature_2m", HourlyForecastVar.PRECIPITATION])
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
---
|
|
489
|
+
|
|
490
|
+
## Units and options
|
|
491
|
+
|
|
492
|
+
```python
|
|
493
|
+
from pyopenmeteo.params.units import (
|
|
494
|
+
TemperatureUnit, # CELSIUS, FAHRENHEIT
|
|
495
|
+
WindSpeedUnit, # KMH, MS, MPH, KN
|
|
496
|
+
PrecipitationUnit, # MM, INCH
|
|
497
|
+
TimeFormat, # ISO8601, UNIXTIME
|
|
498
|
+
CellSelection, # LAND, SEA, NEAREST
|
|
499
|
+
LengthUnit, # METRIC, IMPERIAL
|
|
500
|
+
)
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## Error handling
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
from pyopenmeteo.core.exceptions import (
|
|
509
|
+
MeteoPyError, # catch-all base class
|
|
510
|
+
APIError, # 4xx from the API (has .status_code, .reason)
|
|
511
|
+
RateLimitError, # 429 - subclass of APIError
|
|
512
|
+
ServerError, # 5xx - subclass of APIError
|
|
513
|
+
GeocodingError, # base for location errors
|
|
514
|
+
LocationNotFoundError, # place name returned zero results (has .query)
|
|
515
|
+
ValidationError, # bad parameters before any HTTP call
|
|
516
|
+
ConnectionError, # network unreachable
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
from pyopenmeteo.api.forecast import ForecastAPI
|
|
520
|
+
|
|
521
|
+
api = ForecastAPI()
|
|
522
|
+
try:
|
|
523
|
+
resp = api.get("Atlantis", hourly=["temperature_2m"])
|
|
524
|
+
except LocationNotFoundError as e:
|
|
525
|
+
print(f"Could not find: {e.query}")
|
|
526
|
+
except RateLimitError:
|
|
527
|
+
print("Hit the rate limit - add an API key or slow down")
|
|
528
|
+
except APIError as e:
|
|
529
|
+
print(f"API error {e.status_code}: {e.reason}")
|
|
530
|
+
except MeteoPyError as e:
|
|
531
|
+
print(f"Something went wrong: {e}")
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## Commercial API key
|
|
537
|
+
|
|
538
|
+
Open-Meteo offers a [commercial API](https://open-meteo.com/en/pricing) for higher rate limits. Pass the key to any API client:
|
|
539
|
+
|
|
540
|
+
```python
|
|
541
|
+
api = ForecastAPI(apikey="your-key-here")
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
---
|
|
545
|
+
|
|
546
|
+
## License
|
|
547
|
+
|
|
548
|
+
This project is licensed under the [MIT License](LICENSE).
|