core-lens 0.1.dev142__tar.gz → 0.1.dev143__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.
Files changed (91) hide show
  1. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/PKG-INFO +1 -1
  2. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/_version.py +2 -2
  3. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/aoi.py +29 -42
  4. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_aoi.py +4 -2
  5. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
  6. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  7. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
  8. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/pull_request_template.md +0 -0
  9. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/workflows/ci.yml +0 -0
  10. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/workflows/gh-pages.yml +0 -0
  11. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/workflows/pre-release.yml +0 -0
  12. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.github/workflows/release.yml +0 -0
  13. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.gitignore +0 -0
  14. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.gitmessage +0 -0
  15. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.pre-commit-config.yaml +0 -0
  16. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/.python-version +0 -0
  17. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/CONTRIBUTING.md +0 -0
  18. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/LICENSE +0 -0
  19. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/README.md +0 -0
  20. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/SKILLS.md +0 -0
  21. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/README.md +0 -0
  22. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_aoi.py +0 -0
  23. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_export.py +0 -0
  24. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_polars_utils.py +0 -0
  25. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_result.py +0 -0
  26. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_schema.py +0 -0
  27. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_season.py +0 -0
  28. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_spatial.py +0 -0
  29. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/bench_view.py +0 -0
  30. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/benchmarks/run_all.sh +0 -0
  31. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/Makefile +0 -0
  32. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/make.bat +0 -0
  33. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/concepts.md +0 -0
  34. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/conf.py +0 -0
  35. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/export.md +0 -0
  36. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/index.rst +0 -0
  37. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/intro.md +0 -0
  38. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/logging.md +0 -0
  39. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/plots.md +0 -0
  40. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/plugins.md +0 -0
  41. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/queries.md +0 -0
  42. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/quickstart.md +0 -0
  43. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/docs/source/stats.md +0 -0
  44. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/examples/demo_mws.py +0 -0
  45. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/examples/demo_tehsil.py +0 -0
  46. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/hooks/mypy.sh +0 -0
  47. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/hooks/no-parquet-outside-fixtures.sh +0 -0
  48. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/hooks/pytest.sh +0 -0
  49. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/pyproject.toml +0 -0
  50. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/__init__.py +0 -0
  51. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/__main__.py +0 -0
  52. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/__init__.py +0 -0
  53. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/entity.py +0 -0
  54. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/namespaces/__init__.py +0 -0
  55. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/namespaces/plot.py +0 -0
  56. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/namespaces/stats.py +0 -0
  57. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/result.py +0 -0
  58. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/base/view.py +0 -0
  59. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/entities/__init__.py +0 -0
  60. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/entities/mws.py +0 -0
  61. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/entities/tehsil.py +0 -0
  62. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/entities/waterbody.py +0 -0
  63. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/export/__init__.py +0 -0
  64. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/export/formats.py +0 -0
  65. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/py.typed +0 -0
  66. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/schema/__init__.py +0 -0
  67. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/schema/detection.py +0 -0
  68. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/schema/profile.py +0 -0
  69. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/utils/__init__.py +0 -0
  70. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/utils/paths.py +0 -0
  71. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/utils/polars_utils.py +0 -0
  72. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/utils/season.py +0 -0
  73. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/src/core_lens/utils/spatial.py +0 -0
  74. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/fixtures/generate_fixtures.py +0 -0
  75. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/conftest.py +0 -0
  76. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_entities.py +0 -0
  77. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_entity.py +0 -0
  78. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_export.py +0 -0
  79. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_main.py +0 -0
  80. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_plot.py +0 -0
  81. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_polars_utils.py +0 -0
  82. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_profile.py +0 -0
  83. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_result.py +0 -0
  84. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_schema_detection.py +0 -0
  85. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_schema_profile.py +0 -0
  86. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_season.py +0 -0
  87. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_season_config.py +0 -0
  88. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_spatial.py +0 -0
  89. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_stats.py +0 -0
  90. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/tests/unit/test_view.py +0 -0
  91. {core_lens-0.1.dev142 → core_lens-0.1.dev143}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-lens
3
- Version: 0.1.dev142
3
+ Version: 0.1.dev143
4
4
  Summary: Query, analyse, and visualise CoreStack's microwatershed and Earth science data through a clean, composable Python API.
5
5
  Project-URL: Homepage, https://github.com/ApoorvaKashyap/core-lens
6
6
  Project-URL: Issues, https://github.com/ApoorvaKashyap/core-lens/issues
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '0.1.dev142'
22
- __version_tuple__ = version_tuple = (0, 1, 'dev142')
21
+ __version__ = version = '0.1.dev143'
22
+ __version_tuple__ = version_tuple = (0, 1, 'dev143')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -383,19 +383,16 @@ class AoI:
383
383
  # Entity instances are created lazily on first access via _get_entity().
384
384
  self._entity_instances: dict[str, BaseEntity] = {}
385
385
 
386
- # Initialise boundary-entity cache. Only populated by the
387
- # named-boundary path (_resolve_named_boundary); bbox= / geometry=
388
- # construction leaves these as None so __getattr__ falls through
389
- # to the normal spatial_filter path for every entity.
390
- self._boundary_entity_name: str | None = None
391
- self._boundary_keys: pl.DataFrame | None = None
386
+ # Initialise boundary kwargs and lazily computed geometry.
387
+ self._boundary_kwargs: dict[str, str | list[str]] | None = None
388
+ self._geometry: shapely.Geometry | None = None
392
389
 
393
390
  if geometry is not None:
394
- self.geometry: "shapely.Geometry" = geometry
391
+ self._geometry = geometry
395
392
  elif bbox is not None:
396
- self.geometry = _bbox_to_polygon(bbox)
393
+ self._geometry = _bbox_to_polygon(bbox)
397
394
  else:
398
- self.geometry = self._resolve_named_boundary(entity_kwargs)
395
+ self._boundary_kwargs = entity_kwargs
399
396
 
400
397
  # Entity views are created lazily on demand in __getattr__.
401
398
  self._scoped: dict[str, "View"] = {}
@@ -406,6 +403,17 @@ class AoI:
406
403
  if validate_all:
407
404
  self.validate()
408
405
 
406
+ @property
407
+ def geometry(self) -> "shapely.Geometry":
408
+ """The resolved boundary of this AoI as a Shapely geometry."""
409
+ if self._geometry is None:
410
+ if self._boundary_kwargs is not None:
411
+ self._geometry = self._resolve_named_boundary(self._boundary_kwargs)
412
+ else:
413
+ # Should not happen if initialization was correct
414
+ raise ValueError("No boundary defined.")
415
+ return self._geometry
416
+
409
417
  @property
410
418
  def current_season(self) -> str:
411
419
  """The season name for today's date under the AoI's SeasonConfig.
@@ -538,23 +546,20 @@ class AoI:
538
546
  if name in _REGISTRY:
539
547
  if name not in self._scoped:
540
548
  entity = self._get_entity(name)
541
- if name == self._boundary_entity_name:
542
- # This entity *defined* the AoI boundary — we already
543
- # have its exact matching keys from
544
- # _resolve_named_boundary. Skip the bbox-index build
545
- # and geometric predicate entirely.
546
- from core_lens.base.view import View
547
-
548
- assert (
549
- self._boundary_keys is not None
550
- ) # set by _resolve_named_boundary
551
- view = View(
552
- keys=self._boundary_keys,
553
- entity=entity,
554
- entity_name=name,
555
- )
549
+
550
+ if self._boundary_kwargs is not None:
551
+ schema = entity.schema_profile
552
+ # If this entity has all the filter columns natively, filter directly
553
+ if all(
554
+ k in schema.key_cols or k in schema.extra_static_cols
555
+ for k in self._boundary_kwargs
556
+ ):
557
+ view = entity.where(**self._boundary_kwargs)
558
+ else:
559
+ view = entity.spatial_filter(geometry=self.geometry)
556
560
  else:
557
561
  view = entity.spatial_filter(geometry=self.geometry)
562
+
558
563
  view._season_config = self.seasons
559
564
  self._scoped[name] = view
560
565
  return self._scoped[name]
@@ -608,24 +613,6 @@ class AoI:
608
613
  # Reconstruct the boundary geometry from cached WKB bytes (fast).
609
614
  geometry: shapely.Geometry = shapely.from_wkb(geom_wkb)
610
615
 
611
- # Reconstruct _boundary_keys DataFrame from cached key_rows.
612
- # We need the key column names — get them from the boundary entity.
613
- boundary_entity_cls = _REGISTRY.get(boundary_entity_name)
614
- if boundary_entity_cls is not None:
615
- key_cols = boundary_entity_cls().key_cols
616
- self._boundary_entity_name = boundary_entity_name
617
- self._boundary_keys = pl.DataFrame(
618
- {col: [row[i] for row in key_rows] for i, col in enumerate(key_cols)}
619
- )
620
- else:
621
- # Registry changed since cache was populated — defensive fallback.
622
- logger.warning(
623
- "Boundary cache returned entity name '{}' not found in current registry.",
624
- boundary_entity_name,
625
- )
626
- self._boundary_entity_name = None
627
- self._boundary_keys = None
628
-
629
616
  return geometry
630
617
 
631
618
  @classmethod
@@ -389,13 +389,15 @@ class TestAoIResolveGeometry:
389
389
  assert getattr(aoi.geometry, "equals", lambda x: False)(union)
390
390
 
391
391
  with pytest.raises(ValueError, match="No rows matched the filters"):
392
- AoI(data_root=str(tmp_path), mws_id="999")
392
+ bad_aoi = AoI(data_root=str(tmp_path), mws_id="999")
393
+ _ = bad_aoi.geometry
393
394
 
394
395
  with pytest.raises(
395
396
  EntityValidationError,
396
397
  match="No registered entity can satisfy the filters",
397
398
  ):
398
- AoI(data_root=str(tmp_path), unknown_key="1")
399
+ invalid_aoi = AoI(data_root=str(tmp_path), unknown_key="1")
400
+ _ = invalid_aoi.geometry
399
401
 
400
402
  # Test fallback match by entity name
401
403
  aoi_fallback = AoI(data_root=str(tmp_path), mws="1")
File without changes
File without changes
File without changes
File without changes