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 CHANGED
@@ -1 +1 @@
1
- USER_AGENT = "env_canada/0.11.0"
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
- if base_bytes := Cache.get("basemap"):
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("basemap", base_bytes, timedelta(days=7))
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}", img_byte_arr.getvalue(), timedelta(minutes=200)
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[-3:]
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
- # Station ID will be converted to tuple (province_code, station_number) during update()
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
- # Determine station ID or coordinates if not provided
423
- if not self.site_list:
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, self.station_id[0], self.station_id[1], self.language
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: env_canada
3
- Version: 0.11.0
3
+ Version: 0.11.2
4
4
  Summary: A package to access meteorological data from Environment Canada
5
5
  Author-email: Michael Davie <michael.davie@gmail.com>
6
6
  Maintainer-email: Michael Davie <michael.davie@gmail.com>
@@ -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=YkWAwWu2M6izF61EYMWanH5S-3crC0PV2Wo5mgt5aOg,33
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=936CN6G9ZAY9zQjuph0Xp4f9OwNnHb6qXMnk6M6bE68,15134
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=ztMYx8jnmGqUQiUrqGFkiag7B3Xb0uhqo5g5j57bevI,22948
13
- env_canada-0.11.0.dist-info/licenses/LICENSE,sha256=BkgGIGgy9sv-OsI7mRi9dIQ3Su0m4IbjpZlfxv8oBbM,1073
14
- env_canada-0.11.0.dist-info/METADATA,sha256=KwVr43s8m3N3zOmCuzmThL1kiQUOPQfV-wZibXfJEZs,13678
15
- env_canada-0.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- env_canada-0.11.0.dist-info/top_level.txt,sha256=fw7Pcl9ULBXYvqnAdyBdmwPXW8GSRFmhO0sLZWVfOCc,11
17
- env_canada-0.11.0.dist-info/RECORD,,
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,,