core-lens 0.1.dev183__tar.gz → 0.1.dev186__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.
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/PKG-INFO +2 -2
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/SKILLS.md +38 -10
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/_version.py +2 -2
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/aoi.py +4 -2
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/entity.py +2 -2
- core_lens-0.1.dev186/src/core_lens/entities/farms.py +63 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/utils/polars_utils.py +28 -1
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/utils/spatial.py +3 -2
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/pull_request_template.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/workflows/ci.yml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/workflows/gh-pages.yml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/workflows/pre-release.yml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.github/workflows/release.yml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.gitignore +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.gitmessage +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.pre-commit-config.yaml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/.python-version +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/CONTRIBUTING.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/LICENSE +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/README.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/README.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_aoi.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_entity.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_export.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_polars_utils.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_result.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_schema.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_season.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_spatial.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/bench_view.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/benchmarks/run_all.sh +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/Makefile +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/make.bat +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/concepts.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/conf.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/export.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/index.rst +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/intro.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/logging.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/plots.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/plugins.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/queries.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/quickstart.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/docs/source/stats.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/examples/demo_mws.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/examples/demo_tehsil.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/hooks/mypy.sh +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/hooks/no-parquet-outside-fixtures.sh +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/hooks/pytest.sh +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/pyproject.toml +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/__main__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/namespaces/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/namespaces/plot.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/namespaces/stats.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/result.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/base/view.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/entities/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/entities/mws.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/entities/tehsil.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/entities/waterbody.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/export/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/export/formats.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/py.typed +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/schema/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/schema/detection.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/schema/profile.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/utils/__init__.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/utils/paths.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/src/core_lens/utils/season.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/fixtures/generate_fixtures.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/conftest.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_aoi.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_entities.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_entity.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_export.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_main.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_plot.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_polars_utils.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_profile.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_result.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_schema_detection.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_schema_profile.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_season.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_season_config.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_spatial.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_stats.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/tests/unit/test_view.py +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/usage.md +0 -0
- {core_lens-0.1.dev183 → core_lens-0.1.dev186}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: core-lens
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.dev186
|
|
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
|
|
@@ -57,7 +57,9 @@ aoi = AoI(DATA_ROOT, bbox=(76.0, 31.0, 78.0, 33.0))
|
|
|
57
57
|
aoi = AoI(DATA_ROOT, mws_id="13_551")
|
|
58
58
|
|
|
59
59
|
# Optional: Override default seasons (kharif, rabi, zaid)
|
|
60
|
-
custom_seasons = SeasonConfig(
|
|
60
|
+
custom_seasons = SeasonConfig(
|
|
61
|
+
kharif=("06-01", "10-15"), rabi=("10-16", "02-28"), zaid=("03-01", "05-31")
|
|
62
|
+
)
|
|
61
63
|
aoi_custom = AoI(DATA_ROOT, tehsil="Pangi", seasons=custom_seasons)
|
|
62
64
|
```
|
|
63
65
|
|
|
@@ -78,6 +80,7 @@ spatial_filtered = mws_view.spatial_filter(bbox=(76.5, 31.5, 77.5, 32.5))
|
|
|
78
80
|
# Temporal Filtering
|
|
79
81
|
# Note: You can filter by exact years, a range of years, or predefined seasons.
|
|
80
82
|
from core_lens.base.view import Season
|
|
83
|
+
|
|
81
84
|
temporal_view = mws_view.between(year=(2020, 2022), season=Season.KHARIF)
|
|
82
85
|
current_view = mws_view.between(season=Season.CURRENT)
|
|
83
86
|
```
|
|
@@ -98,9 +101,9 @@ annual_result = temporal_view.annual.materialise()
|
|
|
98
101
|
|
|
99
102
|
```python
|
|
100
103
|
# To access underlying data structures:
|
|
101
|
-
df = static_result.df()
|
|
102
|
-
lazy_df = static_result.lazy()
|
|
103
|
-
gdf = static_result.gdf()
|
|
104
|
+
df = static_result.df() # Polars DataFrame (Zero-copy)
|
|
105
|
+
lazy_df = static_result.lazy() # Polars LazyFrame
|
|
106
|
+
gdf = static_result.gdf() # GeoPandas GeoDataFrame (Heavy!)
|
|
104
107
|
```
|
|
105
108
|
|
|
106
109
|
## 5. Computation and Data Manipulation (Result API)
|
|
@@ -145,30 +148,53 @@ result.stats.describe(columns=["ndvi", "rainfall"])
|
|
|
145
148
|
|
|
146
149
|
# Correlation (pearson, spearman, kendall)
|
|
147
150
|
from core_lens.base.namespaces.stats import CorrelateMethod
|
|
148
|
-
|
|
151
|
+
|
|
152
|
+
result.stats.correlate(
|
|
153
|
+
columns=["ndvi", "rainfall"], method=CorrelateMethod.PEARSON, across="entity"
|
|
154
|
+
)
|
|
149
155
|
|
|
150
156
|
# Hypothesis Testing (t-test, mann-whitney, wilcoxon, ks, chi-square)
|
|
151
157
|
from core_lens.base.namespaces.stats import TestMethod
|
|
152
|
-
|
|
158
|
+
|
|
159
|
+
result.stats.test(
|
|
160
|
+
column="cropping_intensity",
|
|
161
|
+
groups="temperature_zone",
|
|
162
|
+
method=TestMethod.MANN_WHITNEY,
|
|
163
|
+
)
|
|
153
164
|
|
|
154
165
|
# Change Detection (absolute, percentage, trend)
|
|
155
166
|
from core_lens.base.namespaces.stats import ChangeMethod
|
|
156
|
-
|
|
167
|
+
|
|
168
|
+
result.stats.change(
|
|
169
|
+
column="tree_cover",
|
|
170
|
+
from_period=2018,
|
|
171
|
+
to_period=2023,
|
|
172
|
+
method=ChangeMethod.PERCENTAGE,
|
|
173
|
+
)
|
|
157
174
|
|
|
158
175
|
# Anomaly Detection
|
|
159
176
|
from core_lens.base.namespaces.stats import AnomalyTsMethod, AnomalyCrossMethod
|
|
177
|
+
|
|
160
178
|
# Mode 1: Cross-sectional (zscore, iqr, percentile, threshold)
|
|
161
|
-
result.stats.anomaly(
|
|
179
|
+
result.stats.anomaly(
|
|
180
|
+
column="ndvi",
|
|
181
|
+
mode="cross_sectional",
|
|
182
|
+
method=AnomalyCrossMethod.ZSCORE,
|
|
183
|
+
baseline=(2010, 2020),
|
|
184
|
+
)
|
|
162
185
|
# Mode 2: Time-series (stl, cusum, mad)
|
|
163
|
-
result.stats.anomaly(
|
|
186
|
+
result.stats.anomaly(
|
|
187
|
+
column="ndvi", mode="timeseries", method=AnomalyTsMethod.STL, baseline=(2010, 2018)
|
|
188
|
+
)
|
|
164
189
|
|
|
165
190
|
# Similarity Matching (euclidean, cosine, mahalanobis, manhattan)
|
|
166
191
|
from core_lens.base.namespaces.stats import SimilarityMethod
|
|
192
|
+
|
|
167
193
|
result.stats.similarity(
|
|
168
194
|
target="13_551",
|
|
169
195
|
columns={"rainfall": ("annual", {"year": 2018})},
|
|
170
196
|
method=SimilarityMethod.EUCLIDEAN,
|
|
171
|
-
top_n=10
|
|
197
|
+
top_n=10,
|
|
172
198
|
)
|
|
173
199
|
```
|
|
174
200
|
|
|
@@ -214,6 +240,7 @@ You can extend core-lens with custom entities by subclassing `BaseEntity`.
|
|
|
214
240
|
```python
|
|
215
241
|
from core_lens.base import BaseEntity
|
|
216
242
|
|
|
243
|
+
|
|
217
244
|
class CustomEntity(BaseEntity):
|
|
218
245
|
@property
|
|
219
246
|
def key_cols(self) -> list[str]:
|
|
@@ -231,6 +258,7 @@ class CustomEntity(BaseEntity):
|
|
|
231
258
|
def annual_path(self) -> str | None:
|
|
232
259
|
return "custom/annual.parquet"
|
|
233
260
|
|
|
261
|
+
|
|
234
262
|
AoI.register(CustomEntity)
|
|
235
263
|
```
|
|
236
264
|
|
|
@@ -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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 1, '
|
|
21
|
+
__version__ = version = '0.1.dev186'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 'dev186')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -20,7 +20,7 @@ from loguru import logger
|
|
|
20
20
|
|
|
21
21
|
from core_lens.base.entity import BaseEntity, EntityValidationError
|
|
22
22
|
from core_lens.utils.paths import is_cloud_uri
|
|
23
|
-
from core_lens.utils.polars_utils import cached_read_schema
|
|
23
|
+
from core_lens.utils.polars_utils import cached_read_schema, parquet_scan_path
|
|
24
24
|
|
|
25
25
|
if TYPE_CHECKING:
|
|
26
26
|
from core_lens.base.view import View
|
|
@@ -225,7 +225,9 @@ def _cached_resolve_boundary(
|
|
|
225
225
|
geom_col = schema.geometry_col
|
|
226
226
|
static_path = candidate._resolve(candidate.static_path)
|
|
227
227
|
|
|
228
|
-
lf = pl.scan_parquet(
|
|
228
|
+
lf = pl.scan_parquet(
|
|
229
|
+
parquet_scan_path(static_path), storage_options=storage_options or None
|
|
230
|
+
)
|
|
229
231
|
filter_expr = pl.lit(True)
|
|
230
232
|
schema_types = cached_read_schema(static_path, storage_options)
|
|
231
233
|
for col, val in entity_kwargs.items():
|
|
@@ -29,7 +29,7 @@ from core_lens.utils.spatial import (
|
|
|
29
29
|
build_bbox_index,
|
|
30
30
|
exact_spatial_filter,
|
|
31
31
|
)
|
|
32
|
-
from core_lens.utils.polars_utils import cached_read_schema
|
|
32
|
+
from core_lens.utils.polars_utils import cached_read_schema, parquet_scan_path
|
|
33
33
|
|
|
34
34
|
if TYPE_CHECKING:
|
|
35
35
|
import shapely
|
|
@@ -475,7 +475,7 @@ class BaseEntity(ABC):
|
|
|
475
475
|
)
|
|
476
476
|
|
|
477
477
|
# --- Combine attribute and spatial filters into a single lazy pipeline ---
|
|
478
|
-
lf = pl.scan_parquet(static)
|
|
478
|
+
lf = pl.scan_parquet(parquet_scan_path(static))
|
|
479
479
|
|
|
480
480
|
if attr_kwargs:
|
|
481
481
|
filter_expr = pl.lit(True)
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from core_lens.base import BaseEntity
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FarmEntity(BaseEntity):
|
|
5
|
+
"""Farms entity.
|
|
6
|
+
|
|
7
|
+
Backed by:
|
|
8
|
+
- Static: farms/static
|
|
9
|
+
- Annual: farms/annual
|
|
10
|
+
- SubAnnual: farms/sub_annual
|
|
11
|
+
|
|
12
|
+
All paths are relative to the AoI ``data_root``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
@property
|
|
16
|
+
def key_cols(self) -> list[str]:
|
|
17
|
+
"""Get the property value.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
list[str]: The key columns for the entity.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
return ["farm_id"]
|
|
24
|
+
|
|
25
|
+
@property
|
|
26
|
+
def geometry_col(self) -> str:
|
|
27
|
+
"""Get the property value.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
str: The geometry column name.
|
|
31
|
+
|
|
32
|
+
"""
|
|
33
|
+
return "geometry"
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def static_path(self) -> str:
|
|
37
|
+
"""Get the property value.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
str: The relative path to the static data.
|
|
41
|
+
|
|
42
|
+
"""
|
|
43
|
+
return "farms/static.parquet"
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def annual_path(self) -> str:
|
|
47
|
+
"""Get the property value.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: The relative path to the annual data.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
return "farms/annual.parquet"
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def sub_annual_path(self) -> str:
|
|
57
|
+
"""Get the property value.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
str: The relative path to the monthly data.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
return "farms/sub_annual.parquet"
|
|
@@ -7,6 +7,8 @@ import functools
|
|
|
7
7
|
|
|
8
8
|
import polars as pl
|
|
9
9
|
|
|
10
|
+
import os
|
|
11
|
+
|
|
10
12
|
_GPU_AVAILABLE: bool | None = None # None = not yet probed
|
|
11
13
|
|
|
12
14
|
|
|
@@ -16,9 +18,34 @@ def _so_key(storage_options: dict[str, Any] | None) -> tuple[tuple[str, Any], ..
|
|
|
16
18
|
return tuple(sorted(storage_options.items()))
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
def parquet_scan_path(path: str) -> str:
|
|
22
|
+
"""Return a glob-safe path for ``pl.scan_parquet``.
|
|
23
|
+
|
|
24
|
+
When *path* is a directory polars will reject it if the directory contains
|
|
25
|
+
files with mixed extensions (e.g. a ``data_dictionary.csv`` sitting
|
|
26
|
+
alongside Hive-partitioned ``.parquet`` files). This helper coerces a bare
|
|
27
|
+
directory to ``<path>/**/*.parquet`` so only Parquet files are matched.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
path (str): Filesystem path or cloud URI to a Parquet file or directory.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
str: The original path if it already points to a file, otherwise
|
|
34
|
+
``<path>/**/*.parquet``.
|
|
35
|
+
"""
|
|
36
|
+
if os.path.isdir(path):
|
|
37
|
+
return os.path.join(path, "**", "*.parquet")
|
|
38
|
+
return path
|
|
39
|
+
|
|
40
|
+
|
|
19
41
|
@functools.cache
|
|
20
42
|
def _cached_schema_internal(path: str, so_key: tuple[tuple[str, Any], ...]) -> Any:
|
|
21
|
-
|
|
43
|
+
# Coerce bare directory paths to a *.parquet glob so polars doesn't raise
|
|
44
|
+
# InvalidOperationError when the directory contains mixed-extension files
|
|
45
|
+
# (e.g. data_dictionary.csv alongside Hive-partitioned .parquet files).
|
|
46
|
+
return pl.scan_parquet(
|
|
47
|
+
parquet_scan_path(path), storage_options=dict(so_key) or None
|
|
48
|
+
).collect_schema()
|
|
22
49
|
|
|
23
50
|
|
|
24
51
|
def cached_read_schema(path: str, storage_options: dict[str, Any] | None = None) -> Any:
|
|
@@ -12,6 +12,7 @@ import shapely
|
|
|
12
12
|
from loguru import logger
|
|
13
13
|
|
|
14
14
|
from core_lens.utils.paths import is_cloud_uri, path_exists, resolve_fs_and_path
|
|
15
|
+
from core_lens.utils.polars_utils import parquet_scan_path
|
|
15
16
|
|
|
16
17
|
if TYPE_CHECKING:
|
|
17
18
|
pass
|
|
@@ -361,7 +362,7 @@ def exact_spatial_filter(
|
|
|
361
362
|
|
|
362
363
|
# Build a lazy scan and push down an inner join to load only required geometries.
|
|
363
364
|
full_df = (
|
|
364
|
-
pl.scan_parquet(static_path)
|
|
365
|
+
pl.scan_parquet(parquet_scan_path(static_path))
|
|
365
366
|
.select(key_cols + [geometry_col])
|
|
366
367
|
.join(candidates.select(key_cols).lazy(), on=key_cols, how="semi")
|
|
367
368
|
.collect()
|
|
@@ -684,7 +685,7 @@ def point_in_entities(
|
|
|
684
685
|
static_path: str = entity._resolve(entity.static_path)
|
|
685
686
|
|
|
686
687
|
geom_df = (
|
|
687
|
-
pl.scan_parquet(static_path)
|
|
688
|
+
pl.scan_parquet(parquet_scan_path(static_path))
|
|
688
689
|
.select(key_cols + [geom_col])
|
|
689
690
|
.join(candidates.select(key_cols).lazy(), on=key_cols, how="semi")
|
|
690
691
|
.collect()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|