core-lens 0.1.dev136__tar.gz → 0.1.dev138__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.dev136 → core_lens-0.1.dev138}/PKG-INFO +1 -1
  2. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/export.md +8 -2
  3. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/intro.md +3 -1
  4. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/queries.md +3 -2
  5. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/quickstart.md +4 -4
  6. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/_version.py +2 -2
  7. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/schema/detection.py +8 -0
  8. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/utils/spatial.py +17 -4
  9. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
  10. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  11. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
  12. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/pull_request_template.md +0 -0
  13. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/workflows/ci.yml +0 -0
  14. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/workflows/gh-pages.yml +0 -0
  15. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/workflows/pre-release.yml +0 -0
  16. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.github/workflows/release.yml +0 -0
  17. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.gitignore +0 -0
  18. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.gitmessage +0 -0
  19. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.pre-commit-config.yaml +0 -0
  20. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/.python-version +0 -0
  21. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/CONTRIBUTING.md +0 -0
  22. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/LICENSE +0 -0
  23. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/README.md +0 -0
  24. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/SKILLS.md +0 -0
  25. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/README.md +0 -0
  26. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_aoi.py +0 -0
  27. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_export.py +0 -0
  28. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_polars_utils.py +0 -0
  29. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_result.py +0 -0
  30. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_schema.py +0 -0
  31. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_season.py +0 -0
  32. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_spatial.py +0 -0
  33. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/bench_view.py +0 -0
  34. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/benchmarks/run_all.sh +0 -0
  35. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/Makefile +0 -0
  36. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/make.bat +0 -0
  37. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/concepts.md +0 -0
  38. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/conf.py +0 -0
  39. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/index.rst +0 -0
  40. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/logging.md +0 -0
  41. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/plots.md +0 -0
  42. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/plugins.md +0 -0
  43. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/docs/source/stats.md +0 -0
  44. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/examples/demo_mws.py +0 -0
  45. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/examples/demo_tehsil.py +0 -0
  46. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/hooks/mypy.sh +0 -0
  47. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/hooks/no-parquet-outside-fixtures.sh +0 -0
  48. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/hooks/pytest.sh +0 -0
  49. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/pyproject.toml +0 -0
  50. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/__init__.py +0 -0
  51. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/__main__.py +0 -0
  52. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/aoi.py +0 -0
  53. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/__init__.py +0 -0
  54. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/entity.py +0 -0
  55. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/namespaces/__init__.py +0 -0
  56. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/namespaces/plot.py +0 -0
  57. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/namespaces/stats.py +0 -0
  58. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/result.py +0 -0
  59. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/base/view.py +0 -0
  60. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/entities/__init__.py +0 -0
  61. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/entities/mws.py +0 -0
  62. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/entities/tehsil.py +0 -0
  63. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/entities/waterbody.py +0 -0
  64. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/export/__init__.py +0 -0
  65. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/export/formats.py +0 -0
  66. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/py.typed +0 -0
  67. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/schema/__init__.py +0 -0
  68. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/schema/profile.py +0 -0
  69. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/utils/__init__.py +0 -0
  70. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/utils/paths.py +0 -0
  71. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/utils/polars_utils.py +0 -0
  72. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/src/core_lens/utils/season.py +0 -0
  73. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/fixtures/generate_fixtures.py +0 -0
  74. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/conftest.py +0 -0
  75. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_aoi.py +0 -0
  76. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_entities.py +0 -0
  77. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_entity.py +0 -0
  78. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_export.py +0 -0
  79. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_main.py +0 -0
  80. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_plot.py +0 -0
  81. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_polars_utils.py +0 -0
  82. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_profile.py +0 -0
  83. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_result.py +0 -0
  84. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_schema_detection.py +0 -0
  85. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_schema_profile.py +0 -0
  86. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_season.py +0 -0
  87. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_season_config.py +0 -0
  88. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_spatial.py +0 -0
  89. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_stats.py +0 -0
  90. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/tests/unit/test_view.py +0 -0
  91. {core_lens-0.1.dev136 → core_lens-0.1.dev138}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-lens
3
- Version: 0.1.dev136
3
+ Version: 0.1.dev138
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
@@ -41,7 +41,13 @@ geoparquet(
41
41
 
42
42
  # Export to GeoJSON
43
43
  geojson(spatial_res, "output.json")
44
+
45
+ # Export to newline-delimited GeoJSON (GeoJSONSeq)
46
+ geojson(spatial_res, "output.ndjson", driver="GeoJSONSeq")
44
47
  ```
45
48
 
46
- > [!NOTE]
47
- > Exporting to geospatial formats automatically uses DuckDB to cast WKB geometries to proper spatial types via the `spatial` extension. CoreLens handles the installation and loading of the extension seamlessly under the hood.
49
+ ```{note}
50
+ Exporting to **GeoParquet** uses DuckDB to cast WKB geometries to proper spatial types via the `spatial` extension. CoreLens handles the installation and loading of the extension seamlessly under the hood.
51
+
52
+ Exporting to **GeoJSON** is fully natively streamed directly from Polars and Shapely without invoking GDAL or DuckDB, making it extremely fast for large datasets. Driver-specific kwargs for GeoJSON are ignored.
53
+ ```
@@ -13,4 +13,6 @@ CoreLens provides a unified interface over microwatersheds, administrative bound
13
13
  - **Temporal & Seasonal Awareness**: Native support for agronomic seasons (Kharif, Rabi, Zaid) and time-range filtering.
14
14
  - **Spatial Statistics & Analysis**: Built-in methods for anomaly detection, spatial similarity, temporal correlation, and hypothesis testing.
15
15
  - **Interactive Visualisation**: Generate interactive maps using Lonboard with zero-copy GeoArrow rendering directly from Polars, and timeseries/scatter plots using Bokeh.
16
- - **High-Performance Exports**: Export results directly to Parquet, JSON, CSV, or spatially-enabled GeoParquet and GeoJSON using integrated DuckDB spatial extensions.
16
+ - **Cloud & Local Data**: Native support for reading Parquet data from local filesystems or directly from cloud storage (e.g., `s3://`).
17
+ - **High-Performance Exports**: Export results directly to Parquet, JSON, CSV, or spatially-enabled GeoParquet and GeoJSON (with a fast streaming writer).
18
+ - **Advanced Caching**: Transparently caches boundaries and pre-computes spatial indices (via on-disk sidecars) to achieve sub-second query initialisation times.
@@ -58,5 +58,6 @@ joined_view = aoi.mws.spatial_join(
58
58
  # res = joined_view.annual
59
59
  ```
60
60
 
61
- > [!NOTE]
62
- > Cross-entity spatial join execution (materialisation) is currently under development and will be added in a subsequent release. Calling materialisation methods (like `.annual`, `.static`, or `.fortnightly`) on a joined view will raise a `NotImplementedError` in the current version.
61
+ ```{note}
62
+ Cross-entity spatial join execution (materialisation) is currently under development and will be added in a subsequent release. Calling materialisation methods (like `.annual`, `.static`, or `.fortnightly`) on a joined view will raise a `NotImplementedError` in the current version.
63
+ ```
@@ -25,18 +25,18 @@ AoI.register(MWSEntity)
25
25
  AoI.register(TehsilEntity)
26
26
  ```
27
27
 
28
- Create an `AoI` (Area of Interest) by specifying the `data_root` and your boundary (either by name, bounding box, or Shapely geometry):
28
+ Create an `AoI` (Area of Interest) by specifying the `data_root` (which can be a local path or a cloud URI like `s3://bucket/data`) and your boundary (either by name, bounding box, or Shapely geometry):
29
29
 
30
30
  ```python
31
- # Create an AoI scoped to a specific Tehsil
31
+ # Create an AoI scoped to a specific Tehsil using a local path
32
32
  aoi = AoI(
33
33
  data_root="/path/to/data",
34
34
  tehsil="Pangi"
35
35
  )
36
36
 
37
- # Or scope it directly to a specific list of entity IDs
37
+ # Or scope it directly to a specific list of entity IDs using an S3 bucket
38
38
  aoi_specific = AoI(
39
- data_root="/path/to/data",
39
+ data_root="s3://my-cloud-bucket/core-lens-data",
40
40
  mws_id=["13_001", "13_002"]
41
41
  )
42
42
 
@@ -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.dev136'
22
- __version_tuple__ = version_tuple = (0, 1, 'dev136')
21
+ __version__ = version = '0.1.dev138'
22
+ __version_tuple__ = version_tuple = (0, 1, 'dev138')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -178,6 +178,14 @@ def _infer_bbox_cols(schema: pl.Schema) -> tuple[str, str, str, str] | None:
178
178
  for pattern in _BBOX_PATTERNS:
179
179
  if all(col in schema for col in pattern):
180
180
  return pattern
181
+
182
+ for col_name, dtype in schema.items():
183
+ if isinstance(dtype, pl.Struct):
184
+ struct_fields = [f.name for f in dtype.fields]
185
+ for pattern in _BBOX_PATTERNS:
186
+ if all(f in struct_fields for f in pattern):
187
+ return tuple(f"{col_name}.{f}" for f in pattern) # type: ignore
188
+
181
189
  return None
182
190
 
183
191
 
@@ -173,14 +173,27 @@ def build_bbox_index(
173
173
  _so = storage_options or {}
174
174
  if bbox_cols is not None:
175
175
  logger.debug("Using pre-computed bbox columns: {}", bbox_cols)
176
- cols_to_read = key_cols + list(bbox_cols)
176
+ base_bbox_cols = list(dict.fromkeys(c.split(".")[0] for c in bbox_cols))
177
+ cols_to_read = key_cols + base_bbox_cols
177
178
  df = pl.read_parquet(
178
179
  static_path, columns=cols_to_read, storage_options=_so or None
179
180
  )
180
181
  minx_col, miny_col, maxx_col, maxy_col = bbox_cols
181
- return df.rename(
182
- {minx_col: "minx", miny_col: "miny", maxx_col: "maxx", maxy_col: "maxy"}
183
- )
182
+
183
+ for alias, col_path in zip(
184
+ ["minx", "miny", "maxx", "maxy"],
185
+ [minx_col, miny_col, maxx_col, maxy_col],
186
+ ):
187
+ parts = col_path.split(".")
188
+ if len(parts) == 1:
189
+ df = df.rename({col_path: alias})
190
+ else:
191
+ expr = pl.col(parts[0])
192
+ for p in parts[1:]:
193
+ expr = expr.struct.field(p)
194
+ df = df.with_columns(expr.alias(alias))
195
+
196
+ return df.select(key_cols + ["minx", "miny", "maxx", "maxy"])
184
197
 
185
198
  cols_to_read = key_cols + [geometry_col]
186
199
  if geometry_type == "latlon":
File without changes
File without changes
File without changes
File without changes