core-lens 0.1.dev129__tar.gz → 0.1.dev130__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.dev129 → core_lens-0.1.dev130}/PKG-INFO +1 -1
  2. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/_version.py +2 -2
  3. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/result.py +4 -2
  4. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/export/formats.py +20 -32
  5. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_export.py +5 -2
  6. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/ISSUE_TEMPLATE/blank-proposal.yaml +0 -0
  7. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/ISSUE_TEMPLATE/bug-report.yaml +0 -0
  8. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/ISSUE_TEMPLATE/feature-request.yaml +0 -0
  9. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/pull_request_template.md +0 -0
  10. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/workflows/ci.yml +0 -0
  11. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/workflows/gh-pages.yml +0 -0
  12. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/workflows/pre-release.yml +0 -0
  13. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.github/workflows/release.yml +0 -0
  14. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.gitignore +0 -0
  15. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.gitmessage +0 -0
  16. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.pre-commit-config.yaml +0 -0
  17. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/.python-version +0 -0
  18. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/CONTRIBUTING.md +0 -0
  19. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/LICENSE +0 -0
  20. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/README.md +0 -0
  21. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/SKILLS.md +0 -0
  22. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/README.md +0 -0
  23. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_aoi.py +0 -0
  24. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_export.py +0 -0
  25. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_polars_utils.py +0 -0
  26. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_result.py +0 -0
  27. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_schema.py +0 -0
  28. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_season.py +0 -0
  29. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_spatial.py +0 -0
  30. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/bench_view.py +0 -0
  31. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/benchmarks/run_all.sh +0 -0
  32. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/Makefile +0 -0
  33. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/make.bat +0 -0
  34. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/concepts.md +0 -0
  35. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/conf.py +0 -0
  36. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/export.md +0 -0
  37. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/index.rst +0 -0
  38. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/intro.md +0 -0
  39. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/logging.md +0 -0
  40. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/plots.md +0 -0
  41. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/plugins.md +0 -0
  42. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/queries.md +0 -0
  43. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/quickstart.md +0 -0
  44. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/docs/source/stats.md +0 -0
  45. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/examples/demo_mws.py +0 -0
  46. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/examples/demo_tehsil.py +0 -0
  47. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/hooks/mypy.sh +0 -0
  48. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/hooks/no-parquet-outside-fixtures.sh +0 -0
  49. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/hooks/pytest.sh +0 -0
  50. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/pyproject.toml +0 -0
  51. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/__init__.py +0 -0
  52. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/__main__.py +0 -0
  53. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/aoi.py +0 -0
  54. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/__init__.py +0 -0
  55. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/entity.py +0 -0
  56. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/namespaces/__init__.py +0 -0
  57. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/namespaces/plot.py +0 -0
  58. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/namespaces/stats.py +0 -0
  59. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/base/view.py +0 -0
  60. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/entities/__init__.py +0 -0
  61. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/entities/mws.py +0 -0
  62. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/entities/tehsil.py +0 -0
  63. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/entities/waterbody.py +0 -0
  64. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/export/__init__.py +0 -0
  65. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/py.typed +0 -0
  66. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/schema/__init__.py +0 -0
  67. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/schema/detection.py +0 -0
  68. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/schema/profile.py +0 -0
  69. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/utils/__init__.py +0 -0
  70. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/utils/paths.py +0 -0
  71. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/utils/polars_utils.py +0 -0
  72. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/utils/season.py +0 -0
  73. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/src/core_lens/utils/spatial.py +0 -0
  74. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/fixtures/generate_fixtures.py +0 -0
  75. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/conftest.py +0 -0
  76. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_aoi.py +0 -0
  77. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_entities.py +0 -0
  78. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_entity.py +0 -0
  79. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_main.py +0 -0
  80. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_plot.py +0 -0
  81. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_polars_utils.py +0 -0
  82. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_profile.py +0 -0
  83. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_result.py +0 -0
  84. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_schema_detection.py +0 -0
  85. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_schema_profile.py +0 -0
  86. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_season.py +0 -0
  87. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_season_config.py +0 -0
  88. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_spatial.py +0 -0
  89. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_stats.py +0 -0
  90. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/tests/unit/test_view.py +0 -0
  91. {core_lens-0.1.dev129 → core_lens-0.1.dev130}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: core-lens
3
- Version: 0.1.dev129
3
+ Version: 0.1.dev130
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.dev129'
22
- __version_tuple__ = version_tuple = (0, 1, 'dev129')
21
+ __version__ = version = '0.1.dev130'
22
+ __version_tuple__ = version_tuple = (0, 1, 'dev130')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -100,10 +100,12 @@ class Result:
100
100
  "Call .with_geometry() first to join the static geometry column before extracting GeoDataFrame."
101
101
  )
102
102
  import geopandas as gpd
103
- import shapely.wkb as wkb
103
+ import shapely
104
104
 
105
105
  geometry_col = self.entity.geometry_col
106
- geometries = [wkb.loads(b) for b in self.data[geometry_col].to_list()]
106
+ # Vectorised C-level decode — shapely.from_wkb operates on the whole
107
+ # numpy array at once, avoiding a Python-loop per row.
108
+ geometries = shapely.from_wkb(self.data[geometry_col].to_numpy())
107
109
  geo_series = gpd.GeoSeries(geometries, crs="EPSG:4326")
108
110
  return gpd.GeoDataFrame(
109
111
  self.data.drop(geometry_col).to_pandas(),
@@ -162,12 +162,17 @@ def geoparquet(result: "Result", path: str | pathlib.Path, **kwargs: Any) -> Non
162
162
  def geojson(result: "Result", path: str | pathlib.Path, **kwargs: Any) -> None:
163
163
  """Export the result data to a GeoJSON file.
164
164
 
165
- This function uses DuckDB to write the data frame with spatial extensions.
165
+ This function uses GeoPandas to write the data frame. It prefers the
166
+ ``pyogrio`` engine (faster, C-backed) when available and falls back to
167
+ ``fiona`` automatically. The ``engine`` kwarg can override this:
168
+ ``engine="fiona"`` or ``engine="pyogrio"``.
166
169
 
167
170
  Args:
168
171
  result (Result): The Result object to export.
169
172
  path (str | pathlib.Path): Destination path for the GeoJSON file.
170
- **kwargs (Any): Additional options to pass to DuckDB's COPY statement.
173
+ **kwargs (Any): Additional options forwarded to
174
+ ``GeoDataFrame.to_file()`` (e.g. ``driver="GeoJSON"``,
175
+ ``engine="pyogrio"``).
171
176
 
172
177
  Example:
173
178
  >>> from core_lens.export import geojson
@@ -182,37 +187,20 @@ def geojson(result: "Result", path: str | pathlib.Path, **kwargs: Any) -> None:
182
187
  "Call .with_geometry() first to join the static geometry column before exporting to geojson."
183
188
  )
184
189
 
185
- df = result.df()
186
- geom_col = result.entity.geometry_col
187
-
188
- cols = []
189
- for c in df.columns:
190
- if c == geom_col:
191
- cols.append(f'ST_GeomFromWKB("{c}") AS "{c}"')
192
- else:
193
- cols.append(f'"{c}"')
194
-
195
- select_clause = ", ".join(cols)
196
-
197
- conn = _get_duckdb_conn()
190
+ gdf = result.gdf()
198
191
 
199
- path_str = str(path).replace("'", "''")
192
+ # Resolve write engine: prefer pyogrio (C-backed, no GDAL Python overhead),
193
+ # fall back to fiona if not installed.
194
+ engine = kwargs.pop("engine", None)
195
+ if engine is None:
196
+ try:
197
+ import pyogrio as _ # type: ignore[import-untyped] # noqa: F401
200
198
 
201
- options = ["FORMAT GDAL", "DRIVER 'GeoJSON'"]
202
- for k, v in kwargs.items():
203
- if isinstance(v, bool):
204
- options.append(f"{k.upper()} {'TRUE' if v else 'FALSE'}")
205
- elif isinstance(v, str):
206
- options.append(f"{k.upper()} '{v}'")
207
- else:
208
- options.append(f"{k.upper()} {v}")
199
+ engine = "pyogrio"
200
+ except ModuleNotFoundError:
201
+ engine = "fiona"
209
202
 
210
- options_str = ", ".join(options)
203
+ # Always write as GeoJSON unless the caller overrides driver.
204
+ kwargs.setdefault("driver", "GeoJSON")
211
205
 
212
- query = f"""
213
- COPY (
214
- SELECT {select_clause}
215
- FROM df
216
- ) TO '{path_str}' ({options_str});
217
- """
218
- conn.execute(query)
206
+ gdf.to_file(path, engine=engine, **kwargs)
@@ -152,8 +152,10 @@ def test_geoparquet_options_partition(
152
152
 
153
153
 
154
154
  def test_geojson_options(sample_result: Result, tmp_path: pathlib.Path) -> None:
155
+ """Unknown kwargs are forwarded to the write engine; unrecognised ones raise
156
+ ValueError (pyogrio) rather than silently being ignored. The test simply
157
+ verifies the call path is exercised without an unexpected exception type."""
155
158
  result_with_geom = sample_result.with_geometry()
156
- import duckdb
157
159
 
158
160
  out_path = tmp_path / "out_opts.json"
159
161
  try:
@@ -165,5 +167,6 @@ def test_geojson_options(sample_result: Result, tmp_path: pathlib.Path) -> None:
165
167
  flag=False,
166
168
  number=10,
167
169
  )
168
- except duckdb.BinderException:
170
+ except ValueError:
171
+ # pyogrio rejects unrecognised driver options — that's expected.
169
172
  pass
File without changes
File without changes
File without changes
File without changes