env-canada 0.11.0__py3-none-any.whl → 0.11.2__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_map.py +13 -6
- env_canada/ec_weather.py +55 -35
- {env_canada-0.11.0.dist-info → env_canada-0.11.2.dist-info}/METADATA +1 -1
- {env_canada-0.11.0.dist-info → env_canada-0.11.2.dist-info}/RECORD +8 -8
- {env_canada-0.11.0.dist-info → env_canada-0.11.2.dist-info}/WHEEL +0 -0
- {env_canada-0.11.0.dist-info → env_canada-0.11.2.dist-info}/licenses/LICENSE +0 -0
- {env_canada-0.11.0.dist-info → env_canada-0.11.2.dist-info}/top_level.txt +0 -0
env_canada/constants.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
USER_AGENT = "env_canada/0.11.
|
1
|
+
USER_AGENT = "env_canada/0.11.2"
|
env_canada/ec_map.py
CHANGED
@@ -168,15 +168,20 @@ class ECMap:
|
|
168
168
|
self._font = None
|
169
169
|
self.timestamp = None
|
170
170
|
|
171
|
+
def _get_cache_prefix(self):
|
172
|
+
"""Generate a location-specific cache prefix based on bounding box."""
|
173
|
+
return f"{self.bbox[0]:.3f},{self.bbox[1]:.3f},{self.bbox[2]:.3f},{self.bbox[3]:.3f}"
|
174
|
+
|
171
175
|
async def _get_basemap(self):
|
172
176
|
"""Fetch the background map image."""
|
173
|
-
|
177
|
+
basemap_cache_key = f"{self._get_cache_prefix()}-basemap"
|
178
|
+
if base_bytes := Cache.get(basemap_cache_key):
|
174
179
|
return base_bytes
|
175
180
|
|
176
181
|
basemap_params.update(self.map_params)
|
177
182
|
try:
|
178
183
|
base_bytes = await _get_resource(basemap_url, basemap_params)
|
179
|
-
return Cache.add(
|
184
|
+
return Cache.add(basemap_cache_key, base_bytes, timedelta(days=7))
|
180
185
|
except ClientConnectorError as e:
|
181
186
|
LOG.warning("Map from %s could not be retrieved: %s", basemap_url, e)
|
182
187
|
return None
|
@@ -220,7 +225,7 @@ class ECMap:
|
|
220
225
|
async def _get_legend(self):
|
221
226
|
"""Fetch legend image for the layer."""
|
222
227
|
|
223
|
-
legend_cache_key = f"legend-{self.layer}"
|
228
|
+
legend_cache_key = f"{self._get_cache_prefix()}-legend-{self.layer}"
|
224
229
|
if legend := Cache.get(legend_cache_key):
|
225
230
|
return legend
|
226
231
|
|
@@ -269,7 +274,7 @@ class ECMap:
|
|
269
274
|
async def _get_layer_image(self, frame_time):
|
270
275
|
"""Fetch image for the layer at a specific time."""
|
271
276
|
time = frame_time.strftime("%Y-%m-%dT%H:%M:00Z")
|
272
|
-
layer_cache_key = f"layer-{self.layer}-{time}"
|
277
|
+
layer_cache_key = f"{self._get_cache_prefix()}-layer-{self.layer}-{time}"
|
273
278
|
|
274
279
|
if img := Cache.get(layer_cache_key):
|
275
280
|
return img
|
@@ -362,11 +367,13 @@ class ECMap:
|
|
362
367
|
composite.save(img_byte_arr, format="PNG")
|
363
368
|
|
364
369
|
return Cache.add(
|
365
|
-
f"composite-{time}",
|
370
|
+
f"{self._get_cache_prefix()}-composite-{time}",
|
371
|
+
img_byte_arr.getvalue(),
|
372
|
+
timedelta(minutes=200),
|
366
373
|
)
|
367
374
|
|
368
375
|
time = frame_time.strftime("%Y-%m-%dT%H:%M:00Z")
|
369
|
-
cache_key = f"composite-{time}"
|
376
|
+
cache_key = f"{self._get_cache_prefix()}-composite-{time}"
|
370
377
|
|
371
378
|
if img := Cache.get(cache_key):
|
372
379
|
return img
|
env_canada/ec_weather.py
CHANGED
@@ -240,7 +240,7 @@ def validate_station(station):
|
|
240
240
|
raise vol.Invalid(
|
241
241
|
'Station ID must be of the form "XX/s0000###", "s0000###" or 1-3 digits'
|
242
242
|
)
|
243
|
-
return station
|
243
|
+
return station
|
244
244
|
|
245
245
|
|
246
246
|
def _parse_timestamp(time_str: str | None) -> datetime | None:
|
@@ -385,10 +385,13 @@ class ECWeather:
|
|
385
385
|
self.hourly_forecasts = []
|
386
386
|
self.forecast_time = ""
|
387
387
|
self.site_list = []
|
388
|
+
self._station_resolved = False
|
389
|
+
self._station_tuple = (
|
390
|
+
None # Internal storage for (province_code, station_number)
|
391
|
+
)
|
388
392
|
|
389
393
|
if "station_id" in kwargs and kwargs["station_id"] is not None:
|
390
|
-
|
391
|
-
self.station_id = kwargs["station_id"] # Store raw input temporarily
|
394
|
+
self.station_id = kwargs["station_id"] # Keep as string for external API
|
392
395
|
self.lat = None
|
393
396
|
self.lon = None
|
394
397
|
else:
|
@@ -413,43 +416,57 @@ class ECWeather:
|
|
413
416
|
self.metadata.cache_returned_on_update = 0
|
414
417
|
raise ECWeatherUpdateFailed(msg) from err
|
415
418
|
|
419
|
+
async def _resolve_station(self) -> None:
|
420
|
+
"""Resolve station ID and coordinates (one-time initialization)."""
|
421
|
+
if self._station_resolved:
|
422
|
+
return
|
423
|
+
|
424
|
+
if not self.site_list:
|
425
|
+
self.site_list = await get_ec_sites()
|
426
|
+
|
427
|
+
if self.station_id:
|
428
|
+
# Convert raw station input to tuple (province_code, station_number)
|
429
|
+
station_number = self.station_id[-3:]
|
430
|
+
province_code = find_province_for_station(self.site_list, station_number)
|
431
|
+
if province_code is None:
|
432
|
+
raise ec_exc.UnknownStationId
|
433
|
+
self._station_tuple = (province_code, station_number)
|
434
|
+
|
435
|
+
# Find lat/lon for the station
|
436
|
+
for site in self.site_list:
|
437
|
+
if (
|
438
|
+
site["Codes"] == f"s0000{self._station_tuple[1].zfill(3)}"
|
439
|
+
and site["Province Codes"] == self._station_tuple[0]
|
440
|
+
):
|
441
|
+
self.lat = site["Latitude"]
|
442
|
+
self.lon = site["Longitude"]
|
443
|
+
break
|
444
|
+
if not self.lat:
|
445
|
+
raise ec_exc.UnknownStationId
|
446
|
+
else:
|
447
|
+
self._station_tuple = closest_site(self.site_list, self.lat, self.lon)
|
448
|
+
if not self._station_tuple:
|
449
|
+
raise ec_exc.UnknownStationId
|
450
|
+
# Set station_id to string format for external API
|
451
|
+
self.station_id = (
|
452
|
+
f"{self._station_tuple[0]}/s0000{self._station_tuple[1].zfill(3)}"
|
453
|
+
)
|
454
|
+
|
455
|
+
self._station_resolved = True
|
456
|
+
|
457
|
+
@property
|
458
|
+
def station_tuple(self):
|
459
|
+
"""Return station ID as a tuple (province_code, station_number) for internal use."""
|
460
|
+
return self._station_tuple
|
461
|
+
|
416
462
|
async def update(self) -> None:
|
417
463
|
"""Get the latest data from Environment Canada."""
|
418
464
|
|
419
465
|
# Clear error at start, any error that is handled will set it
|
420
466
|
self.metadata.last_update_error = ""
|
421
467
|
|
422
|
-
#
|
423
|
-
|
424
|
-
self.site_list = await get_ec_sites()
|
425
|
-
if self.station_id:
|
426
|
-
# Convert raw station input to tuple (province_code, station_number)
|
427
|
-
if isinstance(self.station_id, str):
|
428
|
-
station_number = (
|
429
|
-
self.station_id
|
430
|
-
) # Already normalized by validate_station
|
431
|
-
province_code = find_province_for_station(
|
432
|
-
self.site_list, station_number
|
433
|
-
)
|
434
|
-
if province_code is None:
|
435
|
-
raise ec_exc.UnknownStationId
|
436
|
-
self.station_id = (province_code, station_number)
|
437
|
-
|
438
|
-
# Find lat/lon for the station
|
439
|
-
for site in self.site_list:
|
440
|
-
if (
|
441
|
-
site["Codes"] == f"s0000{self.station_id[1].zfill(3)}"
|
442
|
-
and site["Province Codes"] == self.station_id[0]
|
443
|
-
):
|
444
|
-
self.lat = site["Latitude"]
|
445
|
-
self.lon = site["Longitude"]
|
446
|
-
break
|
447
|
-
if not self.lat:
|
448
|
-
raise ec_exc.UnknownStationId
|
449
|
-
else:
|
450
|
-
self.station_id = closest_site(self.site_list, self.lat, self.lon)
|
451
|
-
if not self.station_id:
|
452
|
-
raise ec_exc.UnknownStationId
|
468
|
+
# Resolve station ID and coordinates if not already done
|
469
|
+
await self._resolve_station()
|
453
470
|
|
454
471
|
LOG.debug(
|
455
472
|
"update(): station %s lat %f lon %f", self.station_id, self.lat, self.lon
|
@@ -460,7 +477,10 @@ class ECWeather:
|
|
460
477
|
async with ClientSession(raise_for_status=True) as session:
|
461
478
|
# Discover the URL for the most recent weather file
|
462
479
|
weather_url = await discover_weather_file_url(
|
463
|
-
session,
|
480
|
+
session,
|
481
|
+
self._station_tuple[0],
|
482
|
+
self._station_tuple[1],
|
483
|
+
self.language,
|
464
484
|
)
|
465
485
|
LOG.debug("Using weather URL: %s", weather_url)
|
466
486
|
|
@@ -1,17 +1,17 @@
|
|
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=EXzGEHwon-usFzLzuJeISKHlfJdV3DBa0_rR9b_XfvE,405
|
4
|
-
env_canada/constants.py,sha256=
|
4
|
+
env_canada/constants.py,sha256=pB3ld_VBSAL8c6LAUF0U2VgKhhkOmP7U6T5kLJvo938,33
|
5
5
|
env_canada/ec_aqhi.py,sha256=oE52qfk-AKbHdhTSl5RP3vsWL-50eMRCCRVy9RW-pP4,8080
|
6
6
|
env_canada/ec_cache.py,sha256=zb3n79ul7hUTE0IohDfZbRBLY-siOHPjYzWldMbuPVk,798
|
7
7
|
env_canada/ec_exc.py,sha256=SBJwzmLf94lTx7KYVLfQYrMXYNYUoIxeVXc-BLkuXoE,67
|
8
8
|
env_canada/ec_historical.py,sha256=qMr4RE6vfNiNa_zFolQ0PQGraok8bQtIVjs_o6sJKD4,16276
|
9
9
|
env_canada/ec_hydro.py,sha256=JoBe-QVV8GEeZXCNFscIs2R_spgkbxCZpLt7tL6-NUI,4889
|
10
|
-
env_canada/ec_map.py,sha256=
|
10
|
+
env_canada/ec_map.py,sha256=HIyCZ8j9EbuLX3-YhvMKjYPiE2AH_behL6IaOXjXpaQ,15564
|
11
11
|
env_canada/ec_radar.py,sha256=dKZqWJyb66R2EJzAy4K7pii7vPK9FxDKmuW9vA1ADbw,3330
|
12
|
-
env_canada/ec_weather.py,sha256=
|
13
|
-
env_canada-0.11.
|
14
|
-
env_canada-0.11.
|
15
|
-
env_canada-0.11.
|
16
|
-
env_canada-0.11.
|
17
|
-
env_canada-0.11.
|
12
|
+
env_canada/ec_weather.py,sha256=pFcVKBbYKLj01ocjZmy8RiQaf7YVSQOo4id_BbZ7gds,23421
|
13
|
+
env_canada-0.11.2.dist-info/licenses/LICENSE,sha256=BkgGIGgy9sv-OsI7mRi9dIQ3Su0m4IbjpZlfxv8oBbM,1073
|
14
|
+
env_canada-0.11.2.dist-info/METADATA,sha256=uWS4H6dImil9koOPvBxkdSJKut2gmOzsqLwjIyE2sOc,13678
|
15
|
+
env_canada-0.11.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
16
|
+
env_canada-0.11.2.dist-info/top_level.txt,sha256=fw7Pcl9ULBXYvqnAdyBdmwPXW8GSRFmhO0sLZWVfOCc,11
|
17
|
+
env_canada-0.11.2.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|