meteostat 1.7.5__tar.gz → 2.0.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.
- meteostat-2.0.0/PKG-INFO +134 -0
- meteostat-2.0.0/README.md +116 -0
- meteostat-2.0.0/meteostat/__init__.py +48 -0
- meteostat-2.0.0/meteostat/api/daily.py +76 -0
- meteostat-2.0.0/meteostat/api/hourly.py +80 -0
- meteostat-2.0.0/meteostat/api/interpolate.py +240 -0
- meteostat-2.0.0/meteostat/api/inventory.py +59 -0
- meteostat-2.0.0/meteostat/api/merge.py +103 -0
- meteostat-2.0.0/meteostat/api/monthly.py +73 -0
- meteostat-2.0.0/meteostat/api/normals.py +144 -0
- meteostat-2.0.0/meteostat/api/point.py +30 -0
- meteostat-2.0.0/meteostat/api/stations.py +234 -0
- meteostat-2.0.0/meteostat/api/timeseries.py +334 -0
- meteostat-2.0.0/meteostat/core/cache.py +224 -0
- meteostat-2.0.0/meteostat/core/config.py +158 -0
- meteostat-2.0.0/meteostat/core/data.py +199 -0
- meteostat-2.0.0/meteostat/core/logger.py +9 -0
- meteostat-2.0.0/meteostat/core/network.py +82 -0
- meteostat-2.0.0/meteostat/core/parameters.py +112 -0
- meteostat-2.0.0/meteostat/core/providers.py +184 -0
- meteostat-2.0.0/meteostat/core/schema.py +170 -0
- meteostat-2.0.0/meteostat/core/validator.py +38 -0
- meteostat-2.0.0/meteostat/enumerations.py +149 -0
- meteostat-2.0.0/meteostat/interpolation/idw.py +120 -0
- meteostat-2.0.0/meteostat/interpolation/lapserate.py +91 -0
- meteostat-2.0.0/meteostat/interpolation/nearest.py +31 -0
- meteostat-2.0.0/meteostat/parameters.py +354 -0
- meteostat-2.0.0/meteostat/providers/dwd/climat.py +166 -0
- meteostat-2.0.0/meteostat/providers/dwd/daily.py +144 -0
- meteostat-2.0.0/meteostat/providers/dwd/hourly.py +218 -0
- meteostat-2.0.0/meteostat/providers/dwd/monthly.py +138 -0
- meteostat-2.0.0/meteostat/providers/dwd/mosmix.py +351 -0
- meteostat-2.0.0/meteostat/providers/dwd/poi.py +117 -0
- meteostat-2.0.0/meteostat/providers/dwd/shared.py +155 -0
- meteostat-2.0.0/meteostat/providers/eccc/daily.py +87 -0
- meteostat-2.0.0/meteostat/providers/eccc/hourly.py +104 -0
- meteostat-2.0.0/meteostat/providers/eccc/monthly.py +66 -0
- meteostat-2.0.0/meteostat/providers/eccc/shared.py +45 -0
- meteostat-2.0.0/meteostat/providers/index.py +496 -0
- meteostat-2.0.0/meteostat/providers/meteostat/daily.py +65 -0
- meteostat-2.0.0/meteostat/providers/meteostat/daily_derived.py +110 -0
- meteostat-2.0.0/meteostat/providers/meteostat/hourly.py +66 -0
- meteostat-2.0.0/meteostat/providers/meteostat/monthly.py +45 -0
- meteostat-2.0.0/meteostat/providers/meteostat/monthly_derived.py +106 -0
- meteostat-2.0.0/meteostat/providers/meteostat/shared.py +93 -0
- meteostat-2.0.0/meteostat/providers/metno/forecast.py +186 -0
- meteostat-2.0.0/meteostat/providers/noaa/ghcnd.py +228 -0
- meteostat-2.0.0/meteostat/providers/noaa/isd_lite.py +142 -0
- meteostat-2.0.0/meteostat/providers/noaa/metar.py +163 -0
- meteostat-2.0.0/meteostat/typing.py +113 -0
- meteostat-2.0.0/meteostat/utils/conversions.py +231 -0
- meteostat-2.0.0/meteostat/utils/data.py +194 -0
- meteostat-2.0.0/meteostat/utils/geo.py +28 -0
- meteostat-2.0.0/meteostat/utils/parsers.py +168 -0
- meteostat-2.0.0/meteostat/utils/types.py +113 -0
- meteostat-2.0.0/meteostat/utils/validators.py +31 -0
- meteostat-2.0.0/pyproject.toml +29 -0
- meteostat-1.7.5/PKG-INFO +0 -109
- meteostat-1.7.5/README.md +0 -89
- meteostat-1.7.5/meteostat/__init__.py +0 -35
- meteostat-1.7.5/meteostat/core/cache.py +0 -71
- meteostat-1.7.5/meteostat/core/loader.py +0 -103
- meteostat-1.7.5/meteostat/core/warn.py +0 -34
- meteostat-1.7.5/meteostat/enumerations/granularity.py +0 -22
- meteostat-1.7.5/meteostat/interface/base.py +0 -39
- meteostat-1.7.5/meteostat/interface/daily.py +0 -118
- meteostat-1.7.5/meteostat/interface/hourly.py +0 -154
- meteostat-1.7.5/meteostat/interface/meteodata.py +0 -210
- meteostat-1.7.5/meteostat/interface/monthly.py +0 -109
- meteostat-1.7.5/meteostat/interface/normals.py +0 -245
- meteostat-1.7.5/meteostat/interface/point.py +0 -143
- meteostat-1.7.5/meteostat/interface/stations.py +0 -252
- meteostat-1.7.5/meteostat/interface/timeseries.py +0 -237
- meteostat-1.7.5/meteostat/series/aggregate.py +0 -48
- meteostat-1.7.5/meteostat/series/convert.py +0 -28
- meteostat-1.7.5/meteostat/series/count.py +0 -17
- meteostat-1.7.5/meteostat/series/coverage.py +0 -20
- meteostat-1.7.5/meteostat/series/fetch.py +0 -28
- meteostat-1.7.5/meteostat/series/interpolate.py +0 -47
- meteostat-1.7.5/meteostat/series/normalize.py +0 -76
- meteostat-1.7.5/meteostat/series/stations.py +0 -22
- meteostat-1.7.5/meteostat/units.py +0 -149
- meteostat-1.7.5/meteostat/utilities/__init__.py +0 -0
- meteostat-1.7.5/meteostat/utilities/aggregations.py +0 -37
- meteostat-1.7.5/meteostat/utilities/endpoint.py +0 -33
- meteostat-1.7.5/meteostat/utilities/helpers.py +0 -70
- meteostat-1.7.5/meteostat/utilities/mutations.py +0 -85
- meteostat-1.7.5/meteostat/utilities/validations.py +0 -30
- meteostat-1.7.5/meteostat.egg-info/PKG-INFO +0 -109
- meteostat-1.7.5/meteostat.egg-info/SOURCES.txt +0 -42
- meteostat-1.7.5/meteostat.egg-info/dependency_links.txt +0 -1
- meteostat-1.7.5/meteostat.egg-info/requires.txt +0 -3
- meteostat-1.7.5/meteostat.egg-info/top_level.txt +0 -1
- meteostat-1.7.5/setup.cfg +0 -4
- meteostat-1.7.5/setup.py +0 -40
- {meteostat-1.7.5 → meteostat-2.0.0}/LICENSE +0 -0
- {meteostat-1.7.5/meteostat/core → meteostat-2.0.0/meteostat/api}/__init__.py +0 -0
- {meteostat-1.7.5/meteostat/enumerations → meteostat-2.0.0/meteostat/interpolation}/__init__.py +0 -0
- {meteostat-1.7.5/meteostat/interface → meteostat-2.0.0/meteostat/providers}/__init__.py +0 -0
- /meteostat-1.7.5/meteostat/interface/interpolate.py → /meteostat-2.0.0/meteostat/py.typed +0 -0
- {meteostat-1.7.5/meteostat/series → meteostat-2.0.0/meteostat/utils}/__init__.py +0 -0
meteostat-2.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: meteostat
|
|
3
|
+
Version: 2.0.0
|
|
4
|
+
Summary: Access and analyze historical weather and climate data with Python.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Meteostat
|
|
7
|
+
Requires-Python: >=3.11,<4.0
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: pandas (>=2.3.3,<3.0.0)
|
|
14
|
+
Requires-Dist: pytz (>=2023.3.post1,<2024.0)
|
|
15
|
+
Requires-Dist: requests (>=2.31.0,<3.0.0)
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
<!-- PROJECT SHIELDS -->
|
|
19
|
+
<div align="center">
|
|
20
|
+
|
|
21
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
22
|
+
[![Forks][forks-shield]][forks-url]
|
|
23
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
24
|
+
[![Issues][issues-shield]][issues-url]
|
|
25
|
+
[![Unlicense License][license-shield]][license-url]
|
|
26
|
+
[![LinkedIn][linkedin-shield]][linkedin-url]
|
|
27
|
+
|
|
28
|
+
</div>
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
<!-- PROJECT LOGO -->
|
|
32
|
+
<br />
|
|
33
|
+
<div align="center">
|
|
34
|
+
<a href="https://github.com/meteostat/meteostat">
|
|
35
|
+
<img src="https://media.meteostat.net/icon.svg" alt="Meteostat Logo" width="80" height="80">
|
|
36
|
+
</a>
|
|
37
|
+
|
|
38
|
+
<h3 align="center">Meteostat Python Package</h3>
|
|
39
|
+
|
|
40
|
+
<p align="center">
|
|
41
|
+
Access and analyze historical weather and climate data with Python.
|
|
42
|
+
<p>
|
|
43
|
+
<a href="https://dev.meteostat.net/python"><strong>Explore the docs »</strong></a>
|
|
44
|
+
</p>
|
|
45
|
+
<p>
|
|
46
|
+
<a href="https://meteostat.net">Visit Website</a>
|
|
47
|
+
·
|
|
48
|
+
<a href="https://github.com/meteostat/meteostat/issues">Report Bug</a>
|
|
49
|
+
·
|
|
50
|
+
<a href="https://github.com/orgs/meteostat/discussions">Request Feature</a>
|
|
51
|
+
</p>
|
|
52
|
+
</p>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
## 📚 Installation
|
|
56
|
+
|
|
57
|
+
The Meteostat Python package is available through [PyPI](https://pypi.org/project/meteostat/):
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
pip install meteostat
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 🚀 Usage
|
|
64
|
+
|
|
65
|
+
Let's plot 2018 temperature data for Frankfurt, Germany:
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
from datetime import date
|
|
69
|
+
import matplotlib.pyplot as plt
|
|
70
|
+
import meteostat as ms
|
|
71
|
+
|
|
72
|
+
# Specify location and time range
|
|
73
|
+
POINT = ms.Point(50.1155, 8.6842, 113) # Try with your location
|
|
74
|
+
START = date(2018, 1, 1)
|
|
75
|
+
END = date(2018, 12, 31)
|
|
76
|
+
|
|
77
|
+
# Get nearby weather stations
|
|
78
|
+
stations = ms.stations.nearby(POINT, limit=4)
|
|
79
|
+
|
|
80
|
+
# Get daily data & perform interpolation
|
|
81
|
+
ts = ms.daily(stations, START, END)
|
|
82
|
+
df = ms.interpolate(ts, POINT).fetch()
|
|
83
|
+
|
|
84
|
+
# Plot line chart including average, minimum and maximum temperature
|
|
85
|
+
df.plot(y=[ms.Parameter.TEMP, ms.Parameter.TMIN, ms.Parameter.TMAX])
|
|
86
|
+
plt.show()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Take a look at the expected output:
|
|
90
|
+
|
|
91
|
+
![2018 temperature data for Frankfurt, Germany][product-screenshot]
|
|
92
|
+
|
|
93
|
+
## 🤝 Contributing
|
|
94
|
+
|
|
95
|
+
Please read our [contributing guidelines](https://dev.meteostat.net/python/contributing) for details on how to contribute to the Meteostat Python library.
|
|
96
|
+
|
|
97
|
+
**Top contributors**
|
|
98
|
+
|
|
99
|
+
<a href="https://github.com/meteostat/meteostat/graphs/contributors">
|
|
100
|
+
<img src="https://contrib.rocks/image?repo=meteostat/meteostat" alt="Meteostat Contributors" />
|
|
101
|
+
</a>
|
|
102
|
+
|
|
103
|
+
## 🌟 Featured In
|
|
104
|
+
|
|
105
|
+
Meteostat has been featured and used by various media outlets and organizations, including:
|
|
106
|
+
|
|
107
|
+
- [Towards Data Science](https://towardsdatascience.com/get-temperature-data-by-location-with-python-52ed872dd621/)
|
|
108
|
+
- [ZEIT ONLINE](https://www.zeit.de/digital/internet/2022-03/desinformation-russland-ukraine-fotos-fake-news-falschinformation-echtheit)
|
|
109
|
+
- [Deutsche Presse-Agentur (dpa)](https://dpa-factchecking.com/germany/230103-99-92282/)
|
|
110
|
+
- [heise online](https://www.heise.de/news/Open-Source-Projekt-zu-Klimadaten-Meteostat-Python-Library-1-0-erschienen-4985015.html)
|
|
111
|
+
|
|
112
|
+
Join the growing community of users and researchers relying on Meteostat for their weather data needs.
|
|
113
|
+
|
|
114
|
+
## 📄 License
|
|
115
|
+
|
|
116
|
+
Meteostat is licensed under the [**MIT License**](https://github.com/meteostat/meteostat/blob/main/LICENSE). Data provided by Meteostat is generally licensed under the [**Creative Commons Attribution 4.0 International (CC BY 4.0)**](https://creativecommons.org/licenses/by/4.0) license. Please refer to the [documentation](https://dev.meteostat.net/license) for more information.
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
120
|
+
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
|
121
|
+
[contributors-shield]: https://img.shields.io/github/contributors/meteostat/meteostat.svg?style=for-the-badge
|
|
122
|
+
[contributors-url]: https://github.com/meteostat/meteostat/graphs/contributors
|
|
123
|
+
[forks-shield]: https://img.shields.io/github/forks/meteostat/meteostat.svg?style=for-the-badge
|
|
124
|
+
[forks-url]: https://github.com/meteostat/meteostat/network/members
|
|
125
|
+
[stars-shield]: https://img.shields.io/github/stars/meteostat/meteostat.svg?style=for-the-badge
|
|
126
|
+
[stars-url]: https://github.com/meteostat/meteostat/stargazers
|
|
127
|
+
[issues-shield]: https://img.shields.io/github/issues/meteostat/meteostat.svg?style=for-the-badge
|
|
128
|
+
[issues-url]: https://github.com/meteostat/meteostat/issues
|
|
129
|
+
[license-shield]: https://img.shields.io/github/license/meteostat/meteostat.svg?style=for-the-badge
|
|
130
|
+
[license-url]: https://github.com/meteostat/meteostat/blob/main/LICENSE
|
|
131
|
+
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
|
|
132
|
+
[linkedin-url]: https://www.linkedin.com/company/meteostat
|
|
133
|
+
[product-screenshot]: https://dev.meteostat.net/assets/images/example-8b6cf2a3fe2efa285bc72d7dc72c4865.png
|
|
134
|
+
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<!-- PROJECT SHIELDS -->
|
|
2
|
+
<div align="center">
|
|
3
|
+
|
|
4
|
+
[![Contributors][contributors-shield]][contributors-url]
|
|
5
|
+
[![Forks][forks-shield]][forks-url]
|
|
6
|
+
[![Stargazers][stars-shield]][stars-url]
|
|
7
|
+
[![Issues][issues-shield]][issues-url]
|
|
8
|
+
[![Unlicense License][license-shield]][license-url]
|
|
9
|
+
[![LinkedIn][linkedin-shield]][linkedin-url]
|
|
10
|
+
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
<!-- PROJECT LOGO -->
|
|
15
|
+
<br />
|
|
16
|
+
<div align="center">
|
|
17
|
+
<a href="https://github.com/meteostat/meteostat">
|
|
18
|
+
<img src="https://media.meteostat.net/icon.svg" alt="Meteostat Logo" width="80" height="80">
|
|
19
|
+
</a>
|
|
20
|
+
|
|
21
|
+
<h3 align="center">Meteostat Python Package</h3>
|
|
22
|
+
|
|
23
|
+
<p align="center">
|
|
24
|
+
Access and analyze historical weather and climate data with Python.
|
|
25
|
+
<p>
|
|
26
|
+
<a href="https://dev.meteostat.net/python"><strong>Explore the docs »</strong></a>
|
|
27
|
+
</p>
|
|
28
|
+
<p>
|
|
29
|
+
<a href="https://meteostat.net">Visit Website</a>
|
|
30
|
+
·
|
|
31
|
+
<a href="https://github.com/meteostat/meteostat/issues">Report Bug</a>
|
|
32
|
+
·
|
|
33
|
+
<a href="https://github.com/orgs/meteostat/discussions">Request Feature</a>
|
|
34
|
+
</p>
|
|
35
|
+
</p>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
## 📚 Installation
|
|
39
|
+
|
|
40
|
+
The Meteostat Python package is available through [PyPI](https://pypi.org/project/meteostat/):
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
pip install meteostat
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## 🚀 Usage
|
|
47
|
+
|
|
48
|
+
Let's plot 2018 temperature data for Frankfurt, Germany:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from datetime import date
|
|
52
|
+
import matplotlib.pyplot as plt
|
|
53
|
+
import meteostat as ms
|
|
54
|
+
|
|
55
|
+
# Specify location and time range
|
|
56
|
+
POINT = ms.Point(50.1155, 8.6842, 113) # Try with your location
|
|
57
|
+
START = date(2018, 1, 1)
|
|
58
|
+
END = date(2018, 12, 31)
|
|
59
|
+
|
|
60
|
+
# Get nearby weather stations
|
|
61
|
+
stations = ms.stations.nearby(POINT, limit=4)
|
|
62
|
+
|
|
63
|
+
# Get daily data & perform interpolation
|
|
64
|
+
ts = ms.daily(stations, START, END)
|
|
65
|
+
df = ms.interpolate(ts, POINT).fetch()
|
|
66
|
+
|
|
67
|
+
# Plot line chart including average, minimum and maximum temperature
|
|
68
|
+
df.plot(y=[ms.Parameter.TEMP, ms.Parameter.TMIN, ms.Parameter.TMAX])
|
|
69
|
+
plt.show()
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Take a look at the expected output:
|
|
73
|
+
|
|
74
|
+
![2018 temperature data for Frankfurt, Germany][product-screenshot]
|
|
75
|
+
|
|
76
|
+
## 🤝 Contributing
|
|
77
|
+
|
|
78
|
+
Please read our [contributing guidelines](https://dev.meteostat.net/python/contributing) for details on how to contribute to the Meteostat Python library.
|
|
79
|
+
|
|
80
|
+
**Top contributors**
|
|
81
|
+
|
|
82
|
+
<a href="https://github.com/meteostat/meteostat/graphs/contributors">
|
|
83
|
+
<img src="https://contrib.rocks/image?repo=meteostat/meteostat" alt="Meteostat Contributors" />
|
|
84
|
+
</a>
|
|
85
|
+
|
|
86
|
+
## 🌟 Featured In
|
|
87
|
+
|
|
88
|
+
Meteostat has been featured and used by various media outlets and organizations, including:
|
|
89
|
+
|
|
90
|
+
- [Towards Data Science](https://towardsdatascience.com/get-temperature-data-by-location-with-python-52ed872dd621/)
|
|
91
|
+
- [ZEIT ONLINE](https://www.zeit.de/digital/internet/2022-03/desinformation-russland-ukraine-fotos-fake-news-falschinformation-echtheit)
|
|
92
|
+
- [Deutsche Presse-Agentur (dpa)](https://dpa-factchecking.com/germany/230103-99-92282/)
|
|
93
|
+
- [heise online](https://www.heise.de/news/Open-Source-Projekt-zu-Klimadaten-Meteostat-Python-Library-1-0-erschienen-4985015.html)
|
|
94
|
+
|
|
95
|
+
Join the growing community of users and researchers relying on Meteostat for their weather data needs.
|
|
96
|
+
|
|
97
|
+
## 📄 License
|
|
98
|
+
|
|
99
|
+
Meteostat is licensed under the [**MIT License**](https://github.com/meteostat/meteostat/blob/main/LICENSE). Data provided by Meteostat is generally licensed under the [**Creative Commons Attribution 4.0 International (CC BY 4.0)**](https://creativecommons.org/licenses/by/4.0) license. Please refer to the [documentation](https://dev.meteostat.net/license) for more information.
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
<!-- MARKDOWN LINKS & IMAGES -->
|
|
103
|
+
<!-- https://www.markdownguide.org/basic-syntax/#reference-style-links -->
|
|
104
|
+
[contributors-shield]: https://img.shields.io/github/contributors/meteostat/meteostat.svg?style=for-the-badge
|
|
105
|
+
[contributors-url]: https://github.com/meteostat/meteostat/graphs/contributors
|
|
106
|
+
[forks-shield]: https://img.shields.io/github/forks/meteostat/meteostat.svg?style=for-the-badge
|
|
107
|
+
[forks-url]: https://github.com/meteostat/meteostat/network/members
|
|
108
|
+
[stars-shield]: https://img.shields.io/github/stars/meteostat/meteostat.svg?style=for-the-badge
|
|
109
|
+
[stars-url]: https://github.com/meteostat/meteostat/stargazers
|
|
110
|
+
[issues-shield]: https://img.shields.io/github/issues/meteostat/meteostat.svg?style=for-the-badge
|
|
111
|
+
[issues-url]: https://github.com/meteostat/meteostat/issues
|
|
112
|
+
[license-shield]: https://img.shields.io/github/license/meteostat/meteostat.svg?style=for-the-badge
|
|
113
|
+
[license-url]: https://github.com/meteostat/meteostat/blob/main/LICENSE
|
|
114
|
+
[linkedin-shield]: https://img.shields.io/badge/-LinkedIn-black.svg?style=for-the-badge&logo=linkedin&colorB=555
|
|
115
|
+
[linkedin-url]: https://www.linkedin.com/company/meteostat
|
|
116
|
+
[product-screenshot]: https://dev.meteostat.net/assets/images/example-8b6cf2a3fe2efa285bc72d7dc72c4865.png
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""
|
|
2
|
+
█▀▄▀█ █▀▀ ▀█▀ █▀▀ █▀█ █▀ ▀█▀ ▄▀█ ▀█▀
|
|
3
|
+
█░▀░█ ██▄ ░█░ ██▄ █▄█ ▄█ ░█░ █▀█ ░█░
|
|
4
|
+
|
|
5
|
+
A Python library for accessing open weather and climate data.
|
|
6
|
+
|
|
7
|
+
Meteorological data provided by Meteostat (https://dev.meteostat.net)
|
|
8
|
+
under the terms of the Creative Commons Attribution 4.0 International
|
|
9
|
+
License.
|
|
10
|
+
|
|
11
|
+
The code is licensed under the MIT license.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
__appname__ = "meteostat"
|
|
15
|
+
__version__ = "2.0.0"
|
|
16
|
+
|
|
17
|
+
from meteostat.api.daily import daily
|
|
18
|
+
from meteostat.api.hourly import hourly
|
|
19
|
+
from meteostat.api.interpolate import interpolate
|
|
20
|
+
from meteostat.api.merge import merge
|
|
21
|
+
from meteostat.api.monthly import monthly
|
|
22
|
+
from meteostat.api.normals import normals
|
|
23
|
+
from meteostat.api.point import Point
|
|
24
|
+
from meteostat.api.stations import stations
|
|
25
|
+
from meteostat.core.cache import purge
|
|
26
|
+
from meteostat.core.config import config
|
|
27
|
+
from meteostat.enumerations import Parameter, Provider, UnitSystem
|
|
28
|
+
from meteostat.interpolation.lapserate import lapse_rate
|
|
29
|
+
from meteostat.typing import Station
|
|
30
|
+
|
|
31
|
+
# Export public API
|
|
32
|
+
__all__ = [
|
|
33
|
+
"config",
|
|
34
|
+
"daily",
|
|
35
|
+
"hourly",
|
|
36
|
+
"interpolate",
|
|
37
|
+
"lapse_rate",
|
|
38
|
+
"merge",
|
|
39
|
+
"monthly",
|
|
40
|
+
"normals",
|
|
41
|
+
"Parameter",
|
|
42
|
+
"Point",
|
|
43
|
+
"Provider",
|
|
44
|
+
"purge",
|
|
45
|
+
"Station",
|
|
46
|
+
"stations",
|
|
47
|
+
"UnitSystem",
|
|
48
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Daily Time Series Data
|
|
3
|
+
|
|
4
|
+
Access daily time series data for one or multiple weather stations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
from datetime import datetime, date
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from meteostat.core.data import data_service
|
|
13
|
+
from meteostat.enumerations import Parameter, Provider, Granularity
|
|
14
|
+
from meteostat.typing import Station, Request
|
|
15
|
+
from meteostat.api.point import Point
|
|
16
|
+
from meteostat.utils.parsers import parse_station, parse_time
|
|
17
|
+
|
|
18
|
+
DEFAULT_PARAMETERS = [
|
|
19
|
+
Parameter.TEMP,
|
|
20
|
+
Parameter.TMIN,
|
|
21
|
+
Parameter.TMAX,
|
|
22
|
+
Parameter.RHUM,
|
|
23
|
+
Parameter.PRCP,
|
|
24
|
+
Parameter.SNWD,
|
|
25
|
+
Parameter.WSPD,
|
|
26
|
+
Parameter.WPGT,
|
|
27
|
+
Parameter.PRES,
|
|
28
|
+
Parameter.TSUN,
|
|
29
|
+
Parameter.CLDC,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def daily(
|
|
34
|
+
station: str | Station | Point | List[str | Station | Point] | pd.DataFrame,
|
|
35
|
+
start: Optional[datetime | date],
|
|
36
|
+
end: Optional[datetime | date],
|
|
37
|
+
parameters: Optional[List[Parameter]] = None,
|
|
38
|
+
providers: Optional[List[Provider]] = None,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Access daily time series data.
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
station : str, Station, Point, List[str | Station | Point], pd.Index, pd.Series
|
|
46
|
+
Weather station(s) or Point(s) to query data for. Can be a single station/point or a list.
|
|
47
|
+
Points are converted to virtual stations with IDs like $0001, $0002, etc.
|
|
48
|
+
start : datetime, date, optional
|
|
49
|
+
Start date for the data query. If None, the earliest available date will be used.
|
|
50
|
+
end : datetime, date, optional
|
|
51
|
+
End date for the data query. If None, the latest available date will be used.
|
|
52
|
+
parameters : List[Parameter], optional
|
|
53
|
+
List of parameters to include in the data query. Defaults to a set of common parameters.
|
|
54
|
+
providers : List[Provider], optional
|
|
55
|
+
List of data providers to use for the query. Defaults to the daily provider.
|
|
56
|
+
|
|
57
|
+
Returns
|
|
58
|
+
-------
|
|
59
|
+
TimeSeries
|
|
60
|
+
A TimeSeries object containing the daily data for the specified stations and parameters.
|
|
61
|
+
"""
|
|
62
|
+
if parameters is None:
|
|
63
|
+
parameters = DEFAULT_PARAMETERS
|
|
64
|
+
if providers is None:
|
|
65
|
+
providers = [Provider.DAILY]
|
|
66
|
+
|
|
67
|
+
req = Request(
|
|
68
|
+
granularity=Granularity.DAILY,
|
|
69
|
+
providers=providers,
|
|
70
|
+
parameters=parameters,
|
|
71
|
+
station=parse_station(station),
|
|
72
|
+
start=parse_time(start),
|
|
73
|
+
end=parse_time(end, is_end=True),
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
return data_service.fetch(req)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Hourly Time Series Data
|
|
3
|
+
|
|
4
|
+
Access hourly time series data for one or multiple weather stations.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import List, Optional
|
|
8
|
+
from datetime import datetime, date
|
|
9
|
+
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from meteostat.core.data import data_service
|
|
13
|
+
from meteostat.enumerations import Parameter, Provider, Granularity
|
|
14
|
+
from meteostat.typing import Station, Request
|
|
15
|
+
from meteostat.api.point import Point
|
|
16
|
+
from meteostat.utils.parsers import parse_station, parse_time
|
|
17
|
+
|
|
18
|
+
DEFAULT_PARAMETERS = [
|
|
19
|
+
Parameter.TEMP,
|
|
20
|
+
Parameter.RHUM,
|
|
21
|
+
Parameter.PRCP,
|
|
22
|
+
Parameter.SNWD,
|
|
23
|
+
Parameter.WDIR,
|
|
24
|
+
Parameter.WSPD,
|
|
25
|
+
Parameter.WPGT,
|
|
26
|
+
Parameter.PRES,
|
|
27
|
+
Parameter.TSUN,
|
|
28
|
+
Parameter.CLDC,
|
|
29
|
+
Parameter.COCO,
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def hourly(
|
|
34
|
+
station: str | Station | Point | List[str | Station | Point] | pd.DataFrame,
|
|
35
|
+
start: Optional[datetime | date],
|
|
36
|
+
end: Optional[datetime | date],
|
|
37
|
+
timezone: Optional[str] = None,
|
|
38
|
+
parameters: Optional[List[Parameter]] = None,
|
|
39
|
+
providers: Optional[List[Provider]] = None,
|
|
40
|
+
):
|
|
41
|
+
"""
|
|
42
|
+
Access hourly time series data.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
station : str, Station, Point, List[str | Station | Point], pd.Index, pd.Series
|
|
47
|
+
Weather station(s) or Point(s) to query data for. Can be a single station/point or a list.
|
|
48
|
+
Points are converted to virtual stations with IDs like $0001, $0002, etc.
|
|
49
|
+
start : datetime, date, optional
|
|
50
|
+
Start date for the data query. If None, the earliest available date will be used.
|
|
51
|
+
end : datetime, date, optional
|
|
52
|
+
End date for the data query. If None, the latest available date will be used.
|
|
53
|
+
timezone : str, optional
|
|
54
|
+
Time zone for the data query. If None, UTC will be used.
|
|
55
|
+
parameters : List[Parameter], optional
|
|
56
|
+
List of parameters to include in the data query. Defaults to a set of common parameters.
|
|
57
|
+
providers : List[Provider], optional
|
|
58
|
+
List of data providers to use for the query. Defaults to the hourly provider.
|
|
59
|
+
|
|
60
|
+
Returns
|
|
61
|
+
-------
|
|
62
|
+
TimeSeries
|
|
63
|
+
A TimeSeries object containing the hourly data for the specified stations and parameters.
|
|
64
|
+
"""
|
|
65
|
+
if parameters is None:
|
|
66
|
+
parameters = DEFAULT_PARAMETERS
|
|
67
|
+
if providers is None:
|
|
68
|
+
providers = [Provider.HOURLY]
|
|
69
|
+
|
|
70
|
+
req = Request(
|
|
71
|
+
granularity=Granularity.HOURLY,
|
|
72
|
+
providers=providers,
|
|
73
|
+
parameters=parameters,
|
|
74
|
+
station=parse_station(station),
|
|
75
|
+
start=parse_time(start, timezone),
|
|
76
|
+
end=parse_time(end, timezone, is_end=True),
|
|
77
|
+
timezone=timezone,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
return data_service.fetch(req)
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interpolation Module
|
|
3
|
+
|
|
4
|
+
Provides spatial interpolation functions for meteorological data.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Optional, Union
|
|
8
|
+
|
|
9
|
+
import numpy as np
|
|
10
|
+
import pandas as pd
|
|
11
|
+
|
|
12
|
+
from meteostat.api.point import Point
|
|
13
|
+
from meteostat.api.timeseries import TimeSeries
|
|
14
|
+
from meteostat.typing import Station
|
|
15
|
+
from meteostat.interpolation.lapserate import apply_lapse_rate
|
|
16
|
+
from meteostat.interpolation.nearest import nearest_neighbor
|
|
17
|
+
from meteostat.interpolation.idw import inverse_distance_weighting
|
|
18
|
+
from meteostat.utils.data import aggregate_sources, reshape_by_source, stations_to_df
|
|
19
|
+
from meteostat.utils.geo import get_distance
|
|
20
|
+
from meteostat.utils.parsers import parse_station
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _create_timeseries(
|
|
24
|
+
ts: TimeSeries, point: Point, df: Optional[pd.DataFrame] = None
|
|
25
|
+
) -> TimeSeries:
|
|
26
|
+
"""
|
|
27
|
+
Create a TimeSeries object from interpolated DataFrame
|
|
28
|
+
"""
|
|
29
|
+
parsed = parse_station(point)
|
|
30
|
+
stations_list = [parsed] if isinstance(parsed, Station) else parsed
|
|
31
|
+
|
|
32
|
+
# Convert stations to DataFrame
|
|
33
|
+
stations_df = stations_to_df(stations_list)
|
|
34
|
+
|
|
35
|
+
return TimeSeries(
|
|
36
|
+
ts.granularity,
|
|
37
|
+
stations_df,
|
|
38
|
+
df=df,
|
|
39
|
+
start=ts.start,
|
|
40
|
+
end=ts.end,
|
|
41
|
+
timezone=ts.timezone,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _add_source_columns(
|
|
46
|
+
result: pd.DataFrame,
|
|
47
|
+
df: pd.DataFrame,
|
|
48
|
+
) -> pd.DataFrame:
|
|
49
|
+
"""
|
|
50
|
+
Add source columns to the result DataFrame
|
|
51
|
+
"""
|
|
52
|
+
source_cols = [c for c in df.columns if c.endswith("_source")]
|
|
53
|
+
if source_cols:
|
|
54
|
+
grouped = df.groupby("time")[source_cols].agg(aggregate_sources)
|
|
55
|
+
if isinstance(grouped, pd.Series):
|
|
56
|
+
grouped = grouped.to_frame(name=source_cols[0])
|
|
57
|
+
grouped.index.name = "time"
|
|
58
|
+
|
|
59
|
+
# Safely align on time and add/fill source columns without causing overlaps
|
|
60
|
+
result_has_time_col = "time" in result.columns
|
|
61
|
+
if result_has_time_col:
|
|
62
|
+
result = result.set_index("time")
|
|
63
|
+
|
|
64
|
+
# Ensure both frames align on the same index (time)
|
|
65
|
+
# For each source column, add it if missing or fill NaNs if present
|
|
66
|
+
for col in source_cols:
|
|
67
|
+
if col in grouped.columns:
|
|
68
|
+
if col in result.columns:
|
|
69
|
+
# Fill missing values in result using aggregated sources
|
|
70
|
+
result[col] = result[col].where(result[col].notna(), grouped[col])
|
|
71
|
+
else:
|
|
72
|
+
# Add aggregated source column
|
|
73
|
+
result[col] = grouped[col]
|
|
74
|
+
|
|
75
|
+
if result_has_time_col:
|
|
76
|
+
result = result.reset_index()
|
|
77
|
+
return result
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def interpolate(
|
|
81
|
+
ts: TimeSeries,
|
|
82
|
+
point: Point,
|
|
83
|
+
distance_threshold: Union[int, None] = 5000,
|
|
84
|
+
elevation_threshold: Union[int, None] = 50,
|
|
85
|
+
elevation_weight: float = 10,
|
|
86
|
+
power: float = 2.0,
|
|
87
|
+
lapse_rate: Union[float, None] = 6.5,
|
|
88
|
+
lapse_rate_threshold: int = 50,
|
|
89
|
+
) -> TimeSeries:
|
|
90
|
+
"""
|
|
91
|
+
Interpolate time series data spatially to a specific point.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
ts : TimeSeries
|
|
96
|
+
The time series to interpolate.
|
|
97
|
+
point : Point
|
|
98
|
+
The point to interpolate the data for.
|
|
99
|
+
distance_threshold : int, optional
|
|
100
|
+
Maximum distance (in meters) to use nearest neighbor (default: 5000).
|
|
101
|
+
Beyond this, IDW is used.
|
|
102
|
+
elevation_threshold : int, optional
|
|
103
|
+
Maximum elevation difference (in meters) to use nearest neighbor (default: 50).
|
|
104
|
+
Beyond this, IDW is used even if distance is within threshold.
|
|
105
|
+
elevation_weight : float, optional
|
|
106
|
+
Weight for elevation difference in distance calculation (default: 0.1).
|
|
107
|
+
The effective distance is calculated as:
|
|
108
|
+
sqrt(horizontal_distance^2 + (elevation_diff * elevation_weight)^2)
|
|
109
|
+
power : float, optional
|
|
110
|
+
Power parameter for IDW (default: 2.0). Higher values give more
|
|
111
|
+
weight to closer stations.
|
|
112
|
+
lapse_rate : float, optional
|
|
113
|
+
Apply lapse rate correction based on elevation difference (default: 6.5).
|
|
114
|
+
lapse_rate_threshold : int, optional
|
|
115
|
+
Elevation difference threshold (in meters) to apply lapse rate correction
|
|
116
|
+
(default: 50). If the elevation difference between the point and stations
|
|
117
|
+
is less than this, no correction is applied.
|
|
118
|
+
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
pd.DataFrame or None
|
|
122
|
+
A DataFrame containing the interpolated data for the specified point,
|
|
123
|
+
or None if no data is available.
|
|
124
|
+
"""
|
|
125
|
+
# Fetch DataFrame, filling missing values and adding location data
|
|
126
|
+
df = ts.fetch(fill=True, location=True, sources=True)
|
|
127
|
+
|
|
128
|
+
# If no data is returned, return None
|
|
129
|
+
if df is None:
|
|
130
|
+
return _create_timeseries(ts, point)
|
|
131
|
+
|
|
132
|
+
# Add distance column
|
|
133
|
+
df["distance"] = get_distance(
|
|
134
|
+
point.latitude, point.longitude, df["latitude"], df["longitude"]
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
# Add effective distance column if elevation is available
|
|
138
|
+
if point.elevation is not None and "elevation" in df.columns:
|
|
139
|
+
elev_diff = np.abs(df["elevation"] - point.elevation)
|
|
140
|
+
df["effective_distance"] = np.sqrt(
|
|
141
|
+
df["distance"] ** 2 + (elev_diff * elevation_weight) ** 2
|
|
142
|
+
)
|
|
143
|
+
else:
|
|
144
|
+
df["effective_distance"] = df["distance"]
|
|
145
|
+
|
|
146
|
+
# Add elevation difference column
|
|
147
|
+
if "elevation" in df.columns and point.elevation is not None:
|
|
148
|
+
df["elevation_diff"] = np.abs(df["elevation"] - point.elevation)
|
|
149
|
+
else:
|
|
150
|
+
df["elevation_diff"] = np.nan
|
|
151
|
+
|
|
152
|
+
# Apply lapse rate if specified and elevation is available
|
|
153
|
+
if (
|
|
154
|
+
lapse_rate
|
|
155
|
+
and point.elevation
|
|
156
|
+
and df["elevation_diff"].max() >= lapse_rate_threshold
|
|
157
|
+
):
|
|
158
|
+
df = apply_lapse_rate(df, point.elevation, lapse_rate)
|
|
159
|
+
|
|
160
|
+
# Check if any stations are close enough for nearest neighbor
|
|
161
|
+
min_distance = df["distance"].min()
|
|
162
|
+
use_nearest = distance_threshold is None or min_distance <= distance_threshold
|
|
163
|
+
if use_nearest and point.elevation is not None and "elevation" in df.columns:
|
|
164
|
+
# Calculate minimum elevation difference
|
|
165
|
+
min_elev_diff = np.abs(df["elevation"] - point.elevation).min()
|
|
166
|
+
use_nearest = (
|
|
167
|
+
elevation_threshold is None or min_elev_diff <= elevation_threshold
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# Initialize variables
|
|
171
|
+
df_nearest = None
|
|
172
|
+
df_idw = None
|
|
173
|
+
|
|
174
|
+
# Perform nearest neighbor if applicable
|
|
175
|
+
if use_nearest:
|
|
176
|
+
# Filter applicable stations based on thresholds
|
|
177
|
+
distance_filter = (
|
|
178
|
+
pd.Series([True] * len(df), index=df.index)
|
|
179
|
+
if distance_threshold is None
|
|
180
|
+
else (df["distance"] <= distance_threshold)
|
|
181
|
+
)
|
|
182
|
+
elevation_filter = (
|
|
183
|
+
pd.Series([True] * len(df), index=df.index)
|
|
184
|
+
if elevation_threshold is None
|
|
185
|
+
else (np.abs(df["elevation"] - point.elevation) <= elevation_threshold)
|
|
186
|
+
)
|
|
187
|
+
df_filtered = df[distance_filter & elevation_filter]
|
|
188
|
+
df_nearest = nearest_neighbor(df_filtered, ts, point)
|
|
189
|
+
|
|
190
|
+
# Check if we need to use IDW
|
|
191
|
+
if (
|
|
192
|
+
not use_nearest
|
|
193
|
+
or df_nearest is None
|
|
194
|
+
or len(df_nearest) == 0
|
|
195
|
+
or df_nearest.isna().any().any()
|
|
196
|
+
):
|
|
197
|
+
# Perform IDW interpolation
|
|
198
|
+
idw_func = inverse_distance_weighting(power=power)
|
|
199
|
+
df_idw = idw_func(df, ts, point)
|
|
200
|
+
|
|
201
|
+
# Merge DataFrames with priority to nearest neighbor
|
|
202
|
+
if use_nearest and df_nearest is not None and len(df_nearest) > 0:
|
|
203
|
+
if df_idw is not None:
|
|
204
|
+
# Combine nearest and IDW results, prioritizing nearest values
|
|
205
|
+
result = df_nearest.combine_first(df_idw)
|
|
206
|
+
else:
|
|
207
|
+
result = df_nearest
|
|
208
|
+
else:
|
|
209
|
+
result = df_idw
|
|
210
|
+
|
|
211
|
+
# If no data is returned, return None
|
|
212
|
+
if result is None or result.empty:
|
|
213
|
+
return _create_timeseries(ts, point)
|
|
214
|
+
|
|
215
|
+
# Drop location-related columns & return
|
|
216
|
+
result = result.drop(
|
|
217
|
+
[
|
|
218
|
+
"latitude",
|
|
219
|
+
"longitude",
|
|
220
|
+
"elevation",
|
|
221
|
+
"distance",
|
|
222
|
+
"effective_distance",
|
|
223
|
+
"elevation_diff",
|
|
224
|
+
],
|
|
225
|
+
axis=1,
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# Add source columns: aggregate all columns that end with "_source"
|
|
229
|
+
result = _add_source_columns(result, df)
|
|
230
|
+
|
|
231
|
+
# Reshape by source
|
|
232
|
+
result = reshape_by_source(result)
|
|
233
|
+
|
|
234
|
+
# Add station index
|
|
235
|
+
result["station"] = "$0001"
|
|
236
|
+
result = result.set_index("station", append=True).reorder_levels(
|
|
237
|
+
["station", "time", "source"]
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
return _create_timeseries(ts, point, result)
|