release: GeoBrix 0.5.0 — virtual tiles, v2 tile struct, CRS families - #72
Draft
mjohns-databricks wants to merge 501 commits into
Draft
release: GeoBrix 0.5.0 — virtual tiles, v2 tile struct, CRS families#72mjohns-databricks wants to merge 501 commits into
mjohns-databricks wants to merge 501 commits into
Conversation
added 30 commits
August 4, 2026 11:36
…rx+tests Docker lint (the CI-authoritative isort/black/flake8) surfaced a batch of pre-existing violations that blocked the branch push, plus two orphaned by R2's removal of from_epsg call sites: - F401 unused imports: edit.py (rasterio), open_tile.py (rasterio.crs), removed after R2 dropped their from_epsg uses; plus resample.py (np), functions.py (_raster_field), stac/_download.py (np), bench/compression_sweep.py, and several test files. - F841 dead locals (compression_sweep decoded_bytes, test_compression_sites pred, test_compress_option ds), E226 spacing, E402 post-importorskip imports (add # noqa: E402 per the repo convention), F811 redefined MemoryFile and E402 mid-file imports in test_pending_instructions (hoisted to the top). - black line-wrapping on 7 test files whose committed form failed Docker black. Import removals smoke-tested; 111 affected light tests green; full Docker python lint now passes (isort + black + flake8, 445 files). Co-authored-by: Isaac
Follow-on to Spec R2. Standardizes two CRS roles for every geometry argument
(source vs target/output), maps them to a GeoBrix-wide naming rule (out_srid/
out_crs for output projection; srid/crs/clip_crs for input source), and closes
the remaining RasterX gaps: clip_crs on rst_clip Column API, crs on rst_sample/
rst_viewshed, out_srid/out_crs on the produce-new-raster + grid rasterize/spec
functions (with Rule-2 geometry reprojection), the rst_{h3,quadbin}_rastertogrid
silent-4326 correctness fix (auto-reproject to grid-native + crs override), and
the heavy reader clipCrs option. Never-error invariant: absent CRS always
degrades to a sensible assumption; only explicit conflict or explicit garbage
raises.
Co-authored-by: Isaac
…GridX Audited every function directly against Scala + Python sources: the recon table's tier labels were wrong — rst_sample, rst_viewshed, and rst_gridfrompoints(+_agg) are ALL both-tiers, not light-only. Added a verified tier-map section so the implementation plan isn't misled; every function is both-tiers except rst_h3_gridspec (light-only DataFrame helper). Added Group E: relocate gbx_h3_cell_bbox (a gbx_h3_* GridX-namespace function with no raster dependency) from the raster package to GridX, while doing its out_srid/out_crs CRS work here since it serves the rst_h3_* 100% goal (Q11-3). Co-authored-by: Isaac
…r cache Q12: the explicit srid/crs source param is a PER-GEOM fallback, not a per-row error — a geometry with an embedded SRID uses it, a plain-WKB geometry uses the param, and a mixed column (some EWKB, some WKB) is first-class. Only the call-level both-source-params / both-out-params / unresolvable-string conditions throw. Supersedes the earlier Q8 EWKB+param error. Q13/Q14: add a Performance section — a thread-local, LRU-bounded transformer cache in the centralized helpers (crs.get_transformer / SpatialRefOps.getTransformer), keyed by canonical CRS pair, always_xy, size _TRANSFORMER_CACHE_SIZE=128 (120 UTM zones + 4326/27700/3857 + headroom). No user-facing strict/permissive mode; correctness identical with or without the cache. Added mixed-column + cache tests. Co-authored-by: Isaac
The nuance of all decisions (two-role source/output model plus out_ naming, the per-geom source fallback for mixed columns, the never-error invariant and error matrix, produce-new-raster reprojection, rastertogrid auto-reproject, the transformer-cache perf note, and per-function cross-links) must land user-visible on coordinate-reference-systems.mdx, backed by runnable doc tests so the page cannot drift from behavior. Added as an itemized Group F deliverable with docs-test requirements, and elevated to goal (d). Co-authored-by: Isaac
Group F item 7: a single tabular enumeration of every CRS-touching function across all of GeoBrix (Package, Function, Tiers, CRS params, Role, CRS-in behavior, Notes). Seeded here with the complete RasterX surface (Groups A-E plus the shipped R/R2 functions); VectorX and GridX rows are appended by their own follow-on specs, which inherit the fixed column shape and source/output-role vocabulary. Every table row links to the function reference entry and vice versa. Recorded the append contract in the out-of-scope notes for both follow-on specs. Co-authored-by: Isaac
Task-by-task plan for the RasterX-CRS-100 spec. Two foundational tasks (light + heavy transformer cache + resolve_source_crs / resolveSourceSR), then Group C rastertogrid correctness fix, Group A source params (both tiers), Group B out_srid/out_crs + Rule-2 reprojection, gbx_h3_cell_bbox relocation to GridX, heavy reader clipCrs, and the CRS docs page + master cross-reference table. Shared-core code given verbatim; per-function param threading enumerated. Self-review covers spec coverage, arity-bump and rename risks, nearest-neighbour warp, and the light-tier no-heavy-deps constraint.
Task 1 of RasterX CRS to 100%. Two shared primitives in the light resolver: - get_transformer(src, dst): thread-local, LRU-bounded (_TRANSFORMER_CACHE_SIZE =128) pyproj Transformer keyed by canonical CRS pair, always_xy=True. Reuses one transformer per thread per CRS pair instead of rebuilding per row. - resolve_source_crs(embedded_srid, srid, crs): Rule-1 per-geom source-CRS resolution — embedded SRID wins; else single explicit param; both -> error; neither -> None. Mixed-column safe (embedded present ignores the param, no error). Consumed by Tasks 3-7. 34 tests green (7 new + crs_resolve/crs_ops regression). Co-authored-by: Isaac
…tion) Task 2 of RasterX CRS to 100%, heavy mirror of Task 1: - getTransformer(srcKey, dstKey): thread-local, LRU-bounded (cap 128) CoordinateTransformation keyed by canonical CRS pair. Reuses one transformation per thread per pair instead of rebuilding per row. - resolveSourceSR(embeddedSrid, srid, crs): Rule-1 per-geom source resolution — embedded SRID wins; else single explicit param; both -> error; neither -> None. Mixed-column safe. Consumed by Tasks 3/5/6. 12/12 SpatialRefOpsTest green; scalastyle clean. Co-authored-by: Isaac
Three pre-existing failures on branch/0.5.0 surfaced by CI (run 30925679475): - test_set_srid_rejects_nonpositive asserted set_srid(0) raises, but Spec R2 changed semantics to srid>=0 (0 clears the CRS). Split into test_set_srid_rejects_negative (-1 raises) + test_set_srid_zero_clears_crs. - bench REGISTRY was missing FnSpecs for rst_crs/rst_setcrs/rst_transformcrs (added as registered fns in Sub-spec R but never benched) -> full-set count 126 != 129 registered. Added the three specs (transformcrs core=False; the warp family's core representative is rst_transform). - test_pending_instructions _a_tif() IndexError'd when neither the sample-data bundle nor the Maven MODIS fixture is present (lightweight CI has no data); now pytest.skip() instead of crashing. 136 tests green locally (bench spec + core_ops + pending_instructions). Co-authored-by: Isaac
…tive (Group C) Task 3 of RasterX CRS to 100%. H3/quadbin raster-to-grid silently read a non-4326 raster's easting/northing as lon/lat (wrong cell assignments). Now the raster is auto-reprojected to grid-native EPSG:4326 (nearest-neighbour, so pixel statistics are never interpolated) when it carries a differing CRS — mirroring what BNG already does for 27700 — with an optional source override for a CRS-less-but-known raster. A CRS-less raster with no override is assumed grid-native and processed unchanged (never errors). Light: gridagg.raster_to_grid gains crs param + _warp_to_4326_if_needed; BNG path gains the same override. Heavy: shared GridReprojection.toGridCrs guard wired into RST_H3_RasterToGrid + RST_Quadbin_RasterToGrid (releases the warped Dataset). Light 5/5 new tests; heavy grid suites 25/25; cross-tier parity 21/21; scalastyle clean. Co-authored-by: Isaac
VizX already reprojects for basemaps/PMTiles but uses a parallel ds.crs read (not the canonical helper) and offers no crs override for a CRS-less raster, so a CRS-less-but-known raster gets a correct grid via Group C yet no correct basemap plot. Group G adds an optional crs source param to the plot entry points (plot_tile/cog/raster/file/static) plus routes VizX CRS through crs_to_canonical/resolve_crs. Never-error preserved (absent CRS yields a basemap-less plot, as today). Completes read to operate to visualize. Co-authored-by: Isaac
Task 10 covers VizX CRS consistency: a _resolve_plot_crs helper routing through crs_to_canonical/resolve_crs, an optional crs source param on the plot entry points (plot_tile/cog/raster/file/static), never-error preserved. The docs task (now Task 11) also seeds the VizX rows into the master cross-reference table. Self-review updated: seven groups + two foundations across 11 tasks. Co-authored-by: Isaac
…shed (Group A) Task 4 of RasterX CRS to 100%. A plain WKB/WKT geometry can now declare its source CRS on the light Column API: - rst_clip gains clip_crs; rst_sample and rst_viewshed gain crs. - edit.clip_to_geom / ops.sample / _viewshed_bytes route the source CRS through crs.resolve_source_crs (Rule 1: embedded EWKB SRID wins per-geom; else the explicit param incl. ESRI/WKT; else assume aligned) and reproject via the cached crs.get_transformer. Never errors on a CRS-less geom. - Replaces the old int-only CRS.from_epsg sites in sample/viewshed (ESRI/WKT source CRS now work). Params default None so SQL registration + existing calls are unchanged (arity-compatible). 24 + 56 + 136 tests green (source-crs, clip/edit/pipeline regression, Spark-UDF + SQL registration). flake8 clean. Co-authored-by: Isaac
…roup A) Task 5 of RasterX CRS to 100%, heavy mirror of Task 4: - RST_Clip gains a 4th arg clipCrs (String); execute resolves the cutline source CRS via SpatialRefOps.resolveCrs (embedded SRID wins, else clipCrs, else the raster CRS fallback) — ESRI/WKT cutline CRS now work. - RST_Sample gains a 3rd arg crs; doInvoke now reprojects the point from its source CRS to the raster CRS (previously heavy assumed pre-aligned) via a CoordinateTransformation; execute keeps its pre-aligned contract. - RST_Viewshed gains a 6th arg crs; observer point reprojected the same way. All three: builder accepts both the old arity and the new (crs) arity, so existing registered calls are unaffected. Never-error preserved (missing source or raster CRS / transform failure -> point/cutline used as-is). RST_CrsOpsTest 17 (+2), PixelOpsTest 11, AnalysisTest 4 green; scalastyle clean. Co-authored-by: Isaac
…ter (Group B, light) Task 6 (light, produce-new-raster family) of RasterX CRS to 100%: - features.rasterize_geom: rename srid -> out_srid, add out_crs; resolve target (out_crs wins, both -> error, neither -> geometry's carried source CRS). Rule-2: reproject the geometry from its source CRS (embedded EWKB SRID) into the output CRS before burning — fixes the silent-garbage case for an EWKB geom + differing output CRS. - tin.idw_grid / delaunay_dtm: srid -> out_srid + out_crs via shared _resolve_out_crs -> canonical string; _write_float64_grid now takes a CRS string (None -> CRS-less). Points are assumed already in the output CRS (label-only). - Column + agg wiring: rst_rasterize, rst_gridfrompoints(+_agg), rst_dtmfromgeoms(+_agg) gain out_srid/out_crs; UDFs guard int(None) and thread out_crs. Positional callers still bind srid->out_srid (backward-compatible). Grid rasterize_agg out_crs (B4) + heavy Group B are the remaining T6 pieces. 21 out_crs+tin tests green; flake8 clean. Co-authored-by: Isaac
Refactor cellraster to accept an output-CRS spec that is an int SRID OR a CRS
string (per the user's request to do the refactor, not just the rename):
- _reproject normalises both sides via pyproj CRS so the identity short-circuit
survives across spellings (4326 == "EPSG:4326" == CRS(4326)).
- _norm_out_crs / _is_geographic replace the srid==4326 pixel-size branch with a
proper is_geographic check (any geographic CRS, not only 4326).
- cells_to_raster stamps the canonical CRS string (EPSG/ESRI/WKT), not EPSG:{int}.
Thread out_crs through _rst_{h3,quadbin,bng}_rasterize_agg_udf + rename srid ->
out_srid and add out_crs on the three public functions (out_crs wins; grid-native
default preserved; bng out params documented no-op, always 27700).
13 cellraster regression + 6 out_crs (incl. ESRI:54008) + h3-rasterize-validate
green; flake8 clean.
Co-authored-by: Isaac
…avy) Route the heavy vector->raster output-CRS construction through the shared SpatialRefOps.resolveCrs instead of raw ImportFromEPSG, so an ESRI code passed as the int srid (e.g. 54008) classifies correctly and stamps ESRI:54008 — matching the light tier's out_srid handling. Changed VectorRasterBridge.buildOgrLayer / buildEmptyRaster and RST_GridFromPoints.writeGeoJson. Heavy RST_Rasterize/gridfrompoints/dtm/grid-aggs use one int srid for the output CRS (geom assumed already in it — no source/target mismatch, so no Rule-2 reprojection needed on the heavy tier); a separate out_crs string param is not added (the int srid already accepts EPSG+ESRI via the resolver). Signatures unchanged — no arity/registration churn. 8 rasterize/gridfrompoints/polygonize + 18 RST_CrsOps (incl. new ESRI bridge test) green; scalastyle clean. Co-authored-by: Isaac
…roup B5, light) Task 7 of RasterX CRS to 100%: - gbx_h3_cell_bbox: rename srid -> out_srid, add out_crs (string wins). The _h3_cell_bbox_udf gains an optional out_crs; SQL-registered arity unchanged for the 4-arg form. - rst_h3_gridspec (light-only DataFrame helper): out_srid/out_crs; bbox coords computed in the resolved CRS, grid.srid INT field carries the EPSG code when the CRS has one (else out_srid); pixel-size geographic branch now keys on is_geographic (any geographic CRS), not srid==4326. - FIX cellraster._reproject: route int/int-like through resolve_crs so a bare ESRI int (54008) classifies as ESRI (pyproj's lenient from_user_input would reject EPSG:54008); _norm_out_crs passes a rasterio CRS through unchanged. 7 out_crs + h3_gridspec + cellraster regression green; flake8 clean. (Heavy RST_H3_CellBBox int srid already ESRI-correct via ImportFromEPSG; its string out_crs + relocation land together in Task 8.) Co-authored-by: Isaac
…tion deferred) Task 8 of RasterX CRS to 100%, reduced scope (Q(T8)-1 + Q(T8h)-1): the planned relocation of gbx_h3_cell_bbox to GridX is DEFERRED to the future H3-GridX phase -- neither tier has an H3 expression subpackage yet (light pygx has zero H3; heavy has gridx/grid/H3.scala math but no H3 expression dir), so moving it now would seed a lone one-function corner (+ a pyrx->pygx back-dep for rst_h3_gridspec). The function stays in place both tiers; registered SQL name gbx_h3_cell_bbox unchanged. CRS work done in place: RST_H3_CellBBox gains a 5th arg out_crs (String, wins over the int srid); output CRS resolved via SpatialRefOps.resolveCrs (ESRI codes + WKT/PROJ4 work, not just EPSG), identity short-circuit at grid-native 4326. Builder accepts 1-5 args (old arities unchanged); added a 5-arg Scala Column overload. 9/9 RST_H3_RasterizeAggTest (+2 new: out_crs web-mercator, ESRI srid); scalastyle clean. Co-authored-by: Isaac
… light) Task 9 of RasterX CRS to 100%. The heavy GDAL/GTiff reader now honours a clipCrs option (the light reader already had it): GDAL_Batch resolves the option to a canonical CRS string once (via SpatialRefOps.resolveCrs/crsToCanonical — ESRI/WKT work), threads it through GDAL_Partition to GDAL_Reader, and a new tileToRow overload stamps it into the v2 tile's clip_crs field (position 5). Absent -> None -> clip_crs stays null (never errors); an explicitly-unresolvable value raises. 4/4 GDAL_DataSourceTest (+2: clipCrs populates ESRI:54008, absent stays null); scalastyle clean. Co-authored-by: Isaac
…roup G) Task 10 of RasterX CRS to 100%. VizX now shares the canonical CRS authority instead of a parallel ds.crs read, and offers a crs override for a CRS-less raster (consistency with Group C's rastertogrid override): - _resolve_plot_crs(ds_crs, crs_override): raster CRS canonicalised via crs_to_canonical/resolve_crs (ESRI/WKT consistent); else the override; else None -> basemap-less plot. Never errors on absent CRS. - plot_tile and plot_cog gain an optional crs param, routed through the helper to the contextily basemap. Scope: plot_tile/plot_cog are the raster-over-basemap plots that read ds.crs; plot_raster/plot_file are basemap-less array renders (no CRS role); plot_static is vector (fixed 3857 + geopandas to_crs — VectorX CRS territory, out of scope). 4/4 _resolve_plot_crs tests + 255 vizx suite green; flake8 clean. (The 1 vizx failure, test_no_folium_import, is a pre-existing host-dep issue, not CRS.) Co-authored-by: Isaac
…/PROJ4 examples Task 11 (Group F) of RasterX CRS to 100%. Coordinate Reference Systems page gains: - "The four CRS-string forms" near the top: authority code / int-castable / WKT (a genuinely authority-less custom Transverse Mercator) / PROJ4 (Albers), all verified to resolve — makes WKT + PROJ4 prominent per request. - "Source CRS vs output CRS": the two-role model + srid/crs/clip_crs (source) vs out_srid/out_crs (output) naming standard. - Per-geom source resolution + first-class mixed columns; the never-error invariant note (only 3 conditions raise); produce-new-raster Rule-2 reprojection; rastertogrid auto-reproject-to-grid-native; a performance note (internal transformer caching — no need to pre-warp). - Master "CRS function reference" table: every CRS-touching function across GeoBrix with param + role, each linked to its reference entry; VectorX/GridX rows deferred with a note. Plus per-function CRS-page links + updated signatures on rst_clip/sample/ viewshed/rasterize/gridfrompoints/dtmfromgeoms in raster-functions.mdx. Voice-grep clean; docs build green (MDX compiles, links + new anchors resolve). Co-authored-by: Isaac
Explains grid-shift datum/vertical transforms (NTv2/NADCON/PROJ .tif grids; NAD27<->NAD83, OSGB36<->ETRS89 via OSTN15): reprojection delegates to PROJ so any grid-referencing WKT/PROJ4 CRS is honored when the grid is found; how PROJ finds grids (PROJ_DATA search path + PROJ_NETWORK=ON CDN); a prominent warning that a missing grid degrades SILENTLY to lower accuracy (does not error) + how to confirm; and a Serverless/no-egress note pointing at Volume-staged grids and a PROJ_DATA prepend (with the standardized customer-grid mechanism flagged as a planned follow-up). Docs build green; links resolve; voice-grep clean. Co-authored-by: Isaac
…inal review) Final whole-branch review of RasterX CRS-100 found a native-memory leak in RST_Sample.reprojectToRaster and RST_Viewshed.reprojectObserver (Task 5): the resolved source SpatialReference was deleted only on the transform branch, not when source==raster CRS (IsSame==1), leaking one SpatialReference per identity-CRS sample/viewshed call. Moved sr.delete() into a finally so it's released on every path. 33/33 heavy Sample/Viewshed/CrsOps tests green; scalastyle clean. Co-authored-by: Isaac
Design for VectorX's CRS-string complement to the product's int-SRID built-ins (never duplicating st_srid/st_setsrid/st_transform), both tiers, mirroring the RasterX rst_crs/rst_setcrs/rst_transformcrs split with geometry-honest semantics: - st_transformcrs(geom, target_crs, source_crs=None): reproject to an EPSG/ESRI/ WKT/PROJ4 string; optional source_crs for plain WKB/WKT; a 12-cell return matrix (encoding follows input text-vs-binary; carried SRID follows the target — authority-coded upgrades plain->E and carries the int code, authority-less yields the plain form with any stale SRID cleared). - st_setcrs(geom, crs): string->SRID stamper, EPSG/ESRI only (WKT/PROJ4 raise — a geometry can't hold an authority-less CRS); encoding-preserving. - st_crs(geom): embedded SRID -> canonical EPSG/ESRI string; plain WKB/WKT -> null. Architecture: Q4-2 tier-neutral resolver refactor (extract pyrx.core.crs -> gbx.core.crs + heavy SpatialRefOps -> neutral home, with re-export shims so the shipped CRS-100 importers keep working) as a prerequisite task; shared parse_geom + an encoding classifier driving the matrix; never-error invariant. Co-authored-by: Isaac
CI (run 30999099989) caught a missed call site from Task 6a's srid -> out_srid rename: the bench REGISTRY FnSpec for rst_h3_rasterize_agg still passed srid= as a kwarg -> "unexpected keyword argument 'srid'". Changed to out_srid=. The quadbin/bng/rasterize/gridfrompoints/dtm bench col_fns were unaffected (they pass srid positionally, which binds to the renamed out_srid in the same slot, or omit it). 92 bench tests (incl. the failing runner test) green; flake8 clean. Co-authored-by: Isaac
Task-by-task plan for the VectorX-CRS spec. T1/T2 extract the CRS resolver to a tier-neutral home (light gbx.core.crs + pyrx shim; heavy neutral SpatialRefOps + rasterx forwarder) so pyvx shares one authority without duplicating the shipped CRS-100 resolver — regression gate is the existing CRS suites passing unchanged through the shims. T3/T4 build st_crs/st_setcrs/st_transformcrs light/heavy over the shared resolver + parse_geom/JTS I/O, driving output via the Q6 return matrix; st_setcrs uses to_authority() (EPSG+ESRI), authority-less raises. T5 = cross-tier parity + binding parity + docs (incl. the deferred VectorX rows in the CRS-page master table). Self-review flags the resolver-relocation import risk, the bytes-or-str UDF return-type nuance, EWKT round-trip, and heavy SR release. Co-authored-by: Isaac
…pyrx shim) Task 1 of the VectorX CRS family. Moved pyrx/core/crs.py verbatim (git mv, behavior-preserving) to the new tier-neutral databricks.labs.gbx.core package so the lightweight packages (pyrx AND the upcoming pyvx CRS family) share one CRS authority instead of forking it. pyrx/core/crs.py is now a re-export shim (public API + the privates the CRS-100 tests reference: _TRANSFORMER_CACHE_SIZE, _is_intlike, _transformer_cache, etc.), so all 20 existing importers keep working unchanged. 36 tests green (new neutral + shim test + full CRS-100 regression through the shim); all importers smoke-import; flake8 clean. Co-authored-by: Isaac
…arder) Move the real SpatialRefOps implementation from rasterx.operations to the new tier-neutral com.databricks.labs.gbx.operations package. VectorX expressions (Task 4) need CRS resolution without depending on the rasterx package; this gives them a clean import. rasterx.operations.SpatialRefOps becomes a thin forwarder — explicit def delegations for all six public members — so the ~12 existing rasterx importers (RST_Clip, RST_Sample, RST_Viewshed, RST_SetSrid, RST_SetCrs, RST_TransformCrs, RST_Crs, RST_H3_CellBBox, RST_GridFromPoints, VectorRasterBridge, GDAL_Batch, GDALRasterize) compile and pass unchanged. SpatialRefOpsTest extended with 10 dual-path assertions confirming the neutral path and the forwarder return identical results for resolveCrs/crsToCanonical/ getEPSGCode/fromEPSGCode/resolveSourceSR. All 22 tests pass; scalastyle clean.
Add INPUT_TILE to _PREAMBLE and pass input_tile=INPUT_TILE to both run_spark_path call sites in run_light (explain-only path + regular _todo path), so the generated notebook propagates the virtual vs materialized tile choice to the spark-path runner. Also sync ORDER with the two ResultRow fields Task 4 added (input_tile, output_disposition) so cluster.py no longer raises at import time. Co-authored-by: Isaac
Expose --input-tile {materialized,virtual} (default materialized) on
gbx:bench:cluster so the virtual-tile spark-path leg can be exercised
on-cluster without changing code. Parses via the existing _arg() helper,
validates choices, and threads the value into cfg["input_tile"] for
build_bench_notebook to consume.
Co-authored-by: Isaac
Add test_spark_virtual_leg_smoke to exercise the full virtual-leg pipeline over Spark: disposition-sampling (Task 5) and creation micro-leg (Task 7) together. Asserts rst_slope→materialized, rst_setsrid→deferred, rst_avg→materialized, rst_fromfile→deferred, all per_tile_avg_s >= 0, and all rows tagged input_tile="virtual". Co-authored-by: Isaac
I1: gate spark-path input_tile="virtual" tag block on input_kind=="tile";
non-tile fns (agg/tile_array/bytes/path/geometry) no longer get falsely
stamped virtual — they keep the truthful materialized/na defaults.
I2: add test_pure_core_parity_full_tile_set covering 103 tile-input fns;
all 103 match (0 divergences). Also fixes run_pure_core_parity to guard
fingerprinting on fingerprint=False (was crashing on rst_boundingbox's
WKB output being opened as raster bytes).
Q1: replace _parity_fp (identical duplicate) with _fingerprint_for in
run_pure_core_parity and delete the dead function.
Q2: results.summarize virtual-tile section now counts all 3 disposition
states (deferred/materialized/na) to match the per-fn table below.
Co-authored-by: Isaac
A QA pass over the virtual-tile bench leg found five defects: - 5 tile-multiplying fns (maketiles/retile/tooverlappingtiles/separatebands/polygonize) were specced as scalars, but pyrx registers them as UDTFs, so spark-path errored -- flag udtf=True so they time via SQL LATERAL. - The disposition sampler called the scalar col_fn for every fn, so all UDTFs recorded "na" -- sample UDTFs via LATERAL and infer materialized for flat-output UDTFs. - rst_contour and rst_isempty read pixels but were classified "deferred" -- pin/allowlist them materialized. - rst_polygonize emits geometry (no tile to sample) -- pin virtual_disposition=materialized. - run_spark_path never routed the role=bng_gb tile to the 9 BNG gb_tile fns (pure-core and the Scala heavy tier do), so BNG spark-path binned an empty out-of-GB grid -- build a GB-tile DataFrame and route gb_tile fns to it. Adds regression + registry-wide guard tests (17 pass). Co-authored-by: Isaac
Part A: the Raster->grid table gains Light virtual/tile + Disposition columns; the 9 BNG rows are refreshed to real Great-Britain-cell numbers (the lightweight spark-path had been binning an empty out-of-GB grid), speedups recomputed, table re-sorted, and the prose corrected (rst_bng_tessellate is heavyweight-favored, not the empty-tile 1.81x). Part B: new "Virtual vs materialized input tiles" subsection (74 fns) documenting the deferred lazy-win -- set-CRS/SRID/nodata/band-select run 4.7-12.5x faster on a virtual tile (no pixel bytes shuffled, raster never opened), while pixel-reading ops pay a small open cost. Co-authored-by: Isaac
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 14:35 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 14:35 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 14:35 — with
GitHub Actions
Inactive
mjohns-databricks
had a problem deploying
to
runtime
August 13, 2026 15:02 — with
GitHub Actions
Failure
plot_raster/plot_file gained three optional kwargs; defaults reproduce the prior behavior byte-for-byte (a back-compat gate), so existing callers and tests are unchanged. - bands=: 1-based band select/reorder (matches rst_frombands order); 1 band renders viridis, >=3 composites RGB from the first three. - stretch="perband"(default)|"shared"|(lo,hi): an independent per-band percentile stretch desaturates strongly-correlated EO RGB to grey; "shared" applies one pooled stretch across the bands to preserve colour balance. - fill=: value masked as NoData for display (excluded from the stretch and drawn transparent) even when the tile has no nodata set. The enhanced RGB path normalizes uint8 (/255) and out-of-range float data to [0,1] before compositing so it is not hard-clipped to white; a 2-band or empty selection raises a clear ValueError. Adds render tests (51 total in vizx). Co-authored-by: Isaac
NB04 renders stacked/clipped tiles as true colour via plot_raster(bands=(1,2,3), stretch="shared", fill=0) with a matching narrative note; NB03 filters empty tiles before sampling so an all-NoData H3 cell no longer shows as a blank grey box. config_nb installs the wheel with --no-cache-dir so a same-version rebuild is actually picked up on Serverless. eo-series.mdx + README updated for the virtual-tile ingestion path. Co-authored-by: Isaac
Pure formatting (isort import order + black) to match CI; no functional change. Clears the isort drift that failed CI lint on these two bench test files. Co-authored-by: Isaac
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 20:45 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 20:46 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 20:46 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 20:46 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 21:12 — with
GitHub Actions
Inactive
mjohns-databricks
had a problem deploying
to
runtime
August 13, 2026 21:21 — with
GitHub Actions
Failure
PR #72's build failed on `black --check`. CI lints as `isort && black && flake8 src test`, so black's failure short-circuited before flake8 — hiding pre-existing flake8 debt that surfaces once black passes. This clears both in one pass: - black-reformat 7 drifted files (bench/{runner,spec}.py, test/bench/{cluster, results,spec}, test/pyrx/{core_agg,v2_tile_output_invariant}). - flake8: drop 3 unused imports (F401 in pyrx/_serde.py, test/rasterx/ test_udtf_error_row.py, test/vectorx/test_crs.py) and mark the bench helper run_spark_path with # noqa: C901 (matches the _render precedent). No functional change; none of these files were part of the vizx/eo-series work — this is latent tree-wide lint debt that was masked by the black short-circuit. Co-authored-by: Isaac
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 22:26 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 22:26 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 22:26 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 22:26 — with
GitHub Actions
Inactive
mjohns-databricks
temporarily deployed
to
runtime
August 13, 2026 22:54 — with
GitHub Actions
Inactive
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
GeoBrix 0.5.0 — release PR (
beta/0.5.0→main)Promotes the 0.5.0 line to
main(~496 commits). Versions are bumped (pom.xml+__version__=0.5.0). Full change list:docs/docs/release-notes.mdx→ "What's new in v0.5.0".Highlights
cog_gbx/raster_gbx/gtiff_gbxreaders can emit virtual tiles (sourcepath+ pixelwindow, no bytes); pixels are read lazily one window at a time. A virtual row is ~100 B vs 148–527 KB materialized (~1,400–5,000× smaller), dissolving Serverless OOM on large-raster fan-out. Lightweight raster readers now default to virtual (breaking behavior change).cellid, raster (nullable), path, window, clip_polygon, clip_crs, crs, metadata. Both tiers read v1 and v2 and always emit v2.rst_*is virtual-tile-aware via one shared open path (header-only accessors; reference/passthrough ops stay virtual; pixel ops materialize only their window). New force-output params:virtualize_dir/virtualize_prefix/materialize.file_gbx(path lister) +cog_gbxwriter (master-COG prep) +cog_gbxreader (COG-aware windowed read:tileSize/overlapPercent/clipPolygons/windows). ReadersplitStrategynow defaults tonone.gbx_st_{crs,setcrs,transformcrs}(VectorX) andgbx_rst_{crs,setcrs,transformcrs}(RasterX): authority-string CRS handling that survives non-EPSG round-trips.DESCRIBE FUNCTIONsignatures, and many fixes.Pre-merge gate (requested)
docs/docs/notebooks/eo-series.mdx) and the eo-seriesREADME.mdreflect the support, validated on Serverless.Opened as draft until the gate above is met.
beta/0.5.0CI is the dev-branch signal (not a merge gate); themainbuild runs on merge.This pull request and its description were written by Isaac.