meteostat 1.7.6__py3-none-any.whl → 2.0.1__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.
Files changed (94) hide show
  1. meteostat/__init__.py +38 -19
  2. meteostat/api/config.py +158 -0
  3. meteostat/api/daily.py +76 -0
  4. meteostat/api/hourly.py +80 -0
  5. meteostat/api/interpolate.py +378 -0
  6. meteostat/api/inventory.py +59 -0
  7. meteostat/api/merge.py +103 -0
  8. meteostat/api/monthly.py +73 -0
  9. meteostat/api/normals.py +144 -0
  10. meteostat/api/point.py +30 -0
  11. meteostat/api/stations.py +234 -0
  12. meteostat/api/timeseries.py +334 -0
  13. meteostat/core/cache.py +212 -59
  14. meteostat/core/data.py +203 -0
  15. meteostat/core/logger.py +9 -0
  16. meteostat/core/network.py +82 -0
  17. meteostat/core/parameters.py +112 -0
  18. meteostat/core/providers.py +184 -0
  19. meteostat/core/schema.py +170 -0
  20. meteostat/core/validator.py +38 -0
  21. meteostat/enumerations.py +149 -0
  22. meteostat/interpolation/idw.py +120 -0
  23. meteostat/interpolation/lapserate.py +91 -0
  24. meteostat/interpolation/nearest.py +31 -0
  25. meteostat/parameters.py +354 -0
  26. meteostat/providers/dwd/climat.py +166 -0
  27. meteostat/providers/dwd/daily.py +144 -0
  28. meteostat/providers/dwd/hourly.py +218 -0
  29. meteostat/providers/dwd/monthly.py +138 -0
  30. meteostat/providers/dwd/mosmix.py +351 -0
  31. meteostat/providers/dwd/poi.py +117 -0
  32. meteostat/providers/dwd/shared.py +155 -0
  33. meteostat/providers/eccc/daily.py +87 -0
  34. meteostat/providers/eccc/hourly.py +104 -0
  35. meteostat/providers/eccc/monthly.py +66 -0
  36. meteostat/providers/eccc/shared.py +45 -0
  37. meteostat/providers/index.py +496 -0
  38. meteostat/providers/meteostat/daily.py +65 -0
  39. meteostat/providers/meteostat/daily_derived.py +110 -0
  40. meteostat/providers/meteostat/hourly.py +66 -0
  41. meteostat/providers/meteostat/monthly.py +45 -0
  42. meteostat/providers/meteostat/monthly_derived.py +106 -0
  43. meteostat/providers/meteostat/shared.py +93 -0
  44. meteostat/providers/metno/forecast.py +186 -0
  45. meteostat/providers/noaa/ghcnd.py +228 -0
  46. meteostat/providers/noaa/isd_lite.py +142 -0
  47. meteostat/providers/noaa/metar.py +163 -0
  48. meteostat/typing.py +113 -0
  49. meteostat/utils/conversions.py +231 -0
  50. meteostat/utils/data.py +194 -0
  51. meteostat/utils/geo.py +28 -0
  52. meteostat/utils/guards.py +51 -0
  53. meteostat/utils/parsers.py +161 -0
  54. meteostat/utils/types.py +113 -0
  55. meteostat/utils/validators.py +31 -0
  56. meteostat-2.0.1.dist-info/METADATA +130 -0
  57. meteostat-2.0.1.dist-info/RECORD +64 -0
  58. {meteostat-1.7.6.dist-info → meteostat-2.0.1.dist-info}/WHEEL +1 -2
  59. meteostat/core/loader.py +0 -103
  60. meteostat/core/warn.py +0 -34
  61. meteostat/enumerations/granularity.py +0 -22
  62. meteostat/interface/base.py +0 -39
  63. meteostat/interface/daily.py +0 -118
  64. meteostat/interface/hourly.py +0 -154
  65. meteostat/interface/meteodata.py +0 -210
  66. meteostat/interface/monthly.py +0 -109
  67. meteostat/interface/normals.py +0 -245
  68. meteostat/interface/point.py +0 -143
  69. meteostat/interface/stations.py +0 -252
  70. meteostat/interface/timeseries.py +0 -237
  71. meteostat/series/aggregate.py +0 -48
  72. meteostat/series/convert.py +0 -28
  73. meteostat/series/count.py +0 -17
  74. meteostat/series/coverage.py +0 -20
  75. meteostat/series/fetch.py +0 -28
  76. meteostat/series/interpolate.py +0 -47
  77. meteostat/series/normalize.py +0 -76
  78. meteostat/series/stations.py +0 -22
  79. meteostat/units.py +0 -149
  80. meteostat/utilities/__init__.py +0 -0
  81. meteostat/utilities/aggregations.py +0 -37
  82. meteostat/utilities/endpoint.py +0 -33
  83. meteostat/utilities/helpers.py +0 -70
  84. meteostat/utilities/mutations.py +0 -89
  85. meteostat/utilities/validations.py +0 -30
  86. meteostat-1.7.6.dist-info/METADATA +0 -112
  87. meteostat-1.7.6.dist-info/RECORD +0 -39
  88. meteostat-1.7.6.dist-info/top_level.txt +0 -1
  89. /meteostat/{core → api}/__init__.py +0 -0
  90. /meteostat/{enumerations → interpolation}/__init__.py +0 -0
  91. /meteostat/{interface → providers}/__init__.py +0 -0
  92. /meteostat/{interface/interpolate.py → py.typed} +0 -0
  93. /meteostat/{series → utils}/__init__.py +0 -0
  94. {meteostat-1.7.6.dist-info → meteostat-2.0.1.dist-info/licenses}/LICENSE +0 -0
@@ -1,237 +0,0 @@
1
- """
2
- TimeSeries Class
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from datetime import datetime
12
- from typing import Optional, Union
13
- import pandas as pd
14
- from meteostat.core.cache import file_in_cache, get_local_file_path
15
- from meteostat.core.loader import load_handler
16
- from meteostat.enumerations.granularity import Granularity
17
- from meteostat.utilities.endpoint import generate_endpoint_path
18
- from meteostat.utilities.mutations import filter_time, localize
19
- from meteostat.utilities.validations import validate_series
20
- from meteostat.utilities.helpers import get_flag_from_source_factory, with_suffix
21
- from meteostat.interface.point import Point
22
- from meteostat.interface.meteodata import MeteoData
23
-
24
-
25
- class TimeSeries(MeteoData):
26
- """
27
- TimeSeries class which provides features which are
28
- used across all time series classes
29
- """
30
-
31
- # Base URL of the Meteostat bulk data interface
32
- endpoint = "https://data.meteostat.net/"
33
-
34
- # The list of origin weather Stations
35
- _origin_stations: Optional[pd.Index] = None
36
-
37
- # The start date
38
- _start: Optional[datetime] = None
39
-
40
- # The end date
41
- _end: Optional[datetime] = None
42
-
43
- # Include model data?
44
- _model = True
45
-
46
- # Fetch source flags?
47
- _flags = False
48
-
49
- def _load_data(self, station: str, year: Optional[int] = None) -> None:
50
- """
51
- Load file for a single station from Meteostat
52
- """
53
- # File name
54
- file = generate_endpoint_path(self.granularity, station, year)
55
-
56
- # Get local file path
57
- path = get_local_file_path(self.cache_dir, self.cache_subdir, file)
58
-
59
- # Check if file in cache
60
- if self.max_age > 0 and file_in_cache(path, self.max_age):
61
- # Read cached data
62
- df = pd.read_pickle(path)
63
-
64
- else:
65
- # Get data from Meteostat
66
- df = load_handler(
67
- self.endpoint,
68
- file,
69
- self.proxy,
70
- default_df=pd.DataFrame(
71
- columns=self._raw_columns
72
- + with_suffix(self._raw_columns, "_source")
73
- ),
74
- )
75
-
76
- # Add time column and drop original columns
77
- if len(self._parse_dates) < 3:
78
- df["day"] = 1
79
-
80
- df["time"] = pd.to_datetime(
81
- df[
82
- (
83
- self._parse_dates
84
- if len(self._parse_dates) > 2
85
- else self._parse_dates + ["day"]
86
- )
87
- ]
88
- )
89
- df = df.drop(self._parse_dates, axis=1)
90
-
91
- # Validate and prepare data for further processing
92
- df = validate_series(df, station)
93
-
94
- # Rename columns
95
- df = df.rename(columns=self._renamed_columns, errors="ignore")
96
-
97
- # Convert sources to flags
98
- for col in df.columns:
99
- basecol = col[:-7] if col.endswith("_source") else col
100
-
101
- if basecol not in self._processed_columns:
102
- df.drop(col, axis=1, inplace=True)
103
- continue
104
-
105
- if basecol == col:
106
- df[col] = df[col].astype("Float64")
107
-
108
- if col.endswith("_source"):
109
- flagcol = f"{basecol}_flag"
110
- df[flagcol] = pd.NA
111
- df[flagcol] = df[flagcol].astype("string")
112
- mask = df[col].notna()
113
- df.loc[mask, flagcol] = df.loc[mask, col].apply(
114
- get_flag_from_source_factory(
115
- self._source_mappings, self._model_flag
116
- )
117
- )
118
- df.drop(col, axis=1, inplace=True)
119
-
120
- # Process virtual columns
121
- for key, value in self._virtual_columns.items():
122
- df = value(df, key)
123
-
124
- # Save as Pickle
125
- if self.max_age > 0:
126
- df.to_pickle(path)
127
-
128
- # Localize time column
129
- if (
130
- self.granularity == Granularity.HOURLY
131
- and self._timezone is not None
132
- and len(df.index) > 0
133
- ):
134
- df = localize(df, self._timezone)
135
-
136
- # Filter time period and append to DataFrame
137
- df = filter_time(df, self._start, self._end)
138
-
139
- # Return
140
- return df
141
-
142
- def _filter_model(self) -> None:
143
- """
144
- Remove model data from time series
145
- """
146
-
147
- for col_name in self._processed_columns:
148
- self._data.loc[
149
- (pd.isna(self._data[f"{col_name}_flag"]))
150
- | (self._data[f"{col_name}_flag"].str.contains(self._model_flag)),
151
- col_name,
152
- ] = pd.NA
153
-
154
- # Drop nan-only rows
155
- self._data.dropna(how="all", subset=self._processed_columns, inplace=True)
156
-
157
- def _init_time_series(
158
- self,
159
- loc: Union[pd.DataFrame, Point, list, str], # Station(s) or geo point
160
- start: datetime = None,
161
- end: datetime = None,
162
- model=True, # Include model data?
163
- flags=False, # Load source flags?
164
- ) -> None:
165
- """
166
- Common initialization for all time series, regardless
167
- of its granularity
168
- """
169
-
170
- # Set list of weather stations based on user
171
- # input or retrieve list of stations programatically
172
- # if location is a geographical point
173
- if isinstance(loc, pd.DataFrame):
174
- self._stations = loc.index
175
- elif isinstance(loc, Point):
176
- stations = loc.get_stations("daily", start, end, model)
177
- self._stations = stations.index
178
- else:
179
- if not isinstance(loc, list):
180
- loc = [loc]
181
- self._stations = pd.Index(loc)
182
-
183
- # Preserve settings
184
- self._start = start if self._start is None else self._start
185
- self._end = end if self._end is None else self._end
186
- self._model = model
187
- self._flags = flags
188
-
189
- # Get data for all weather stations
190
- self._data = self._get_data()
191
-
192
- # Fill columns if they don't exist
193
- for col in self._processed_columns:
194
- if col not in self._data.columns:
195
- self._data[col] = pd.NA
196
- self._data[col] = self._data[col].astype("Float64")
197
- if (flagcol := f"{col}_flag") not in self._data.columns:
198
- self._data[flagcol] = pd.NA
199
- self._data[flagcol] = self._data[flagcol].astype("string")
200
-
201
- # Reorder the DataFrame
202
- self._data = self._data[
203
- self._processed_columns + with_suffix(self._processed_columns, "_flag")
204
- ]
205
-
206
- # Remove model data from DataFrame
207
- if not model:
208
- self._filter_model()
209
-
210
- # Conditionally, remove flags from DataFrame
211
- if not self._flags:
212
- self._data.drop(
213
- with_suffix(self._processed_columns, "_flag"),
214
- axis=1,
215
- errors="ignore",
216
- inplace=True,
217
- )
218
-
219
- # Interpolate data spatially if requested
220
- # location is a geographical point
221
- if isinstance(loc, Point):
222
- self._resolve_point(loc.method, stations, loc.alt, loc.adapt_temp)
223
-
224
- # Clear cache if auto cleaning is enabled
225
- if self.max_age > 0 and self.autoclean:
226
- self.clear_cache()
227
-
228
- # Import methods
229
- from meteostat.series.normalize import normalize
230
- from meteostat.series.interpolate import interpolate
231
- from meteostat.series.aggregate import aggregate
232
- from meteostat.series.convert import convert
233
- from meteostat.series.coverage import coverage
234
- from meteostat.series.count import count
235
- from meteostat.series.fetch import fetch
236
- from meteostat.series.stations import stations
237
- from meteostat.core.cache import clear_cache
@@ -1,48 +0,0 @@
1
- """
2
- Aggregate Data
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
- import pandas as pd
13
- from meteostat.core.warn import warn
14
-
15
-
16
- def aggregate(self, freq: str = None, spatial: bool = False):
17
- """
18
- Aggregate observations
19
- """
20
-
21
- if self.count() > 0 and not self._data.isnull().values.all():
22
- # Create temporal instance
23
- temp = copy(self)
24
-
25
- # Set default frequency if not set
26
- if freq is None:
27
- freq = self._freq
28
-
29
- # Time aggregation
30
- temp._data = temp._data.groupby(
31
- ["station", pd.Grouper(level="time", freq=freq)]
32
- ).agg(temp.aggregations)
33
-
34
- # Spatial aggregation
35
- if spatial:
36
- temp._data = temp._data.groupby(
37
- [pd.Grouper(level="time", freq=freq)]
38
- ).mean()
39
-
40
- # Round
41
- temp._data = temp._data.round(1)
42
-
43
- # Return class instance
44
- return temp
45
-
46
- # Show warning & return self
47
- warn("Skipping aggregation on empty DataFrame")
48
- return self
@@ -1,28 +0,0 @@
1
- """
2
- Convert Data Units
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
-
13
-
14
- def convert(self, units: dict):
15
- """
16
- Convert columns to a different unit
17
- """
18
-
19
- # Create temporal instance
20
- temp = copy(self)
21
-
22
- # Change data units
23
- for parameter, unit in units.items():
24
- if parameter in temp._processed_columns:
25
- temp._data[parameter] = temp._data[parameter].apply(unit)
26
-
27
- # Return class instance
28
- return temp
meteostat/series/count.py DELETED
@@ -1,17 +0,0 @@
1
- """
2
- Get Number Of Rows
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
-
12
- def count(self) -> int:
13
- """
14
- Return number of rows in DataFrame
15
- """
16
-
17
- return len(self._data.index)
@@ -1,20 +0,0 @@
1
- """
2
- Calculate Data Coverage
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
-
12
- def coverage(self, parameter: str = None) -> float:
13
- """
14
- Calculate data coverage (overall or by parameter)
15
- """
16
-
17
- if parameter is None:
18
- return len(self._data.index) / self.expected_rows()
19
-
20
- return round(self._data[parameter].count() / self.expected_rows(), 2)
meteostat/series/fetch.py DELETED
@@ -1,28 +0,0 @@
1
- """
2
- Fetch Data
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
- import pandas as pd
13
-
14
-
15
- def fetch(self) -> pd.DataFrame:
16
- """
17
- Fetch DataFrame
18
- """
19
-
20
- # Copy DataFrame
21
- temp = copy(self._data)
22
-
23
- # Remove station index if it's a single station
24
- if len(self._stations) == 1 and "station" in temp.index.names:
25
- temp = temp.reset_index(level="station", drop=True)
26
-
27
- # Return data frame
28
- return temp
@@ -1,47 +0,0 @@
1
- """
2
- Interpolate Data
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
- import numpy as np
13
- from meteostat.core.warn import warn
14
-
15
-
16
- def interpolate(self, limit: int = 3):
17
- """
18
- Interpolate NULL values
19
- """
20
-
21
- if self.count() > 0 and not self._data.isnull().values.all():
22
- # Create temporal instance
23
- temp = copy(self)
24
-
25
- # Convert to float64
26
- temp._data = temp._data.astype("float64")
27
-
28
- # Apply interpolation
29
- temp._data = temp._data.groupby("station", group_keys=False).apply(
30
- lambda group: group.interpolate(
31
- method="linear",
32
- limit=limit,
33
- limit_direction="both",
34
- axis=0,
35
- fill_value=np.nan,
36
- )
37
- )
38
-
39
- # Convert to original type
40
- temp._data = temp._data.astype("Float64")
41
-
42
- # Return class instance
43
- return temp
44
-
45
- # Show warning & return self
46
- warn("Skipping interpolation on empty DataFrame")
47
- return self
@@ -1,76 +0,0 @@
1
- """
2
- Normalize Data
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
- from numpy import nan
13
- import pandas as pd
14
- import pytz
15
- from meteostat.core.warn import warn
16
-
17
-
18
- def normalize(self):
19
- """
20
- Normalize the DataFrame
21
- """
22
-
23
- if self.count() == 0:
24
- warn("Pointless normalization of empty DataFrame")
25
-
26
- # Create temporal instance
27
- temp = copy(self)
28
-
29
- if temp._start and temp._end and temp.coverage() < 1:
30
- # Create result DataFrame
31
- result = pd.DataFrame(columns=temp._processed_columns, dtype="Float64")
32
-
33
- # Handle tz-aware date ranges
34
- if hasattr(temp, "_timezone") and temp._timezone is not None:
35
- timezone = pytz.timezone(temp._timezone)
36
- start = temp._start.astimezone(timezone)
37
- end = temp._end.astimezone(timezone)
38
- else:
39
- start = temp._start
40
- end = temp._end
41
-
42
- # Go through list of weather stations
43
- for station in temp._stations:
44
- # Create data frame
45
- df = pd.DataFrame(columns=temp._processed_columns, dtype="Float64")
46
- # Add time series
47
- df["time"] = pd.date_range(
48
- start,
49
- end,
50
- freq=self._freq,
51
- tz=temp._timezone if hasattr(temp, "_timezone") else None,
52
- )
53
- # Add station ID
54
- df["station"] = station
55
- # Add columns
56
- for column in temp._processed_columns:
57
- # Add column to DataFrame
58
- df[column] = nan
59
-
60
- result = pd.concat([result, df], axis=0)
61
-
62
- # Set index
63
- result = result.set_index(["station", "time"])
64
-
65
- # Merge data
66
- temp._data = (
67
- pd.concat([temp._data, result], axis=0)
68
- .groupby(["station", "time"], as_index=True)
69
- .first()
70
- )
71
-
72
- # None -> nan
73
- temp._data = temp._data.fillna(pd.NA)
74
-
75
- # Return class instance
76
- return temp
@@ -1,22 +0,0 @@
1
- """
2
- Get Weather Stations
3
-
4
- Meteorological data provided by Meteostat (https://dev.meteostat.net)
5
- under the terms of the Creative Commons Attribution-NonCommercial
6
- 4.0 International Public License.
7
-
8
- The code is licensed under the MIT license.
9
- """
10
-
11
- from copy import copy
12
- import pandas as pd
13
-
14
-
15
- @property
16
- def stations(self) -> pd.Index:
17
- """
18
- Fetch Weather Stations
19
- """
20
-
21
- # Return index of weather stations
22
- return copy(self._stations)
meteostat/units.py DELETED
@@ -1,149 +0,0 @@
1
- """
2
- Meteorological Data Units
3
-
4
- Convert a Pandas Series to any meteorological data unit
5
-
6
- The code is licensed under the MIT license.
7
- """
8
-
9
- from numpy import nan, isnan
10
-
11
-
12
- def fahrenheit(value):
13
- """
14
- Convert Celsius to Fahrenheit
15
- """
16
-
17
- return round((value * 9 / 5) + 32, 1)
18
-
19
-
20
- def kelvin(value):
21
- """
22
- Convert Celsius to Kelvin
23
- """
24
-
25
- return round(value + 273.15, 1)
26
-
27
-
28
- def inches(value):
29
- """
30
- Convert millimeters to inches
31
- """
32
-
33
- return round(value / 25.4, 3)
34
-
35
-
36
- def feet(value):
37
- """
38
- Convert meters to feet
39
- """
40
-
41
- return round(value / 0.3048, 1)
42
-
43
-
44
- def ms(value):
45
- """
46
- Convert kilometers per hour to meters per second
47
- """
48
-
49
- return round(value / 3.6, 1)
50
-
51
-
52
- def mph(value):
53
- """
54
- Convert kilometers per hour to miles per hour
55
- """
56
-
57
- return round(value * 0.6214, 1)
58
-
59
-
60
- def direction(value):
61
- """
62
- Convert degrees to wind direction
63
- """
64
-
65
- wdir = nan
66
-
67
- if (337 <= value <= 360) or value <= 23:
68
- wdir = "N"
69
- if 24 <= value <= 68:
70
- wdir = "NE"
71
- if 69 <= value <= 113:
72
- wdir = "E"
73
- if 114 <= value <= 158:
74
- wdir = "SE"
75
- if 159 <= value <= 203:
76
- wdir = "S"
77
- if 204 <= value <= 248:
78
- wdir = "SW"
79
- if 249 <= value <= 293:
80
- wdir = "W"
81
- if 294 <= value <= 336:
82
- wdir = "NW"
83
-
84
- return wdir
85
-
86
-
87
- def condition(value):
88
- """
89
- Convert Meteostat condition code to descriptive string
90
- """
91
-
92
- if isnan(value) or value < 1 or value > 27:
93
- return nan
94
-
95
- return [
96
- "Clear",
97
- "Fair",
98
- "Cloudy",
99
- "Overcast",
100
- "Fog",
101
- "Freezing Fog",
102
- "Light Rain",
103
- "Rain",
104
- "Heavy Rain",
105
- "Freezing Rain",
106
- "Heavy Freezing Rain",
107
- "Sleet",
108
- "Heavy Sleet",
109
- "Light Snowfall",
110
- "Snowfall",
111
- "Heavy Snowfall",
112
- "Rain Shower",
113
- "Heavy Rain Shower",
114
- "Sleet Shower",
115
- "Heavy Sleet Shower",
116
- "Snow Shower",
117
- "Heavy Snow Shower",
118
- "Lightning",
119
- "Hail",
120
- "Thunderstorm",
121
- "Heavy Thunderstorm",
122
- "Storm",
123
- ][int(value) - 1]
124
-
125
-
126
- # Imperial units
127
- imperial = {
128
- "temp": fahrenheit,
129
- "tavg": fahrenheit,
130
- "tmin": fahrenheit,
131
- "tmax": fahrenheit,
132
- "dwpt": fahrenheit,
133
- "prcp": inches,
134
- "snow": inches,
135
- "wspd": mph,
136
- "wpgt": mph,
137
- "distance": feet,
138
- }
139
-
140
- # Scientific units
141
- scientific = {
142
- "temp": kelvin,
143
- "tavg": kelvin,
144
- "tmin": kelvin,
145
- "tmax": kelvin,
146
- "dwpt": kelvin,
147
- "wspd": ms,
148
- "wpgt": ms,
149
- }
File without changes