Release Notes
Unreleased
Correction ledger and guarded magnetic base correction (TRK-539)
- Matching Python/JavaScript correction history APIs record evidence-backed per-channel states, legacy aliases and immutable application events with shared SHA-256 data bindings. Unknown/applied history, stale data, conflicting summaries and repeat IDs prevent accidental reapplication.
- Magnetic base correction subtracts synchronized, linearly interpolated variation relative to an explicit nT reference. Both survey and base histories must be unapplied; missing/null/excessive-gap/exterior coverage prevents an atomic application. Outputs retain full-resolution observations, proposals, parameters and persisted provenance; uncertainty is not propagated. JavaScript APIs use asynchronous native Web Crypto.
Elevation-aware equivalent sources (TRK-537)
- Optional Python Harmonica adapter fits scalar magnetic/gravity anomalies at measured XYZ, predicts a common height, and returns support, residuals, backend/source geometry and persisted lineage. Explicit vertical context, depth/damping, duplicate and extrapolation policies accompany bounded execution. Line/block holdouts refit after XY/buffer exclusions; candidate ranking requires complete coverage.
- A versioned JSON runner returns full-resolution raster payloads. JavaScript exposes shared schemas and TypeScript request/report contracts; numerical execution is explicitly Python-only. No downward continuation or geological inversion is claimed.
Geophysics scalar raster alignment (TRK-536)
- Matching Python/JavaScript align/match APIs provide explicit nearest/bilinear resampling, rotated affine grids and bounded horizontal reprojection, with per-band support, footprint and coordinate-valid masks. Categorical codes require nearest; local grids require frame/units; datum changes require backend acknowledgement. Field units/correction declarations and flat input lineage are preserved. No vector/height conversion, antialiasing or uncertainty propagation is claimed.
Geophysics baseline gridding and spatial validation (TRK-535)
- Matching Python/JavaScript nearest/IDW observation gridders return full-resolution field/support/count/distance/extrapolation rasters, with explicit support radius, duplicate policy and XY-only elevation treatment. Line/block holdout validation excludes colocated/buffered training observations before fitting and reports coverage with paired-only errors. Inputs, units, correction declarations and persisted grid lineage are retained.
Geophysics observations and survey QC (TRK-533)
- Portable versioned line/station observations and non-destructive QC in Python/JavaScript: acquisition-order segments, missing readings/positions, coincident XY locations, time reversals/gaps, caller-defined speed/clearance thresholds, window-median spike candidates and repeat-station statistics. Explicit context, coverage, correction-state preservation and resource limits accompany the report.
Geophysics operation catalogue and workflows (TRK-532)
- Discoverable geophysics operation contracts and versioned in-memory workflow validation/execution in Python and JavaScript, with a reproducible magnetic-grid regional/residual/edge recipe, explicit scientific context, resource limits and persisted execution history.
Embedded geophysics lineage (TRK-530)
- ERS and GeoTIFF writers embed versioned JSON provenance by default; readers restore processing steps, source references and non-transport metadata, exposing the manifest under the reserved
metadata.baselode_provenancekey. Explicit writer opt-out supports metadata-free exports. - Repeated saves avoid recursive manifest nesting; binary operations merge flat, deduplicated input graphs. Source URL credentials/query fragments are stripped, and stale format-specific fields remain historical rather than current. Band selection records its view; raster arithmetic preserves both input histories. GeoTIFF band descriptions and unit XML entities round-trip correctly.
- Compatibility: writers now require JSON metadata by default;
include_provenance=False/includeProvenance:falserestores the old metadata-agnostic export behaviour. Raster arithmetic offers the same opt-out for opaque in-memory metadata and marks omitted lineage explicitly. - Reader compatibility: malformed/future manifests still fail by default, with an explicit read opt-out that marks the ignored history. Current in-memory source locators retain their original spelling/credentials; persisted source references are sanitized.
- JavaScript GeoTIFF band names now use stored GDAL descriptions. When absent, names retain the output-order
band_1,band_2, ... fallback, including selected bands. - Invalid/non-JSON metadata and malformed/unsupported manifests raise errors; both languages share the version 1 format. The record identifies the exporter and dtype; it does not authenticate data or execute a workflow.
Geophysics processing contracts (TRK-526, Milestone 0 first increment)
- Derivatives and analytic signal now update per-band units (for example
nT/m,nT/m^2); tilt is labelleddegree. Unknown local distances usecoordinate_unit. - Scientific filters and enhancements now reject geographic/geocentric CRSs, angular units and conflicting CRS/unit metadata. Reproject geographic data first. Display and sampling remain available. Distances continue to use native linear coordinate units.
- Compatibility: CRS identifiers/definitions unavailable to a processing CRS engine now require explicit linear
metadata.crs_units; missing units cause an error. This includes JavaScript EPSG:3577/EPSG:2193 rasters and GeoTIFFs without a linear-unit GeoKey. Python can resolve more CRS definitions through pyproj; both engines honour explicit linear units for unresolved external definitions. No distance unit is guessed from an unknown identifier. - GeoTIFF readers retain per-band units, including when selecting or decimating bands.
- Python and JavaScript share regression fixtures for derived units as well as numerical values.
v0.1.71
Saving and editing geophysics grids (TRK-520)
- Write grids: Python
write_ers,write_geotiffandsave_raster(by extension), and JSencodeErsandencodeGeoTifffor saving from the browser.- ERS writing needs only NumPy and maps GDA94, GDA2020, AGD66/84 and WGS84 systems to ERS datums and projections.
- JS writes its own float GeoTIFFs, because geotiff.js's writer stores only 8-bit samples.
- Files from either language read back with
load_raster,readErs/loadGeoTiffand GDAL. Committed JS-written fixtures are checked with GDAL in the Python tests.
- Edit grids:
cropto a box andclipto a polygon (with holes; GeoJSON or Shapely);combinealigned grids or a grid and a number, for residuals (combine(tmi, regional, "subtract")) and scaling. It refuses grids on different cell layouts or CRSs;fill_gapsfills dropouts with a smooth surface that honours the data, optionally only withinmax_distancecells of data.- JS:
cropGeophysicsRaster,clipGeophysicsRaster,combineGeophysicsRastersandfillGeophysicsGaps, checked cell for cell against Python with the shared fixture.
- Each step is recorded in
metadata["processing"].
v0.1.70
Sampling geophysics grids at drill data (TRK-519)
- New
sample_points,sample_table,sample_profileandprofile_stationsinbaselode.geophysics, and JSsampleGeophysicsRaster,sampleGeophysicsRows,sampleGeophysicsProfileandgeophysicsProfileStations. Read grid values at collars, surface samples, desurveyed trace stations or along a section line, by bilinear or nearest-cell interpolation. Points off the grid or next to a gap giveNaN/null, never an invented value. - Points in a different CRS from the grid are reprojected first (pyproj in Python, proj4 in JS), so MGA collars can be sampled against a GDA94 lat/long grid. A shared fixture checks Python and JS agree.
- JS CRS helpers move to a shared module that also registers GDA2020 / MGA2020, AGD66/84 / AMG and WGS84 UTM zones for proj4.
transformCrsPointsexposes the same reprojection, for laying out profile lines in a grid's own units. - Docs: the Python desurvey pages referred to a
desurvey_holesfunction that does not exist; they now showminimum_curvature_desurveyand the other per-method functions. The JS tilt derivative is documented in degrees, as implemented.
v0.1.68
Geophysics enhancements (TRK-513)
- Standard potential-field enhancements in
baselode.geophysics(Python) and JavaScript:vertical_derivative(1VD / 2VD viaorder),horizontal_derivative,total_horizontal_derivative,tilt_derivative,analytic_signal,upward_continuationandreduce_to_pole(induced magnetisation; rejected within 15° of the magnetic equator). JS mirrors them asgeophysicsVerticalDerivative,geophysicsHorizontalDerivative,geophysicsTotalHorizontalDerivative,geophysicsTiltDerivative,geophysicsAnalyticSignal,geophysicsUpwardContinuationandgeophysicsReduceToPole. - JavaScript gains a radix-2 FFT port of the wavelength filters too (
geophysicsLowPass,geophysicsHighPass,geophysicsBandPass) plusprocessGeophysicsRaster, so every operation runs in the browser. A cross-language reference fixture checks Python and JS agree cell for cell. - Shared Fourier core now derives map-axis wavenumbers from the affine transform, fits the detrend plane in centred cell indices, and mirror-pads evenly around the grid to a power-of-two size (previously all padding went after the grid, making isotropic operations slightly directional).
- New DOM-free
baselode/geophysicspackage entry (with type declarations) exporting the raster, ERS / GeoTIFF and processing functions without React, Plotly or the DOM — for running Fourier processing of large grids in a Web Worker. - Raster metadata records coordinate units in
metadata.crs_units(degrees vs metres) for ERS and GeoTIFF sources. - Fix: ER Mapper grids with
CoordinateType = LATLONG(e.g. GSWA ERMapper_grids releases) are now georeferenced; both readers previously only recognisedLL. loadGeoTiffaccepts typed arrays, as its type declarations already stated.
v0.1.67
ERS reader hardening (TRK-393)
- Python and JS ERS readers parse multi-line
Key = { ... }brace lists (e.g. regionSubRegionvertices) as newline-joined strings instead of failing. - Translated ER Mapper datasets, which point at an image in another format rather than raw grid values, are rejected with a clear message; ECW-compressed images say so explicitly.
v0.1.66
JavaScript ERS reader and raster decimation (TRK-393)
- New
readErs/loadErs(plusparseErsHeader,ersCrs) port Python'sread_ersto JavaScript: BIL, all cell types, both byte orders,NullCellValue, registration cell, datum/projection → EPSG.loadErsfetches string header URLs. - New
decimateGeophysicsRasterand amaxCellsoption onloadGeoTiff/loadErsmatch Python'sGeophysicsRaster.decimate(area-weighted). ERS grids decimate straight from the file bytes and GeoTIFFs stream from tile-aligned chunks, so large grids never materialise at full resolution. - New
geophysicsRasterFromBandshelper; rasters built from typed bands keep source-precision nodata.
v0.1.65
Geophysics rasters (TRK-393)
- New
baselode.geophysicspackage (Python):GeophysicsRasterandload_raster..erspaths use a native NumPy ER Mapper reader (read_ers) — no GDAL needed — supporting BIL, all integer/real cell types, both byte orders,RegistrationCoord/RegistrationCell, and Australian / WGS84 datum-projection → EPSG. Other formats use rasterio via the newrasterextra (pip install baselode[raster]). - Gaussian / Butterworth
low_pass,high_passandband_passfilters by wavelength, nodata-safe, with processing provenance recorded in metadata. hillshadewith azimuth, altitude and automatic vertical exaggeration (hillshade_z_factor); gradients follow the affine transform, so sun azimuth stays geographic on south-up, west-up and rotated grids.- Shared colour scales (viridis, magma, inferno, plasma, cividis, turbo, diverging, rainbow, magnetic, terrain, grayscale) with relative, absolute and histogram-equalised stretches, plus RGBA
colourize. GeophysicsRaster.decimate()(area-weighted, memory bounded by input size) andto_tool_ui_payload()for chat-sized viewer results; nodata is compared in the source dtype so float32 sentinels such as-1e32never become measurements.- JavaScript:
createGeophysicsRaster,loadGeoTiff(GDAL nodata, full ModelTransformation, PixelIsPoint, tie-point offsets),renderGeophysicsRaster,geophysicsHillshade,GEOPHYSICS_COLOR_SCALESand stretch helpers matching Python's output (shared reference fixture).geotiffis now a package dependency. - New
GeophysicsRasterViewercomponent andBaselodeGeophysicsRasterToolUI(static, chat-sized by default) with colour bar, raster summary, cursor read-out, reverse palette, stretch and vertical-exaggeration controls; the Tool UI schema acceptsband,units,stretch,reversePaletteandzFactor. Demo viewer gains a geophysics raster page.
v0.1.64
GSWA structural observations (TRK-471)
- Documented the start-depth plotting contract for structural observations:
plot_tadpole_logandstructural_to_tadpoleuse the supplied depth column as-is (preserving GSWA conversion setsdepth = from) and never infer a midpoint; only rows with depth, dip and azimuth are drawn. structural_pointJSON schema now describesdip/azimuthas structural-plane dip and dip direction (not hole orientation). New "Structural observations" docs page.
v0.1.63
Attribute promotion and JSON expansion (TRK-468)
- New
baselode.datamodel.attributes.promote_attributespromotes explicitly mapped long-form attribute records (parent_id,attribute_id,name,value, optionalmodified_date) onto unique parent rows, ranked by mappingpriority, then newest date, then lowest attribute ID. - Unmapped attributes go to an
attributes_extraUTF-8 JSON column;_attribute_recordskeeps every source record and_conflicts/attribute_clash_countmake every promotion decision auditable. Strict validation of keys, priorities and ISO dates. Helpersnormalize_attribute_nameandjson_scalar.
v0.1.62
zod 4.6.6+ required (breaking)
- The
zodpeer dependency is now^4.6.6(was>=3.0.0). zod 3.x and 4.x before 4.6.6 are affected by GHSA-mrhx-c7xc-m3w5, and the Tool UI schemas already target the zod 4 API. Upgrade zod before upgrading baselode.
v0.1.60 – v0.1.61
Local-grid plan view
- New
createPlanView(container, options): a Canvas plan view for points in a local X/Y frame — no CRS, basemap or plotting-library dependency. Equal-scale axes, pan / zoom-to-cursor / fit, hover read-out, scale bar and group legend. Math helpersfitPlanView,planToScreen,screenToPlan,zoomPlanViewandpanPlanVieware exported too.
Contributing
- Added
CONTRIBUTING.mdwith contribution guidelines and a contributor licence agreement.
v0.1.59
GSWA flat SQL queries (TRK-446)
build_assay_flat_queryandbuild_surface_sample_assay_flat_queryinbaselode.adaptors.raw_gswa.queriestakesource_release="2025-10"(default) or"2026-05"to match either GSWA database snapshot shape.- The drill builder resolves the stable
HoleIdthrough a left join todbo_collar, including whenHoleIdis explicitly projected.
v0.1.58
Licensing
- The PyPI and npm packages now ship the root
LICENSEincluding the Darkmine Section 7 additional permission (the PyPI package previously bundled plain GPLv3; the npm package had no licence file).
v0.1.57
3D viewer overhaul
- All drillholes render as one merged tube mesh (single draw call) with seamless joints and end caps, optional constant-pixel width, and a screen-width line level of detail when tubes would be sub-pixel.
- Colour is resolved in the shader from a per-hole interval texture indexed by measured depth: narrow intervals survive sparse surveys, and changing attribute, palette or binning (quantile / linear / log, stepped or smooth) via
setDrillholeColorBynever rebuilds geometry. - Navigation: damped orbit with zoom-to-cursor and pivot-under-cursor, double-click focus, animated fit / presets, perspective ↔ orthographic toggle, Z-up first-person walk mode (replaces FlyControls), Blender-style keyboard map.
- Cube viewport gizmo with Z-up face layout, faces-only targets and hover feedback (new
gizmoSizescene option); ground grid, extent box, depth-cue fog, collar markers and fading hole-id labels. - New
Baselode3DHudcomponent: north arrow, scale bar, plan mini-map with camera footprint, cursor coordinates and key cheat-sheet. - Geometry is stored relative to the bounds centre so projected coordinates keep float32 precision.
v0.1.56
Block models as a primitive (TRK-417)
- New
BlockModelDefinition(Python) /createBlockModelDefinition(JS): origin, base block size, extent, optional parent size, azimuth/dip/plunge rotation, CRS — withlocal_to_world/world_to_local,index_to_world/world_to_index, bounds / corners, and a JSON round-trip that also reads the legacy*_meta.jsonshape. - Sub-blocking built in: blocks carry
i, j, k, ni, nj, nk(base-cell index + extent) alongsidex, y, z, dx, dy, dz; either encoding loads and the other is derived. Newblocktable inbaselode.datamodeland the JSON schemas. BlockModelgainsvalidate()(alignment, extent, overlap via cell walk, duplicate index, index consistency, parent containment),regularize(),to_parent_blocks()(mass-weighted means, majority categoricals,fill_fraction),block_at()/sample_at(),select()/clip(),tonnage()/grade_tonnage(),diff()(first cut at GH #16) andsave(). JS mirrors every operation as functions over{ definition, blocks }; a shared sub-blocked fixture with Python-computed expectations pins parity.- Existing loader / stats / filter APIs and the legacy metadata fields are unchanged;
BlockModel(blocks, metadata)keeps its positional signature (definitionis keyword-only).
v0.1.53
Terrain surfaces in the 3D scene (TRK-395)
- New
createTerrainSurfaceand scene methodssetTerrain/clearTerrain/setTerrainOpacity/setTerrainVisible/getTerrain: builds a height-grid mesh from an elevation grid + bounds in scene units, leaves NoData quads as holes, optional perimeter skirt, decimates to avertexBudget, and never applies vertical exaggeration silently. Malformed grids (ragged, degenerate, fractional dimensions) throw instead of rendering an empty surface.
v0.1.52
Desurvey and survey QA fixes (GH-96)
- Every desurveyed trace now starts at the collar: when the first station is below 0 m its orientation is extended straight up, so a single deep station no longer gives a zero-length trace.
- New
midpoint_tangentialdesurvey method (Pythonmidpoint_tangential_desurvey+ JS), registered onDrillholeSetin both languages. validate_drillhole_dbgainssurvey_null_orientation(error) andsurvey_no_usable_stations(warning) checks; new fix helpersdrop_unusable_survey_rowsandsynthesise_collar_station(Python + JS).fix_single_station_surveysacceptsazimuth_col/dip_col.fix_overlapsgainsprecedence_col/precedenceto resolve interleaved sampling campaigns by dataset rank.- Python support widened to
requires-python >= 3.10.load_geophysicsno longer turns a blankhole_idinto"nan"on pandas 2.x.
v0.1.51
3D sections and slabs (TRK-384)
- New
SectionHelper(orthographic vertical cross-section aligned to X or Y) andSliceHelper(movable finite slab,setWidth/step) forBaselode3DScene. They preserve host clipping planes and are mutually exclusive. Baselode3DControlsgains section / slice controls with a position overview map showing scene objects; the section camera follows the clipping plane and keeps pan and zoom.
v0.1.50
JavaScript desurvey API consolidation (breaking)
- Removed
desurveyTraces. UseminimumCurvatureDesurveywith canonical projected collar fields (easting,northing,elevation) and surveydepthrows. The canonical API emits east/north coordinates and elevation-positive-up Z, matching the Python desurvey contract. - Hole-ID joins between collars and surveys preserve the original case.
- The React demo and committed GSWA precomputed trace fixture now use that canonical path, removing the old visualization-only trajectory maths.
v0.1.49
3D categorical colouring (TRK-317)
- Categorical attributes (e.g. geology) split each survey segment at interval boundaries, so sparse surveys no longer paint a whole segment with its dominant category.
- The assay-presence colour mode always renders as presence, even when the categorical flag is set.
v0.1.46 – v0.1.48
Row-object loaders (JS)
- New
*FromRowsentry points accept already-parsed row objects (Parquet, Arrow, LAS, database results) without a CSV round trip — e.g.parseAssaysFromRows,parseStructuralFromRows,parseDrillholesFromRows,parseGeologyFromRows,parseGeophysicsFromRows,parseBlockModelFromRowsandparseUnifiedDatasetFromRows.loadTable,loadCollars,loadSurveys,loadAssaysandloadGeologyalso accept row arrays. Existing CSV functions are unchanged.
Canonical export layer (Python)
- New
baselode.export(write_project,normalize_table): atomically writes matching CSV / Parquet tables plus a manifest. Canonical identifiers (hole_id,sample_id,project_id, …) are exported as nullable strings, keeping integer spelling (1001, not1001.0); nested values are JSON-encoded.
Desurvey robustness
- Python desurvey ignores rows missing a hole ID, usable collar coordinates or usable survey measurements instead of failing; missing collar elevation defaults to zero.
v0.1.45
JavaScript Extent
- New
Extentclass (also via the lightweightbaselode/extententry) mirroring Python:fromBbox,toCrsreprojection,setCrs,center, and deterministic GeoJSON viatoPolygon/toFeature/toFeatureCollection. Built-in definitions for WGS84, Web Mercator, GDA94 and MGA zones 49–58. - Python
Extent.to_crsnow transforms all four corners, so projected envelopes are no longer clipped.
v0.1.44
assistant-ui integration (TRK-300)
- New
baselode/assistant-uientry:createBaselodeAssistantUiToolkitregisters Baselode's Tool UI renderers (strip log, 3D scene, scatter, histogram, box, violin, ternary) with assistant-ui under your backend tool names.@assistant-ui/reactis an optional peer dependency. - New
baselode/tool-ui/contractsentry exposes the Zod result contracts (BASELODE_TOOL_UI_SCHEMA_CONTRACTS,parseBaselodeToolUiResult) for server code without loading React, Plotly or Three.js. - TypeScript declarations for the Tool UI, contracts and assistant-ui entries.
v0.1.40 – v0.1.43
Strip-log fixes and options
- Fixed a Plotly draw / cleanup race in
TracePlot, the Tool UI and panel wrappers when charts unmount or re-render quickly. - Shared categorical colour maps:
TracePlotpoint logs, structural plots (colourMap) and the 3D drillhole scene (categoryColorMap) honour a caller-supplied category → colour map. - New
TracePlotconfig.depthRange([from, to]) pins a shared measured-depth axis across all display types.
v0.1.38
Strip-log track types and patterns (TRK-241)
- New numeric chart geometries in Python and JS: heat-strip, plus stepped and filled line options (combinable), and per-track log scale (
log_scale/logScale). Legacycolored-line/filled-line/step-linestrings still render. - New tracks: two-curve fill, percent-composition, depth annotations, point log and split dip / azimuth (
plot_two_curve_fill,plot_composition_log,plot_depth_annotations,plot_dip_azimuth_log/buildTwoCurveFillConfig,buildCompositionConfig,buildDepthAnnotationsConfig,buildPointLogConfig,buildDipAzimuthConfig), available as first-class chart types. - Lithology hatch patterns on categorical bands (
LITHOLOGY_PATTERNS,resolve_pattern_map/resolvePatternMap). alpha_beta_to_dip_azimuth/alphaBetaToDipAzimuthoriented-core conversion, validated against a shared reference fixture.- Every track pins an identically padded depth axis so tracks of a hole line up; new "Start at 0" option (
start_from_zero/startFromZero). - Redesigned comments track (theming, text budget, hover); below-detection negatives floored at zero on area / colour geometries (raw value kept in hover); dark-template-safe structural palettes.
TracePlotmoves chart type, colour-by and toggles into a settings popover.- Fix:
parseSurveyCSVtreats blank dip / azimuth cells as missing instead of fabricating 0° stations.
v0.1.37
Strip-log graded line, colour-by and multi-assay (TRK-224)
- Graded (value-coloured) line chart type on the magma ramp; colour a numeric track by a separate categorical column (
color_by/colorBy); multi-assay stacked line and bar tracks (multi-line,multi-stacked; Pythonplot_multi_assay). Python, JS and Tool UI at parity. - Bar tracks size each bar to its interval; hover is depth-unified (
y unified). - 3D drillhole camera stays in a Z-up orbit; legacy Y-up saved views are converted on restore.
v0.1.35
Drillhole QA: auto-fix interval overlaps + numeric-negative BDL handling
- New
baselode.drill.validate.fix_overlapsresolves three classes of interval overlap automatically — touching (snapA.todown toB.fromwithintouching_tol), exact duplicate (drop), resampled superset (drop the coarser interval when finer rows fully cover it and their length-weighted mean ≈ the coarse value withinmerge_tol) — and leaves genuine value-conflicts in aconflictsframe for human review. Passreturn_diagnostics=Trueto also get a per-row audit log of every snap / drop / kept-conflict. replace_below_detection_limitnow handles two BDL conventions in a single call: the historical<Xstring sentinels (unchanged) plus the GSWA / WAMEX numeric-negative encoding where a valueV < 0means below detection at MDL =abs(V). Newstrategykwarg switches the replacement rule —half-mdl(default),mdl,zero, ornan— without breaking the existingsentinel_factorAPI. Opt out of the numeric-negative path withnumeric_negative_sentinels=Falsewhen negatives are real signed values.
Desurvey: traces emit RL elevation (breaking)
_direction_cosineshad a sign flip that produced traceelevationas TVD (positive going down) rather than RL (positive going up). Every*_desurveyfunction andattach_assay_positionsnow emit elevation following the standard right-handed +Z = up convention — a vertical hole from a 0 m collar lands at –100 m elevation at 100 m depth. OMF / Leapfrog / Surpac and any standard +Z-up 3D scene now render holes correctly.- Downstream consumers that were reading the trace
elevationfield will see a sign flip. Code paths that comparedtrace.elevation - collar.elevation == TVDshould switch tocollar.elevation - trace.elevation == TVD.
3D scene polish
parseDrillholesCSV(JS) standardises the sourcemd/survey_depth/measured_depthcolumn to the canonicaldepthfield and now also copies it onto anmdfield on each output point, fixing a silent grey-on-grey colour-by failure for renderers readingpoint.md.- Default
ASSAY_COLOR_PALETTE_10swapped from a diverging blue↔red ramp to a magma-style sequential palette — low values fade into a dark scene background and high-grade samples pop as bright cream/gold.buildEqualRangeColorScaleand the 3D scene share this export so legend and geometry stay in lockstep.
v0.1.34
3D IDW interpolation volumes
- New JavaScript primitives
IDWSampler(pure scalar-field sampler backed by aSpatialHash3D),buildVoxelGrid(async voxel-grid evaluator with progress + cancellation),IDWVolumeRenderer(Three.js shader-based ray-march of aData3DTexture), andIDWVolumeLayer(one-call wrapper that does the whole pipeline and returns aTHREE.Object3Dfor the scene). - Fragment shader writes
gl_FragDepthat the first significant ray hit so opaque objects inside the volume (sample markers, drillhole traces) are correctly occluded behind the visible voxel surface and stay visible in front of it. - Axis-aligned slice planes via
layer.setClipBounds(min, max)in normalised[0, 1]box-local space; cheap uniform-only update, no voxel-grid rebuild. - Live display knobs (
setOpacity,setThreshold,setBlockMode) push straight through to the GPU. IDW kernel parameters (power,searchRadius,maxNeighbors) trigger a rebuild with progress + cancellation. - Demo viewer page at
/idw-volumeexercising the feature against a synthetic five-Gaussian-anomaly dataset. - New "3D Interpolation Volumes" sections in the JavaScript guide + API reference.
v0.1.33
Interactive plot panels (TRK-119)
- New React wrappers that bundle each analytics plot with its own pickers and toggles:
BaselodeScatterPanel,BaselodeHistogramPanel,BaselodeBoxPanel,BaselodeViolinPanel,BaselodeTernaryPanel,BaselodeStripLogPaneland the N-upBaselodeStripLogGrid. Each works uncontrolled or controlled (value+onChange) and is themeable via--baselode-*CSS custom properties. - Exported helpers
detectNumericColumns,detectCategoricalColumns,defaultColorByColumn. TracePlotcontrols use a fixed row layout so long hole IDs no longer overflow strip-log grids.
v0.1.32
JSON Schemas for the data model (TRK-112)
baselode.datamodelis now a package (existing imports unchanged) withto_json_schema/to_json_schema_all, generating Draft 2020-12 schemas (primary / foreign keys, units, descriptions, nullability) for every canonical table.- JS ships the same document as
BASELODE_DATA_MODEL_SCHEMASwithgetBaselodeSchema(name).
v0.1.31
Compositing extensions: hard-boundary + true-thickness
composite_intervals(Python + JavaScript) grows three new kwargs that preserve the existing soft-mode call signature:mode="soft"(default) — the prior length-weighted overlap behaviour, now documented and named explicitly (matches the dhcomp / Leapfrog convention)mode="hard"+boundary_col— composites reset at every change in the boundary column within a hole; no composite straddles a coded contactresidual={"discard","add_to_previous","distribute"}— how to handle a sub-lengthtail at the end of a hard-mode domain
- New Python primitive
composite_true_thickness(intervals, traces, value_col, ref_dip, ref_dip_azimuth, ...)for economic compositing: composites span equal true thickness perpendicular to a reference plane. Reportslength_md+length_trueper composite. Python-only — depends on a desurveyed trace. - JavaScript
compositeIntervalsmirrors the soft/hard modes (no JS true-thickness). - Both implementations validate
method ∈ {average, sum}andlength > 0up front to fail loudly instead of producing nonsense (JS soft mode would have looped forever onlength === 0). - New "Compositing" sections in the Python + JavaScript guides and API references, with a comparison table against
dhcompandPyGSLIB.
v0.1.30
Analytics plots (TRK-52)
- New JS config builders returning Plotly
{data, layout}:buildScatterPlotConfig,buildHistogramPlotConfig(per-axislog: { x, y },barmodeoverlay / stack / group, shared bin edges),buildBoxPlotConfig,buildViolinPlotConfigandbuildTernaryPlotConfig, with categorical colouring that cyclesBASELODE_COLORWAYfor groups the colour map doesn't cover. - Matching Tool UI components (
BaselodeScatterPlotToolUI,BaselodeHistogramPlotToolUI,BaselodeBoxPlotToolUI,BaselodeViolinPlotToolUI,BaselodeTernaryPlotToolUI) and Zod schemas. Demo viewer gains an Analytics page.
v0.1.29
Trajectory interpolation (TRK-108)
- New
baselode.drill.desurvey.interpolate_trajectory(traces, depths)/ JSinterpolateTrajectory: easting / northing / elevation (and azimuth / dip when present) at any downhole depth; out-of-range depths and unknown holes give NaN rows. attach_assay_positions,attach_structure_positions,intervals_to_omf_linesand JSattachAssayPositionsnow use linear interpolation instead of nearest-sample lookup (positions shift slightly, more accurately, at coarse trace steps).
v0.1.28
Desurvey cross-validation (TRK-117 / TRK-118)
- Desurvey methods are now cross-validated against wellpathpy to ≤ 1 cm.
balanced_tangential(Python + JS) now uses the canonical Walstrom form (average of direction cosines). Behaviour change: positions on strongly doglegged holes can shift by centimetres to metres.
v0.1.27
DrillholeSet container (TRK-113)
- New
DrillholeSet(Python + JS) bundles collar, survey and named interval tables, delegating to the existing APIs:validate(),desurvey()(cached traces), and in Pythoncomposite()andto_omf().
v0.1.25
OMF interop (TRK-111)
- New
baselode.drill.omfround-trips drilling data through Open Mining Format v1 (Leapfrog, Deswik, Micromine, …):collars_to_omf_points,traces_to_omf_lines,intervals_to_omf_lines,build_omf_project,write_omf,read_omf. Requires the newomfextra (pip install baselode[omf]). Python-only.
v0.1.24
Drillhole database validation (TRK-110)
- New
validate_drillhole_db(collar, survey, interval_tables=None, ...)/ JSvalidateDrillholeDbreturns a structured{summary, issues}report (never raises) covering duplicate hole IDs, single-station surveys, azimuth / dip range (allow_full_circleoption), orphan intervals, negative lengths, intervals beyond max depth, gaps, overlaps and below-detection sentinels. - Fix helpers in both languages:
fix_single_station_surveys,replace_below_detection_limit,normalize_azimuth,drop_orphan_intervals,swap_inverted_intervals.
v0.1.23
Interval algebra (TRK-106)
- New
baselode.drill.intervals/ JS equivalents:interval_length,from_to_midpoints,detect_gaps,detect_overlaps,split_at,clipandmerge_tables(intervalLength,fromToMidpoints,detectGaps,detectOverlaps,splitAt,clip,mergeTables).
v0.1.21 – v0.1.22
Strip-log units and selector options
- Strip logs show value units in the axis title, track header, selector and hover.
BaselodeStripLogToolUIaccepts apropertyMetamap (unit,sourceAttribute); helpersformatPropertyLabel,resolvePropertyLabelParts,derivePropertyMeta. TracePlotgainsholeSelector(hole, group + hole, or custom field),showHoleSelect/showPropertySelect/showChartTypeSelect, and keeps dropdowns visible when a hole has no data. Dark-mode styling aligned with light mode.
v0.1.20
Raw GSWA adaptor
- New
baselode.adaptors.raw_gswa(columns,convert,queries, andRawGswaApiClientvia the newapiextra) converts raw GSWA data to the canonical baselode model. - New canonical data-model fields (e.g.
datasource,hole_type,max_depth,surface_sample_id,project_id,crs) in Python and JS, and a per-rowextradict column withbaselode.drill.data.bundle_extras.
v0.1.19
Tool UI components
- New
baselode/tool-uientry withBaselodeStripLogToolUIandBaselode3DSceneToolUI, Zod schemas (SerializableBaselodeStripLogSchema,SerializableBaselode3DSceneSchemaandsafeParse…helpers) andbaselode/tool-ui/style.css. Addszodas a peer dependency.
v0.1.18
Licensing
- Added GPLv3 Section 7 additional permissions for copyright holder Darkmine Pty Ltd.
v0.1.17
Geophysics logs and 3D strip logs
- Downhole geophysics data model and loaders: Python
load_geophysics, LAS viabaselode.drill.las.load_geophysics_las(newlasextra); JSparseGeophysicsCSV,parseLasFile,geophysicsToStripLogs. - Floating 2D strip-log panels beside drillholes in the 3D scene (
setStripLogs/clearStripLogs), background colour toggle and adjustable zoom sensitivity.
v0.1.16
Significant intercepts
- New
significant_intercepts(Pythonbaselode.drill.intercepts) /significantIntercepts(JS) for drillhole assay intercept calculation.
v0.1.15
3D raster overlays
- Georeferenced image overlays in the 3D scene:
createRasterOverlay,addRasterOverlay,removeRasterOverlay,setRasterOverlayOpacity/Visibility/Elevation,listRasterOverlays,clearRasterOverlays.
v0.1.14
Core photo viewer
- New
CorePhotoTable/CorePhotoViewerReact components (zoom / pan, thumbnail → full-res level of detail, side-by-side linked or unlinked holes) withcorePhotoVizhelpers, and Pythonplot_core_photo_log. New "Core photo viewer" guide page.
v0.1.12
Demo viewer Vercel deployment
- Demo viewer now deploys to Vercel via GitHub Apps;
vercel.jsonconfigures the build command, output directory, and SPA rewrites - Build script (
scripts/vercel-build.sh) builds the baselode library from source before building the demo app, resolving the localfile:dependency - GSWA sample data copied from
test/data/gswa/at build time so it is served by the deployed app without committing large CSV files to the demo app directory - Added Data Attribution page listing the GSWA Geochemistry dataset source, CC BY 4.0 licence, and required credit lines
v0.1.11
Plotly template system
- Added
BASELODE_TEMPLATE(Light) andBASELODE_DARK_TEMPLATEnamed Plotly templates for both Python and JavaScript - Light template consolidates all default chart styling (axis lines, grid, tick sizes, trace defaults for scatter/bar/box/heatmap/contour) into a single object
- Dark template (
#1b1b1fbackground, warm grid, yellow accent) matches the docs site dark mode; available as"baselode-dark"in the Plotly template registry templateparameter added to all strip-log helpers (buildPlotConfig,buildCategoricalStripLogConfig,TracePlot,plot_drillhole_trace, etc.)- Updated documentation with theming and colour mapping guide sections
v0.1.10
Semantic colour mapping
- Added built-in colour maps:
'commodity'(18 elements — Au, Ag, Cu, …) and'lithology'(~30 rock types) - Numeric strip-log traces auto-detect commodity elements in column names (e.g.
Au_ppm,cu_pct) and apply the matching colour without configuration colourMapparameter added to categorical strip-log helpers in both JS and PythongetColour/get_colourandresolveColourMap/resolve_colour_mapexported for direct use
v0.1.9
CI fix
- Fixed GitHub Actions release workflow: corrected tag detection in the publish pipeline (two follow-up patches)
v0.1.8
GitHub Actions CI/CD
- Added automated release workflow: on tag push, runs tests, builds Python and JS packages, publishes to PyPI and npm
- Added separate test-build workflow for inspecting build artefacts without publishing
v0.1.7
Polygonal grade block viewer
- New
grade_blocksmodule (Python + JS) for loading and rendering closed polyhedral meshes (grade shells, geologic domains) - JSON schema v1.0 for grade block datasets (vertices, triangles, attributes, material colour/opacity)
- 3D selection glow and edge-highlight on click;
addGradeBlocksToSceneintegrates with the existingBaselode3DScene - New Polygon Blocks demo page in the demo viewer
v0.1.6
Geology / lithology strip logs
- Added geology interval loading (
load_geology/loadGeology) andbuildCategoricalStripLogConfigfor rendering banded colour strip logs directly from interval rows - Improved strip log rendering performance; faster demo app data loading
v0.1.5
3D selection glow + documentation site
- Outline selection shader (
OutlinePassviaEffectComposer) applied to drillholes, structural discs, and block model blocks on click - VitePress documentation site launched and deployed to Vercel
- 3D scene code refactored; screenshots added to docs
v0.1.4
Block model viewer
- New
blockmodelsubpackage: CSV loading, column normalisation, validation, property stats, and block filtering (Python + JS) BlockModelWidgetReact component for interactive 3D voxel rendering with property-based colour mapping
v0.1.3
3D structural discs
- Structural measurements rendered as oriented discs in the 3D scene
buildStructuralDiscs+setStructuralDiscspipeline: interpolates trace positions, converts dip/azimuth to disc normals, renders via Three.js
v0.1.2
Structural geology data support
- Alpha/beta and dip/azimuth structural point measurements: data loading, column normalisation, tadpole strip-log rendering
load_structures(Python),parseStructuralPointsCSV(JS),buildTadpoleConfigandbuildStructuralStripConfigvisualisation helpers- Library clean-up and expanded column name normalisation
v0.1.1
Packaging fix
- Fixed npm package configuration for local release
v0.1.0
Initial release
- Python package: drillhole data loading (
load_collars,load_surveys,load_assays), minimum curvature desurveying, Plotly 2D strip logs and collar map - JavaScript/React library: CSV parsing, column normalisation, desurveying, Plotly strip logs (
buildPlotConfig,TracePlot), Three.js 3D scene with orbit controls, drillhole cylinder rendering - Demo viewer React app with GSWA sample data
