env-canada 0.6.2__py3-none-any.whl → 0.7.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.
- env_canada/constants.py +1 -1
- env_canada/ec_radar.py +73 -60
- env_canada/ec_weather.py +7 -22
- {env_canada-0.6.2.dist-info → env_canada-0.7.0.dist-info}/METADATA +3 -3
- {env_canada-0.6.2.dist-info → env_canada-0.7.0.dist-info}/RECORD +8 -8
- {env_canada-0.6.2.dist-info → env_canada-0.7.0.dist-info}/WHEEL +1 -1
- {env_canada-0.6.2.dist-info → env_canada-0.7.0.dist-info}/LICENSE +0 -0
- {env_canada-0.6.2.dist-info → env_canada-0.7.0.dist-info}/top_level.txt +0 -0
env_canada/constants.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
USER_AGENT = "env_canada/0.
|
1
|
+
USER_AGENT = "env_canada/0.7.0"
|
env_canada/ec_radar.py
CHANGED
@@ -157,16 +157,12 @@ class ECRadar(object):
|
|
157
157
|
# Get overlay parameters
|
158
158
|
|
159
159
|
self.show_legend = kwargs["legend"]
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
self.legend_position = None
|
160
|
+
self.legend_layer = None
|
161
|
+
self.legend_image = None
|
162
|
+
self.legend_position = None
|
164
163
|
|
165
164
|
self.show_timestamp = kwargs["timestamp"]
|
166
|
-
|
167
|
-
self.font = ImageFont.load(
|
168
|
-
os.path.join(os.path.dirname(__file__), "10x20.pil")
|
169
|
-
)
|
165
|
+
self.font = None
|
170
166
|
|
171
167
|
@property
|
172
168
|
def precip_type(self):
|
@@ -198,22 +194,20 @@ class ECRadar(object):
|
|
198
194
|
async with ClientSession(raise_for_status=True) as session:
|
199
195
|
response = await session.get(url=basemap_url, params=basemap_params)
|
200
196
|
base_bytes = await response.read()
|
201
|
-
self.map_image = Image.open(BytesIO(base_bytes)).convert("RGBA")
|
202
197
|
|
203
198
|
except ClientConnectorError as e:
|
204
199
|
logging.warning("NRCan base map could not be retrieved: %s" % e)
|
205
|
-
|
206
200
|
try:
|
207
201
|
async with ClientSession(raise_for_status=True) as session:
|
208
202
|
response = await session.get(
|
209
203
|
url=backup_map_url, params=basemap_params
|
210
204
|
)
|
211
205
|
base_bytes = await response.read()
|
212
|
-
self.map_image = Image.open(BytesIO(base_bytes)).convert("RGBA")
|
213
206
|
except ClientConnectorError:
|
214
207
|
logging.warning("Mapbox base map could not be retrieved")
|
208
|
+
return None
|
215
209
|
|
216
|
-
return
|
210
|
+
return base_bytes
|
217
211
|
|
218
212
|
async def _get_legend(self):
|
219
213
|
"""Fetch legend image."""
|
@@ -222,13 +216,13 @@ class ECRadar(object):
|
|
222
216
|
layer=precip_layers[self.layer_key], style=legend_style[self.layer_key]
|
223
217
|
)
|
224
218
|
)
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
219
|
+
try:
|
220
|
+
async with ClientSession(raise_for_status=True) as session:
|
221
|
+
response = await session.get(url=geomet_url, params=legend_params)
|
222
|
+
return await response.read()
|
223
|
+
except ClientConnectorError:
|
224
|
+
logging.warning("Legend could not be retrieved")
|
225
|
+
return None
|
232
226
|
|
233
227
|
async def _get_dimensions(self):
|
234
228
|
"""Get time range of available data."""
|
@@ -256,54 +250,73 @@ class ECRadar(object):
|
|
256
250
|
async def _combine_layers(self, radar_bytes, frame_time):
|
257
251
|
"""Add radar overlay to base layer and add timestamp."""
|
258
252
|
|
259
|
-
|
260
|
-
|
261
|
-
# Add transparency to radar
|
262
|
-
|
263
|
-
if self.radar_opacity < 100:
|
264
|
-
alpha = round((self.radar_opacity / 100) * 255)
|
265
|
-
radar_copy = radar.copy()
|
266
|
-
radar_copy.putalpha(alpha)
|
267
|
-
radar.paste(radar_copy, radar)
|
268
|
-
|
269
|
-
# Overlay radar on basemap
|
270
|
-
|
253
|
+
base_bytes = None
|
271
254
|
if not self.map_image:
|
272
|
-
await self._get_basemap()
|
273
|
-
if self.map_image:
|
274
|
-
frame = Image.alpha_composite(self.map_image, radar)
|
275
|
-
else:
|
276
|
-
frame = radar
|
277
|
-
|
278
|
-
# Add legend
|
255
|
+
base_bytes = await self._get_basemap()
|
279
256
|
|
257
|
+
legend_bytes = None
|
280
258
|
if self.show_legend:
|
281
259
|
if not self.legend_image or self.legend_layer != self.layer_key:
|
282
|
-
await self._get_legend()
|
283
|
-
frame.paste(self.legend_image, self.legend_position)
|
260
|
+
legend_bytes = await self._get_legend()
|
284
261
|
|
285
|
-
#
|
262
|
+
# All the synchronous PIL stuff here
|
263
|
+
def _create_image():
|
264
|
+
radar = Image.open(BytesIO(radar_bytes)).convert("RGBA")
|
286
265
|
|
287
|
-
|
288
|
-
|
289
|
-
timestamp_label[self.layer_key][self.language]
|
290
|
-
+ " @ "
|
291
|
-
+ frame_time.astimezone().strftime("%H:%M")
|
292
|
-
)
|
293
|
-
text_box = Image.new("RGBA", self.font.getbbox(timestamp)[2:], "white")
|
294
|
-
box_draw = ImageDraw.Draw(text_box)
|
295
|
-
box_draw.text(xy=(0, 0), text=timestamp, fill=(0, 0, 0), font=self.font)
|
296
|
-
double_box = text_box.resize((text_box.width * 2, text_box.height * 2))
|
297
|
-
frame.paste(double_box)
|
298
|
-
frame = frame.quantize()
|
299
|
-
|
300
|
-
# Return frame as PNG bytes
|
301
|
-
|
302
|
-
img_byte_arr = BytesIO()
|
303
|
-
frame.save(img_byte_arr, format="PNG")
|
304
|
-
frame_bytes = img_byte_arr.getvalue()
|
266
|
+
if base_bytes:
|
267
|
+
self.map_image = Image.open(BytesIO(base_bytes)).convert("RGBA")
|
305
268
|
|
306
|
-
|
269
|
+
if legend_bytes:
|
270
|
+
self.legend_image = Image.open(BytesIO(legend_bytes)).convert("RGB")
|
271
|
+
legend_width = self.legend_image.size[0]
|
272
|
+
self.legend_position = (self.width - legend_width, 0)
|
273
|
+
self.legend_layer = self.layer_key
|
274
|
+
|
275
|
+
# Add transparency to radar
|
276
|
+
if self.radar_opacity < 100:
|
277
|
+
alpha = round((self.radar_opacity / 100) * 255)
|
278
|
+
radar_copy = radar.copy()
|
279
|
+
radar_copy.putalpha(alpha)
|
280
|
+
radar.paste(radar_copy, radar)
|
281
|
+
|
282
|
+
if self.show_timestamp and not self.font:
|
283
|
+
self.font = ImageFont.load(
|
284
|
+
os.path.join(os.path.dirname(__file__), "10x20.pil")
|
285
|
+
)
|
286
|
+
|
287
|
+
# Overlay radar on basemap
|
288
|
+
if self.map_image:
|
289
|
+
frame = Image.alpha_composite(self.map_image, radar)
|
290
|
+
else:
|
291
|
+
frame = radar
|
292
|
+
|
293
|
+
# Add legend
|
294
|
+
if self.show_legend and self.legend_image:
|
295
|
+
frame.paste(self.legend_image, self.legend_position)
|
296
|
+
|
297
|
+
# Add timestamp
|
298
|
+
if self.show_timestamp and self.font:
|
299
|
+
timestamp = (
|
300
|
+
timestamp_label[self.layer_key][self.language]
|
301
|
+
+ " @ "
|
302
|
+
+ frame_time.astimezone().strftime("%H:%M")
|
303
|
+
)
|
304
|
+
text_box = Image.new("RGBA", self.font.getbbox(timestamp)[2:], "white")
|
305
|
+
box_draw = ImageDraw.Draw(text_box)
|
306
|
+
box_draw.text(xy=(0, 0), text=timestamp, fill=(0, 0, 0), font=self.font)
|
307
|
+
double_box = text_box.resize((text_box.width * 2, text_box.height * 2))
|
308
|
+
frame.paste(double_box)
|
309
|
+
frame = frame.quantize()
|
310
|
+
|
311
|
+
# Return frame as PNG bytes
|
312
|
+
img_byte_arr = BytesIO()
|
313
|
+
frame.save(img_byte_arr, format="PNG")
|
314
|
+
frame_bytes = img_byte_arr.getvalue()
|
315
|
+
|
316
|
+
return frame_bytes
|
317
|
+
|
318
|
+
# Since PIL is synchronous, run it all in another thread
|
319
|
+
return await asyncio.get_event_loop().run_in_executor(None, _create_image)
|
307
320
|
|
308
321
|
async def _get_radar_image(self, session, frame_time):
|
309
322
|
params = dict(
|
env_canada/ec_weather.py
CHANGED
@@ -137,24 +137,6 @@ conditions_meta = {
|
|
137
137
|
"english": "Icon Code",
|
138
138
|
"french": "Code icône",
|
139
139
|
},
|
140
|
-
"high_temp_yesterday": {
|
141
|
-
"xpath": './yesterdayConditions/temperature[@class="high"]',
|
142
|
-
"type": "float",
|
143
|
-
"english": "High Temperature Yesterday",
|
144
|
-
"french": "Haute température d'hier",
|
145
|
-
},
|
146
|
-
"low_temp_yesterday": {
|
147
|
-
"xpath": './yesterdayConditions/temperature[@class="low"]',
|
148
|
-
"type": "float",
|
149
|
-
"english": "Low Temperature Yesterday",
|
150
|
-
"french": "Basse température d'hier",
|
151
|
-
},
|
152
|
-
"precip_yesterday": {
|
153
|
-
"xpath": "./yesterdayConditions/precip",
|
154
|
-
"type": "float",
|
155
|
-
"english": "Precipitation Yesterday",
|
156
|
-
"french": "Précipitation d'hier",
|
157
|
-
},
|
158
140
|
"normal_high": {
|
159
141
|
"xpath": './forecastGroup/regionalNormals/temperature[@class="high"]',
|
160
142
|
"type": "int",
|
@@ -183,7 +165,7 @@ conditions_meta = {
|
|
183
165
|
"xpath": "./currentConditions/dateTime/timeStamp",
|
184
166
|
"type": "timestamp",
|
185
167
|
"english": "Observation Time",
|
186
|
-
"french": "Temps d'observation"
|
168
|
+
"french": "Temps d'observation",
|
187
169
|
},
|
188
170
|
}
|
189
171
|
|
@@ -278,7 +260,6 @@ def closest_site(site_list, lat, lon):
|
|
278
260
|
|
279
261
|
|
280
262
|
class ECWeather(object):
|
281
|
-
|
282
263
|
"""Get weather data from Environment Canada."""
|
283
264
|
|
284
265
|
def __init__(self, **kwargs):
|
@@ -432,7 +413,9 @@ class ECWeather(object):
|
|
432
413
|
self.conditions[c].update(get_condition(meta))
|
433
414
|
|
434
415
|
# Update station metadata
|
435
|
-
self.metadata["station"] = weather_tree.find(
|
416
|
+
self.metadata["station"] = weather_tree.find(
|
417
|
+
"./currentConditions/station"
|
418
|
+
).text
|
436
419
|
|
437
420
|
# Update text summary
|
438
421
|
period = get_condition(summary_meta["forecast_period"])["value"]
|
@@ -494,7 +477,9 @@ class ECWeather(object):
|
|
494
477
|
"temperature": int(f.findtext("./temperature") or 0),
|
495
478
|
"icon_code": f.findtext("./iconCode"),
|
496
479
|
"precip_probability": int(f.findtext("./lop") or "0"),
|
497
|
-
"wind_speed": int(
|
480
|
+
"wind_speed": int(
|
481
|
+
wind_speed_text if wind_speed_text.isnumeric() else 0
|
482
|
+
),
|
498
483
|
"wind_direction": f.findtext("./wind/direction"),
|
499
484
|
}
|
500
485
|
)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: env_canada
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.7.0
|
4
4
|
Summary: A package to access meteorological data from Environment Canada
|
5
5
|
Home-page: https://github.com/michaeldavie/env_canada
|
6
6
|
Author: Michael Davie
|
@@ -25,7 +25,7 @@ Requires-Dist: voluptuous
|
|
25
25
|
# Environment Canada (env_canada)
|
26
26
|
|
27
27
|
[](https://badge.fury.io/py/env-canada)
|
28
|
-
[](https://snyk.io/vuln/pip:env-canada@0.
|
28
|
+
[](https://snyk.io/vuln/pip:env-canada@0.7.0?utm_source=badge)
|
29
29
|
|
30
30
|
This package provides access to various data sources published by [Environment and Climate Change Canada](https://www.canada.ca/en/environment-climate-change.html).
|
31
31
|
|
@@ -207,4 +207,4 @@ of `datetime(2022, 7, 1, 12, 12)`
|
|
207
207
|
|
208
208
|
# License
|
209
209
|
|
210
|
-
The code is available under terms of [MIT License](LICENSE.md)
|
210
|
+
The code is available under terms of [MIT License](https://github.com/michaeldavie/env_canada/tree/master/LICENSE.md)
|
@@ -1,16 +1,16 @@
|
|
1
1
|
env_canada/10x20.pbm,sha256=ClKTs2WUmhUhTHAQzPuGwPTICGVBzCvos5l-vHRBE5M,2463
|
2
2
|
env_canada/10x20.pil,sha256=Oki6-TD7b0xFtfm6vxCKsmpEpsZ5Jaia_0v_aDz8bfE,5143
|
3
3
|
env_canada/__init__.py,sha256=wEx1BCwVUH__GoosSlhNMHuUKCKNZAvv5uuSa5ZWq_g,187
|
4
|
-
env_canada/constants.py,sha256=
|
4
|
+
env_canada/constants.py,sha256=xUcfT4fpHqhAnvC1GZ_gpYYrJYozfitwFiOBKN65xXg,32
|
5
5
|
env_canada/ec_aqhi.py,sha256=kJQ8xEgFnujGMYdxRXpoEK17B5e-ya-Y7rK0vLo_-w0,7768
|
6
6
|
env_canada/ec_cache.py,sha256=qoFxmO-kOBT8jhgPeNWtVBRmguXcARIIOI54OaDh-20,1171
|
7
7
|
env_canada/ec_exc.py,sha256=SBJwzmLf94lTx7KYVLfQYrMXYNYUoIxeVXc-BLkuXoE,67
|
8
8
|
env_canada/ec_historical.py,sha256=slHaFwsoyW16uCVtE3_-IF3_BFhFD4IuWl7rpIRsCm4,15901
|
9
9
|
env_canada/ec_hydro.py,sha256=LBsWreTlaTKec6ObjI0ih8-zOKBNjD02oiXKTyUa1EQ,4898
|
10
|
-
env_canada/ec_radar.py,sha256=
|
11
|
-
env_canada/ec_weather.py,sha256=
|
12
|
-
env_canada-0.
|
13
|
-
env_canada-0.
|
14
|
-
env_canada-0.
|
15
|
-
env_canada-0.
|
16
|
-
env_canada-0.
|
10
|
+
env_canada/ec_radar.py,sha256=gcLa2z5T_CkrY-NLEJRqaLDHODJRcO5unW5MGxjKxF8,13115
|
11
|
+
env_canada/ec_weather.py,sha256=M7nPeZIKLirRIcCENB8z2B8aBDZHrjltzMYPgRz9lz0,16789
|
12
|
+
env_canada-0.7.0.dist-info/LICENSE,sha256=c037dTHQWAgRgDqZNN-5d-CZvcteSYN37u39SNklO0I,1072
|
13
|
+
env_canada-0.7.0.dist-info/METADATA,sha256=IueyWyN-i8vtUD8G4p8kMumCncfOUyPUrUS_TMEfw7w,10707
|
14
|
+
env_canada-0.7.0.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
|
15
|
+
env_canada-0.7.0.dist-info/top_level.txt,sha256=fw7Pcl9ULBXYvqnAdyBdmwPXW8GSRFmhO0sLZWVfOCc,11
|
16
|
+
env_canada-0.7.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|