baselode 0.1.1

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"baselode.js","sources":["../src/data/datamodel.js","../src/data/keying.js","../src/data/assayFieldSets.js","../src/data/dataErrorUtils.js","../src/data/assayLoader.js","../src/data/csvRowUtils.js","../src/data/traceGridConfig.js","../src/data/assayDataLoader.js","../src/data/desurvey.js","../src/data/desurveyMethods.js","../src/data/drillholeLoader.js","../src/data/datasetLoader.js","../src/data/blockModelLoader.js","../src/viz/drillholeViz.js","../src/viz/TracePlot.jsx","../src/viz/useDrillholeTraceGrid.jsx","../src/viz/assayColorScale.js","../src/viz/view2d.js","../src/viz/view3dPayload.js","../src/viz/baselode3dCameraControls.js","../src/viz/baselode3dScene.js","../src/viz/Baselode3DControls.jsx"],"sourcesContent":["/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Baselode Open Data Model for JavaScript/TypeScript\n *\n * Provides a consistent schema for data handling throughout the library.\n *\n * Individual data loaders apply common column mapping, but also accept user-provided column maps to handle variations in source data.\n */\n\n// Standard field names\nexport const HOLE_ID = \"hole_id\";\nexport const LATITUDE = \"latitude\";\nexport const LONGITUDE = \"longitude\";\nexport const ELEVATION = \"elevation\";\nexport const AZIMUTH = \"azimuth\";\nexport const DIP = \"dip\";\nexport const FROM = \"from\";\nexport const TO = \"to\";\nexport const MID = \"mid\";\nexport const PROJECT_ID = \"project_id\";\nexport const EASTING = \"easting\";\nexport const NORTHING = \"northing\";\nexport const CRS = \"crs\";\nexport const DEPTH = \"depth\";\n\n/**\n * Minimum expected columns for drillhole collars\n * The collar forms the basis for hole_id and spatial location, so it is expected to exist in all datasets and be standardized as much as possible.\n */\nexport const BASELODE_DATA_MODEL_DRILL_COLLAR = {\n // A unique hole identifier across the entire dataset and all future data sets\n [HOLE_ID]: \"string\",\n // The hole ID from the original collar source\n \"datasource_hole_id\": \"string\",\n // The project ID or project code from the original collar source, if available\n [PROJECT_ID]: \"string\",\n // The latitude of the collar, in decimal degrees (WGS84)\n [LATITUDE]: \"number\",\n // The longitude of the collar, in decimal degrees (WGS84)\n [LONGITUDE]: \"number\",\n // The elevation of the collar, in meters above sea level (WGS84)\n [ELEVATION]: \"number\",\n // The easting coordinate of the collar, in meters (projected CRS)\n [EASTING]: \"number\",\n // The northing coordinate of the collar, in meters (projected CRS)\n [NORTHING]: \"number\",\n // The coordinate reference system of the collar coordinates for easting/northing, as an EPSG code or proj string\n [CRS]: \"string\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_SURVEY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the survey measurement was taken / started\n [DEPTH]: \"number\",\n // The depth along the hole where the survey measurement ended, if applicable (some surveys are point measurements and may not have a 'to' depth)\n [TO]: \"number\",\n // The azimuth of the hole at the survey depth, in degrees from north\n [AZIMUTH]: \"number\",\n // The dip of the hole at the survey depth, in degrees from horizontal (negative values indicate downward inclination)\n [DIP]: \"number\"\n};\n\nexport const BASELODE_DATA_MODEL_DRILL_ASSAY = {\n // The unique hole id that maps to the collar and any other data tables\n [HOLE_ID]: \"string\",\n // The depth along the hole where the assay interval starts\n [FROM]: \"number\",\n // The depth along the hole where the assay interval ends\n [TO]: \"number\",\n // The midpoint depth of the assay interval\n [MID]: \"number\",\n // assay value columns are variable and not standardized here. \n // Assays may be flattened (one column per assay type) or long (one row per assay type with an additional 'assay_type' column)\n};\n\n/**\n * This column map is used to make a 'best guess' for mapping common variations in source column names to the baselode data model.\n * It is applied in the standardizeColumns function, but users can also provide their own column map to override or extend this mapping as needed.\n * The keys from the input source are normalized to lowercase and stripped of whitespace for more robust matching.\n * This dictionary is stored for human readability, then pivoted to make lookup quicker in code.\n * Be cautious of not mapping a source column to multiple baselode columns, as this can lead to unpredictable results.\n */\nexport const DEFAULT_COLUMN_MAP = {\n [HOLE_ID]: [\"hole_id\", \"holeid\", \"hole id\", \"hole-id\"],\n \"datasource_hole_id\": [\"datasource_hole_id\", \"datasourceholeid\", \"datasource hole id\", \"datasource-hole-id\", \"company_hole_id\", \"companyholeid\", \"company hole id\", \"company-hole-id\"],\n [PROJECT_ID]: [\"project_id\", \"projectid\", \"project id\", \"project-id\", \"project_code\", \"projectcode\", \"project code\", \"project-code\", \"companyId\", \"company_id\", \"companyid\", \"company id\", \"company-id\", \"dataset\", \"project\"],\n [LATITUDE]: [\"latitude\", \"lat\"],\n [LONGITUDE]: [\"longitude\", \"lon\"],\n [ELEVATION]: [\"elevation\", \"rl\", \"elev\", \"z\"],\n [EASTING]: [\"easting\", \"x\"],\n [NORTHING]: [\"northing\", \"y\"],\n [CRS]: [\"crs\", \"epsg\", \"projection\"],\n [FROM]: [\"from\", \"depth_from\", \"from_depth\", \"samp_from\", \"sample_from\", \"sampfrom\", \"fromdepth\"],\n [TO]: [\"to\", \"depth_to\", \"to_depth\", \"samp_to\", \"sample_to\", \"sampto\", \"todepth\"],\n [AZIMUTH]: [\"azimuth\", \"az\", \"dipdir\", \"dip_direction\"],\n [DIP]: [\"dip\"],\n \"declination\": [\"declination\", \"dec\"],\n [DEPTH]: [\"depth\", \"survey_depth\", \"surveydepth\"]\n};\n\n/**\n * Pivot the DEFAULT_COLUMN_MAP for efficient reverse lookup\n * Maps normalized column names -> standardized baselode column names\n * @private\n */\nexport const _COLUMN_LOOKUP = {};\nfor (const [standardCol, variations] of Object.entries(DEFAULT_COLUMN_MAP)) {\n for (const variation of variations) {\n const normalized = variation.toLowerCase().trim();\n _COLUMN_LOOKUP[normalized] = standardCol;\n }\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { _COLUMN_LOOKUP, DEFAULT_COLUMN_MAP, HOLE_ID } from './datamodel.js';\n\n/**\n * Normalize a field name to lowercase with underscores\n * @param {string} name - The field name to normalize\n * @returns {string} - The normalized field name\n */\nexport function normalizeFieldName(name) {\n return (name || '').toString().trim().toLowerCase().replace(/\\s+/g, '_');\n}\n\n/**\n * Standardize column names in a row object using the baselode data model\n * @param {Object} row - The row object with arbitrary column names\n * @param {Object} [columnMap] - Optional column map to use (defaults to DEFAULT_COLUMN_MAP)\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Object} - The row object with standardized column names\n */\nexport function standardizeColumns(row, columnMap = null, sourceColumnMap = null) {\n const lookup = { ..._COLUMN_LOOKUP };\n \n // Add user-provided column mappings\n if (sourceColumnMap) {\n for (const [rawName, expectedName] of Object.entries(sourceColumnMap)) {\n if (rawName != null && expectedName != null) {\n const normalizedKey = normalizeFieldName(rawName);\n const normalizedValue = normalizeFieldName(expectedName);\n lookup[normalizedKey] = normalizedValue;\n }\n }\n }\n\n const renamed = {};\n for (const [col, value] of Object.entries(row)) {\n const key = normalizeFieldName(col);\n const mapped = lookup[key] || key;\n renamed[mapped] = value;\n }\n\n return renamed;\n}\n\n/**\n * Standardize an array of rows (e.g., from CSV parsing)\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map to use\n * @param {Object} [sourceColumnMap] - Optional additional column mappings from user\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeRowArray(rows, columnMap = null, sourceColumnMap = null) {\n return rows.map(row => standardizeColumns(row, columnMap, sourceColumnMap));\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nexport const ASSAY_NON_VALUE_FIELDS = new Set([\n 'hole_id',\n 'holeid',\n 'id',\n 'holeId',\n 'project_code',\n 'project',\n 'latitude',\n 'longitude',\n 'lat',\n 'lng',\n 'elevation',\n 'dip',\n 'azimuth',\n 'holetype',\n 'shape',\n 'anumber',\n 'collarid',\n 'companyholeid',\n 'company_hole_id',\n 'samp_from',\n 'samp_to',\n 'sample_from',\n 'sample_to',\n 'from',\n 'to',\n 'depth_from',\n 'depth_to',\n 'fromdepth',\n 'todepth',\n 'comment',\n 'z'\n]);","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\nconst DATA_LOG_PREFIX = '[baselode:data]';\n\n/**\n * Convert any value to an Error object\n * @param {*} error - Value to convert (Error, string, or other)\n * @param {string} fallbackMessage - Message to use if error is not Error or string\n * @returns {Error} Error object\n */\nexport function toError(error, fallbackMessage = 'Unknown error') {\n if (error instanceof Error) return error;\n const message = typeof error === 'string' && error.trim() ? error : fallbackMessage;\n return new Error(message);\n}\n\n/**\n * Wrap an error with contextual information about where it occurred\n * @param {string} context - Context description (e.g., function name)\n * @param {*} error - Original error\n * @param {string} fallbackMessage - Fallback message if error cannot be parsed\n * @returns {Error} Wrapped error with context and original as cause\n */\nexport function withDataErrorContext(context, error, fallbackMessage = 'Operation failed') {\n const baseError = toError(error, fallbackMessage);\n const wrapped = new Error(`${context}: ${baseError.message}`);\n wrapped.cause = baseError;\n return wrapped;\n}\n\n/**\n * Log a warning message with optional error object\n * @param {string} message - Warning message\n * @param {Error} [error] - Optional error object to log\n */\nexport function logDataWarning(message, error) {\n if (error !== undefined) {\n console.warn(`${DATA_LOG_PREFIX} ${message}`, error);\n return;\n }\n console.warn(`${DATA_LOG_PREFIX} ${message}`);\n}\n\n/**\n * Log an informational message\n * @param {string} message - Info message\n */\nexport function logDataInfo(message) {\n console.info(`${DATA_LOG_PREFIX} ${message}`);\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, FROM, TO, PROJECT_ID } from './datamodel.js';\n\n/**\n * Normalize a raw CSV row to use standardized column names\n * @private\n */\nconst normalizeRow = (rawRow, sourceColumnMap = null) => standardizeColumns(rawRow, null, sourceColumnMap);\n\n/**\n * Extract hole ID from a normalized row\n * @private\n * @param {Object} row - Normalized row object\n * @returns {{holeId: string}} Object containing hole ID\n */\nfunction extractIdFields(row) {\n const holeId = row[HOLE_ID];\n return { holeId };\n}\n\n/**\n * Extract and validate assay interval data from a row\n * @private\n * @param {Object} row - Normalized row object\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object|null} Interval object or null if invalid\n */\nfunction extractInterval(row, sourceColumnMap = null) {\n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n if (!holeId) return null;\n\n const project = row[PROJECT_ID] || row.project || row.project_code;\n const from = Number(row[FROM]);\n const to = Number(row[TO]);\n if (!Number.isFinite(from) || !Number.isFinite(to) || to <= from) return null;\n\n return {\n holeId,\n project,\n from,\n to,\n ...row\n };\n}\n\n/**\n * Convert array of intervals to a hole object with points\n * @private\n * @param {string} holeId - Hole identifier\n * @param {Array<Object>} intervals - Array of interval objects\n * @returns {{id: string, project: string, points: Array<Object>}} Hole object\n */\nfunction intervalsToHole(holeId, intervals) {\n const sorted = intervals.sort((a, b) => a.from - b.from);\n const points = [];\n sorted.forEach((iv) => {\n const { from, to, project, ...rest } = iv;\n const pointData = {\n z: from,\n from,\n to,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n ...rest\n };\n points.push(pointData);\n points.push({ ...pointData, z: to });\n });\n return { id: holeId, project: sorted[0]?.project, points };\n}\n\n/**\n * Parse assay CSV to extract unique hole IDs (quick pass, no interval data)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<string>>} Array of unique hole IDs\n */\nexport function parseAssayHoleIds(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const holeIds = new Set();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const hid = row[HOLE_ID];\n if (hid !== undefined && `${hid}`.trim() !== '') {\n holeIds.add(`${hid}`.trim());\n }\n },\n complete: () => resolve(Array.from(holeIds)),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIds', error))\n });\n });\n}\n\n/**\n * Check if a row has at least one non-null assay value\n * @private\n * @param {Object} row - Normalized row object\n * @returns {boolean} True if row contains assay data\n */\nfunction hasAssayValue(row) {\n return Object.entries(row || {}).some(([k, v]) => {\n if (ASSAY_NON_VALUE_FIELDS.has(k)) return false;\n if (v === undefined || v === null) return false;\n if (typeof v === 'string' && v.trim() === '') return false;\n return true;\n });\n}\n\n/**\n * Parse assay CSV to extract hole IDs that have at least one assay value (quick pass)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<{holeId: string}>>} Array of objects with hole IDs that have assay data\n */\nexport function parseAssayHoleIdsWithAssays(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const byHole = new Map();\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n if (!hasAssayValue(row)) return;\n const ids = extractIdFields(row);\n const hid = ids.holeId;\n if (hid !== undefined && `${hid}`.trim() !== '') {\n const key = `${hid}`.trim();\n if (!byHole.has(key)) {\n byHole.set(key, {\n holeId: key\n });\n }\n }\n },\n complete: () => resolve(Array.from(byHole.values())),\n error: (error) => reject(withDataErrorContext('parseAssayHoleIdsWithAssays', error))\n });\n });\n}\n\n/**\n * Parse assay CSV for a single hole's intervals\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to extract\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object|null>} Hole object with intervals or null if not found\n */\nexport function parseAssayHole(file, holeId, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n const wanted = `${holeId}`.trim();\n if (!wanted) {\n reject(withDataErrorContext('parseAssayHole', new Error('Missing hole id')));\n return;\n }\n const intervals = [];\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n step: (results) => {\n const row = normalizeRow(results.data, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (`${interval.holeId}`.trim() !== wanted) return;\n intervals.push(interval);\n },\n complete: () => {\n if (!intervals.length) {\n resolve(null);\n return;\n }\n const hole = intervalsToHole(wanted, intervals);\n resolve(hole);\n },\n error: (error) => reject(withDataErrorContext('parseAssayHole', error))\n });\n });\n}\n\n/**\n * Parse complete assay CSV file with all holes and intervals (eager load)\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<{holes: Array<Object>}>} Object containing array of all holes with intervals\n */\nexport function parseAssaysCSV(file, config = null, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow) => {\n const row = normalizeRow(rawRow, sourceColumnMap);\n const interval = extractInterval(row, sourceColumnMap);\n if (!interval) return;\n if (!byHole.has(interval.holeId)) byHole.set(interval.holeId, []);\n byHole.get(interval.holeId).push(interval);\n });\n\n const holes = Array.from(byHole.entries()).map(([hid, intervals]) => intervalsToHole(hid, intervals));\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseAssaysCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { normalizeFieldName, standardizeColumns } from './keying.js';\n\n/**\n * Normalize a CSV row by converting all keys to lowercase with underscores\n * @deprecated Use standardizeColumns from keying.js instead for full data model support\n */\nexport function normalizeCsvRow(row = {}) {\n const normalized = {};\n Object.entries(row || {}).forEach(([key, value]) => {\n if (!key) return;\n normalized[normalizeFieldName(key)] = value;\n });\n return normalized;\n}\n\n/**\n * Pick the first present value from a list of keys in a normalized row\n * @param {Object} normalized - The normalized row object\n * @param {Array<string>} keys - Array of keys to check\n * @param {*} fallback - Fallback value if none found\n * @returns {*} - The first present value or fallback\n */\nexport function pickFirstPresent(normalized = {}, keys = [], fallback) {\n for (const key of keys) {\n const value = normalized[key];\n if (value !== undefined && value !== null && `${value}`.trim() !== '') {\n return value;\n }\n }\n return fallback;\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default number of trace plots in a grid */\nexport const DEFAULT_TRACE_PLOT_COUNT = 4;\n\n/**\n * Reorder hole IDs to place a focused hole first\n * @param {Array<string>} ids - Array of hole IDs\n * @param {string} focusId - Hole ID to place first\n * @returns {Array<string>} Reordered array with focusId first if found\n */\nexport function reorderHoleIds(ids = [], focusId = '') {\n if (!ids.length) return [];\n if (!focusId) return ids;\n const matchIdx = ids.findIndex((id) => id === focusId);\n if (matchIdx === -1) return ids;\n const selected = ids[matchIdx];\n const rest = ids.filter((_, idx) => idx !== matchIdx);\n return [selected, ...rest];\n}\n\n/**\n * Determine appropriate chart type for a property\n * @param {Object} options - Configuration options\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Requested chart type\n * @param {Array<string>} options.categoricalProps - List of categorical property names\n * @param {string} options.numericDefaultChartType - Default chart type for numeric properties\n * @returns {string} Coerced chart type ('categorical', 'line', 'markers+line', etc.)\n */\nexport function coerceChartTypeForProperty({\n property = '',\n chartType = '',\n categoricalProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n if (!property) return chartType || numericDefaultChartType;\n const isCategorical = categoricalProps.includes(property);\n if (isCategorical) return 'categorical';\n if (!chartType || chartType === 'categorical') return numericDefaultChartType;\n return chartType;\n}\n\n/**\n * Build trace plot configuration objects for a grid of hole IDs\n * @param {Object} options - Configuration options\n * @param {Array<string>} options.holeIds - Array of hole IDs\n * @param {string} options.focusedHoleId - Hole ID to focus (place first)\n * @param {number} options.plotCount - Number of plots in grid\n * @param {string} options.defaultProp - Default property to display\n * @param {Array<string>} options.categoricalProps - List of categorical properties\n * @param {string} options.numericDefaultChartType - Default chart type for numeric props\n * @returns {Array<{holeId: string, property: string, chartType: string}>} Array of trace configurations\n */\nexport function buildTraceConfigsForHoleIds({\n holeIds = [],\n focusedHoleId = '',\n plotCount = DEFAULT_TRACE_PLOT_COUNT,\n defaultProp = '',\n categoricalProps = [],\n numericDefaultChartType = 'markers+line'\n} = {}) {\n const ordered = reorderHoleIds(holeIds, focusedHoleId);\n return Array.from({ length: plotCount }).map((_, idx) => {\n const holeId = ordered[idx] || holeIds[idx] || '';\n const chartType = coerceChartTypeForProperty({\n property: defaultProp,\n chartType: '',\n categoricalProps,\n numericDefaultChartType\n });\n return {\n holeId,\n property: defaultProp,\n chartType\n };\n });\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { parseAssayHole, parseAssayHoleIdsWithAssays, parseAssaysCSV } from './assayLoader.js';\nimport { ASSAY_NON_VALUE_FIELDS } from './assayFieldSets.js';\nimport { buildTraceConfigsForHoleIds, reorderHoleIds } from './traceGridConfig.js';\n\nexport { reorderHoleIds };\n\n/**\n * Derive numeric and categorical property names from assay hole data\n * @param {Array<Object>} holes - Array of hole objects with points containing assay data\n * @returns {{numericProps: Array<string>, categoricalProps: Array<string>, defaultProp: string}} Property classification\n */\nexport function deriveAssayProps(holes = []) {\n const points = holes.flatMap((h) => h.points || []);\n const candidates = new Set();\n points.forEach((p) => {\n Object.keys(p || {}).forEach((k) => {\n if (!ASSAY_NON_VALUE_FIELDS.has(k)) candidates.add(k);\n });\n });\n\n const numericProps = [];\n const categoricalProps = [];\n\n candidates.forEach((key) => {\n let hasNumber = false;\n let hasValue = false;\n for (let i = 0; i < points.length; i += 1) {\n const v = points[i]?.[key];\n if (v === undefined || v === null || (typeof v === 'string' && v.trim() === '')) continue;\n hasValue = true;\n if (typeof v === 'number' && Number.isFinite(v)) {\n hasNumber = true;\n break;\n }\n }\n if (hasNumber) {\n numericProps.push(key);\n } else if (hasValue) {\n categoricalProps.push(key);\n }\n });\n\n const defaultProp = numericProps[0] || categoricalProps[0] || '';\n\n return { numericProps, categoricalProps, defaultProp };\n}\n\n/**\n * Load metadata (hole IDs) from an assay CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Array<{holeId: string}>>} Array of hole IDs with assay data\n */\nexport async function loadAssayMetadata(file, config = null) {\n const holeIds = await parseAssayHoleIdsWithAssays(file);\n return holeIds;\n}\n\n/**\n * Load assay intervals for a specific hole from CSV file\n * @param {File|Blob} file - Assay CSV file\n * @param {string} holeId - Hole identifier to load\n * @param {Object|null} config - Optional configuration (unused, for backwards compatibility)\n * @returns {Promise<Object|null>} Hole object with assay intervals or null if not found\n */\nexport async function loadAssayHole(file, holeId, config = null) {\n const hole = await parseAssayHole(file, holeId);\n return hole;\n}\n\n/**\n * Build complete assay state from hole data including property analysis and trace configs\n * @param {Array<Object>} holes - Array of hole objects with assay data\n * @param {string} focusedHoleId - Hole ID to focus on (will be first in trace configs)\n * @returns {Object|null} Assay state object with holes, properties, and trace configs\n */\nexport function buildAssayState(holes = [], focusedHoleId = '') {\n if (!holes.length) return null;\n const { numericProps, categoricalProps, defaultProp } = deriveAssayProps(holes);\n const holeIds = holes.map((h) => h.id || h.holeId).filter(Boolean);\n const traceConfigs = buildTraceConfigsForHoleIds({\n holeIds,\n focusedHoleId,\n plotCount: 4,\n defaultProp,\n categoricalProps,\n numericDefaultChartType: 'line'\n });\n return {\n holes,\n numericProps,\n categoricalProps,\n defaultProp,\n traceConfigs\n };\n}\n\n/**\n * Load and parse complete assay CSV file into state object\n * @param {File|Blob} file - Assay CSV file\n * @param {string} focusedHoleId - Hole ID to focus on\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Object>} Complete assay state\n * @throws {Error} If no valid assay intervals found\n */\nexport async function loadAssayFile(file, focusedHoleId = '', sourceColumnMap = null) {\n const { holes } = await parseAssaysCSV(file, sourceColumnMap);\n const state = buildAssayState(holes, focusedHoleId);\n if (!state) throw new Error('No valid assay intervals found.');\n return state;\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, LATITUDE, LONGITUDE, AZIMUTH, DIP, DEPTH, PROJECT_ID } from './datamodel.js';\n\n/**\n * Parse survey CSV file containing downhole survey measurements\n * Expected columns: hole_id, depth, azimuth, dip\n * @param {File|Blob} file - Survey CSV file\n * @param {Object|null} sourceColumnMap - Optional column name mappings\n * @returns {Promise<Array<Object>>} Array of normalized survey rows\n */\nexport function parseSurveyCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const rows = results.data\n .map((row) => normalizeRow(row, sourceColumnMap))\n .filter((row) => row[HOLE_ID] && Number.isFinite(row[DEPTH]) && Number.isFinite(row[DIP]) && Number.isFinite(row[AZIMUTH]));\n resolve(rows);\n },\n error: (err) => reject(withDataErrorContext('parseSurveyCSV', err))\n });\n });\n}\n\n/**\n * Normalize a survey row to standardized field names\n * @private\n * @param {Object} row - Raw survey row\n * @param {Object|null} sourceColumnMap - Optional column mappings\n * @returns {Object} Normalized row with standardized field names\n */\nfunction normalizeRow(row, sourceColumnMap = null) {\n const norm = standardizeColumns(row, null, sourceColumnMap);\n\n const holeId = norm[HOLE_ID];\n const project = norm[PROJECT_ID] || norm.project || norm.project_code;\n const lat = toNumber(norm[LATITUDE]);\n const lng = toNumber(norm[LONGITUDE]);\n const surveyDepth = toNumber(norm[DEPTH]);\n const dip = toNumber(norm[DIP]);\n const azimuth = toNumber(norm[AZIMUTH]);\n const maxDepth = toNumber(norm.maxdepth);\n\n return {\n raw: norm,\n [HOLE_ID]: holeId,\n [PROJECT_ID]: project,\n [LATITUDE]: lat,\n [LONGITUDE]: lng,\n [DEPTH]: surveyDepth,\n [DIP]: dip,\n [AZIMUTH]: azimuth,\n maxdepth: maxDepth,\n // Legacy field names for backwards compatibility\n project_code: project,\n latitude: lat,\n longitude: lng,\n surveydepth: surveyDepth\n };\n}\n\n/**\n * Convert value to number, returning undefined if not finite\n * @private\n * @param {*} v - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nconst toNumber = (v) => {\n const n = Number(v);\n return Number.isFinite(n) ? n : undefined;\n};\n\n/**\n * Desurvey drillhole traces using minimum curvature method\n * Converts survey measurements (azimuth, dip, depth) to 3D coordinates (x, y, z)\n * @param {Array<Object>} collars - Array of collar objects with location data\n * @param {Array<Object>} surveys - Array of survey measurement objects\n * @returns {Array<{id: string, project: string, points: Array<{x: number, y: number, z: number, md: number, azimuth: number, dip: number, lat: number, lng: number}>, collar: Object}>} Array of desurveyed holes with 3D points\n */\nexport function desurveyTraces(collars, surveys) {\n const collarByKey = new Map();\n collars.forEach((c) => {\n const holeId = (c[HOLE_ID] || c.holeId || c.id || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!collarByKey.has(key)) {\n collarByKey.set(key, c);\n }\n });\n\n const refLat = collars[0]?.lat ?? collars[0]?.[LATITUDE] ?? 0;\n const refLng = collars[0]?.lng ?? collars[0]?.[LONGITUDE] ?? 0;\n const refMetersPerDegLat = 111132;\n const refMetersPerDegLon = 111320 * Math.cos((refLat * Math.PI) / 180);\n\n const grouped = new Map();\n surveys.forEach((s) => {\n const holeId = (s[HOLE_ID] || '').toString().trim();\n if (!holeId) return;\n const key = holeId.toLowerCase();\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(s);\n });\n\n const holes = [];\n grouped.forEach((stations, key) => {\n const collar = collarByKey.get(key);\n if (!collar) return;\n const sorted = stations\n .filter((s) => Number.isFinite(s[DEPTH] ?? s.surveydepth))\n .sort((a, b) => (a[DEPTH] ?? a.surveydepth) - (b[DEPTH] ?? b.surveydepth));\n if (!sorted.length) return;\n\n const lat0 = collar.lat ?? collar[LATITUDE];\n const lng0 = collar.lng ?? collar[LONGITUDE];\n const metersPerDegLat = 111132;\n const metersPerDegLon = 111320 * Math.cos((lat0 * Math.PI) / 180);\n const baseX = (lng0 - refLng) * refMetersPerDegLon;\n const baseY = (lat0 - refLat) * refMetersPerDegLat;\n\n const points = [];\n let accX = 0;\n let accY = 0;\n let accZ = 0;\n\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const prev = sorted[i - 1];\n const currDepth = curr[DEPTH] ?? curr.surveydepth;\n const currAzimuth = curr[AZIMUTH] ?? curr.azimuth;\n const currDip = curr[DIP] ?? curr.dip;\n \n if (!prev) {\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: 0,\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n continue;\n }\n\n const prevDepth = prev[DEPTH] ?? prev.surveydepth;\n const prevAzimuth = prev[AZIMUTH] ?? prev.azimuth;\n const prevDip = prev[DIP] ?? prev.dip;\n \n const deltaMD = currDepth - prevDepth;\n if (deltaMD <= 0) continue;\n\n const inc1 = toInclination(prevDip);\n const inc2 = toInclination(currDip);\n const az1 = degToRad(prevAzimuth);\n const az2 = degToRad(currAzimuth);\n\n const beta = Math.acos(\n Math.sin(inc1) * Math.sin(inc2) * Math.cos(az1 - az2) +\n Math.cos(inc1) * Math.cos(inc2)\n );\n\n const rf = beta > 1e-6 ? (2 / beta) * Math.tan(beta / 2) : 1;\n\n const dx = 0.5 * deltaMD * (Math.sin(inc1) * Math.cos(az1) + Math.sin(inc2) * Math.cos(az2)) * rf;\n const dy = 0.5 * deltaMD * (Math.sin(inc1) * Math.sin(az1) + Math.sin(inc2) * Math.sin(az2)) * rf;\n const dzDown = 0.5 * deltaMD * (Math.cos(inc1) + Math.cos(inc2)) * rf; // down is positive\n\n accX += dx;\n accY += dy;\n accZ += dzDown;\n\n points.push({\n x: baseX + accX,\n y: baseY + accY,\n z: -accZ, // render with z up; depth down\n md: currDepth,\n azimuth: currAzimuth,\n dip: currDip\n });\n }\n\n // convert local meters back to approx lat/lon for map overlay if needed\n const pointsWithGeo = points.map((p) => ({\n ...p,\n lat: lat0 + (p.y / metersPerDegLat),\n lng: lng0 + (p.x / metersPerDegLon)\n }));\n\n holes.push({\n id: collar[HOLE_ID] || collar.holeId || key,\n project: collar[PROJECT_ID] || collar.project_id || collar.project || '',\n points: pointsWithGeo,\n collar\n });\n });\n\n return holes;\n}\n\n/**\n * Convert degrees to radians\n * @private\n * @param {number} d - Degrees\n * @returns {number} Radians\n */\nconst degToRad = (d) => (d * Math.PI) / 180;\n\n/**\n * Convert dip (negative downward from horizontal) to inclination from vertical\n * @private\n * @param {number} dipDeg - Dip in degrees (negative = downward)\n * @returns {number} Inclination in radians from vertical (0 = vertical down)\n */\nconst toInclination = (dipDeg) => {\n const val = Number(dipDeg);\n const incDeg = 90 + (Number.isFinite(val) ? val : 0); // dip -60 => inc 30\n const clamped = Math.min(180, Math.max(0, incDeg));\n return degToRad(clamped);\n};\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { withDataErrorContext } from './dataErrorUtils.js';\n/**\n * Convert value to number with optional fallback\n * @private\n */function toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Normalize hole ID value to trimmed string\n * @private\n */\nfunction normalizeHoleIdValue(value) {\n if (value === undefined || value === null) return '';\n return `${value}`.trim();\n}\n\n/**\n * Canonicalize hole ID column across rows with varying column names\n * @private\n */\nfunction canonicalizeHoleIdRows(rows = [], holeIdCol = null) {\n const preferred = holeIdCol || 'hole_id';\n const candidates = [preferred, 'hole_id', 'holeId', 'id'];\n const resolved = candidates.find((col) => rows.some((row) => normalizeHoleIdValue(row?.[col])));\n if (!resolved) {\n throw withDataErrorContext('canonicalizeHoleIdRows', new Error(`hole id column '${preferred}' not found`));\n }\n return {\n aliasCol: resolved,\n rows: rows.map((row) => ({\n ...row,\n hole_id: normalizeHoleIdValue(row?.[resolved])\n }))\n };\n}\n\n/**\n * Convert degrees to radians\n * @private\n */\nfunction degToRad(angle) {\n return (Number(angle) * Math.PI) / 180;\n}\n\n/**\n * Calculate direction cosines from azimuth and dip\n * @private\n */\nfunction directionCosines(azimuth, dip) {\n const azRad = degToRad(azimuth);\n const dipRad = degToRad(dip);\n const ca = Math.cos(dipRad) * Math.sin(azRad);\n const cb = Math.cos(dipRad) * Math.cos(azRad);\n const cc = Math.sin(dipRad) * -1;\n return { ca, cb, cc };\n}\n\n/**\n * Calculate displacement vector for a survey segment using specified method\n * @private\n */\nfunction segmentDisplacement(deltaMd, az0, dip0, az1, dip1, method = 'minimum_curvature') {\n const dc0 = directionCosines(az0, dip0);\n const dc1 = directionCosines(az1, dip1);\n\n if (method === 'tangential') {\n return {\n dx: deltaMd * dc0.ca,\n dy: deltaMd * dc0.cb,\n dz: deltaMd * dc0.cc,\n azimuth: az0,\n dip: dip0\n };\n }\n\n if (method === 'balanced_tangential') {\n const azAvg = 0.5 * (az0 + az1);\n const dipAvg = 0.5 * (dip0 + dip1);\n const dcAvg = directionCosines(azAvg, dipAvg);\n return {\n dx: deltaMd * dcAvg.ca,\n dy: deltaMd * dcAvg.cb,\n dz: deltaMd * dcAvg.cc,\n azimuth: azAvg,\n dip: dipAvg\n };\n }\n\n const dot = (dc0.ca * dc1.ca) + (dc0.cb * dc1.cb) + (dc0.cc * dc1.cc);\n const dogleg = Math.acos(Math.max(-1, Math.min(1, dot)));\n const rf = dogleg > 1e-6 ? (2 * Math.tan(dogleg / 2)) / dogleg : 1;\n\n return {\n dx: 0.5 * deltaMd * (dc0.ca + dc1.ca) * rf,\n dy: 0.5 * deltaMd * (dc0.cb + dc1.cb) * rf,\n dz: 0.5 * deltaMd * (dc0.cc + dc1.cc) * rf,\n azimuth: az1,\n dip: dip1\n };\n}\n\nfunction desurvey(rowsCollars = [], rowsSurveys = [], options = {}) {\n const {\n step = 1,\n holeIdCol = null,\n method = 'minimum_curvature'\n } = options;\n\n const safeStep = Number.isFinite(Number(step)) && Number(step) > 0 ? Number(step) : 1;\n\n const collarsCanonical = canonicalizeHoleIdRows(rowsCollars, holeIdCol);\n const surveysCanonical = canonicalizeHoleIdRows(rowsSurveys, holeIdCol || collarsCanonical.aliasCol);\n\n if (!collarsCanonical.rows.length || !surveysCanonical.rows.length) return [];\n\n const collarsByHole = new Map();\n collarsCanonical.rows.forEach((row) => {\n if (!row.hole_id || collarsByHole.has(row.hole_id)) return;\n collarsByHole.set(row.hole_id, row);\n });\n\n const surveysByHole = new Map();\n surveysCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!surveysByHole.has(row.hole_id)) surveysByHole.set(row.hole_id, []);\n surveysByHole.get(row.hole_id).push(row);\n });\n\n const out = [];\n surveysByHole.forEach((stations, holeId) => {\n const collar = collarsByHole.get(holeId);\n if (!collar) return;\n\n const sorted = [...stations]\n .map((row) => ({\n ...row,\n from: toNumber(row.from),\n azimuth: toNumber(row.azimuth),\n dip: toNumber(row.dip)\n }))\n .filter((row) => Number.isFinite(row.from) && Number.isFinite(row.azimuth) && Number.isFinite(row.dip))\n .sort((a, b) => a.from - b.from);\n\n if (!sorted.length) return;\n\n let x = toNumber(collar.x, 0);\n let y = toNumber(collar.y, 0);\n let z = toNumber(collar.z, 0);\n let mdCursor = sorted[0].from;\n const azPrev = sorted[0].azimuth;\n const dipPrev = sorted[0].dip;\n\n const firstRecord = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: azPrev,\n dip: dipPrev\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n firstRecord[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(firstRecord);\n\n for (let idx = 0; idx < sorted.length - 1; idx += 1) {\n const s0 = sorted[idx];\n const s1 = sorted[idx + 1];\n const md0 = s0.from;\n const md1 = s1.from;\n const deltaMd = md1 - md0;\n if (deltaMd <= 0) continue;\n\n const segmentSteps = Math.max(1, Math.ceil(deltaMd / safeStep));\n const mdIncrement = deltaMd / segmentSteps;\n\n for (let stepIdx = 0; stepIdx < segmentSteps; stepIdx += 1) {\n mdCursor += mdIncrement;\n const weight = (mdCursor - md0) / deltaMd;\n const azInterp = s0.azimuth + weight * (s1.azimuth - s0.azimuth);\n const dipInterp = s0.dip + weight * (s1.dip - s0.dip);\n\n const disp = segmentDisplacement(mdIncrement, s0.azimuth, s0.dip, s1.azimuth, s1.dip, method);\n x += disp.dx;\n y += disp.dy;\n z += disp.dz;\n\n const record = {\n hole_id: holeId,\n md: mdCursor,\n x,\n y,\n z,\n azimuth: method === 'minimum_curvature' ? azInterp : disp.azimuth,\n dip: method === 'minimum_curvature' ? dipInterp : disp.dip\n };\n\n if (collarsCanonical.aliasCol !== 'hole_id' && collar[collarsCanonical.aliasCol] !== undefined) {\n record[collarsCanonical.aliasCol] = collar[collarsCanonical.aliasCol];\n }\n out.push(record);\n }\n }\n });\n\n return out;\n}\n\n/**\n * Desurvey drillholes using minimum curvature method\n * @param {Array<Object>} collars - Collar data with hole_id, lat, lng\n * @param {Array<Object>} surveys - Survey data with hole_id, depth, azimuth, dip\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points with x, y, z, md coordinates\n */\nexport function minimumCurvatureDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'minimum_curvature' });\n}\n\n/**\n * Desurvey drillholes using tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function tangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'tangential' });\n}\n\n/**\n * Desurvey drillholes using balanced tangential method\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function balancedTangentialDesurvey(collars, surveys, options = {}) {\n return desurvey(collars, surveys, { ...options, method: 'balanced_tangential' });\n}\n\n/**\n * Build desurveyed traces using minimum curvature method (alias for minimumCurvatureDesurvey)\n * @param {Array<Object>} collars - Collar data\n * @param {Array<Object>} surveys - Survey data\n * @param {Object} options - Desurvey options\n * @returns {Array<Object>} Desurveyed trace points\n */\nexport function buildTraces(collars, surveys, options = {}) {\n return minimumCurvatureDesurvey(collars, surveys, options);\n}\n\n/**\n * Find nearest trace point by measured depth\n * @private\n */\nfunction nearestByMeasuredDepth(traceRows, midMd) {\n if (!traceRows.length || !Number.isFinite(midMd)) return null;\n let best = null;\n let bestDist = Infinity;\n for (let i = 0; i < traceRows.length; i += 1) {\n const row = traceRows[i];\n const md = toNumber(row.md);\n if (!Number.isFinite(md)) continue;\n const dist = Math.abs(md - midMd);\n if (dist < bestDist) {\n bestDist = dist;\n best = row;\n }\n }\n return best;\n}\n\n/**\n * Attach 3D position data from desurveyed traces to assay intervals\n * Finds nearest trace point by mid-depth of each assay interval\n * @param {Array<Object>} assays - Assay data with hole_id, from, to\n * @param {Array<Object>} traces - Desurveyed trace data with hole_id, md, x, y, z\n * @param {Object} options - Options\n * @param {string} options.holeIdCol - Hole ID column name (default: 'hole_id')\n * @returns {Array<Object>} Assay rows enriched with x, y, z, md, azimuth, dip from nearest trace point\n */\nexport function attachAssayPositions(assays = [], traces = [], options = {}) {\n const holeIdCol = options.holeIdCol || 'hole_id';\n const assaysCanonical = canonicalizeHoleIdRows(assays, holeIdCol);\n const tracesCanonical = canonicalizeHoleIdRows(traces, holeIdCol);\n\n if (!assaysCanonical.rows.length || !tracesCanonical.rows.length) return [...assaysCanonical.rows];\n\n const tracesByHole = new Map();\n tracesCanonical.rows.forEach((row) => {\n if (!row.hole_id) return;\n if (!tracesByHole.has(row.hole_id)) tracesByHole.set(row.hole_id, []);\n tracesByHole.get(row.hole_id).push(row);\n });\n tracesByHole.forEach((rows, holeId) => {\n tracesByHole.set(holeId, [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0)));\n });\n\n return assaysCanonical.rows.map((assay) => {\n const from = toNumber(assay.from);\n const to = toNumber(assay.to);\n const midMd = Number.isFinite(from) && Number.isFinite(to) ? 0.5 * (from + to) : undefined;\n if (!assay.hole_id || !Number.isFinite(midMd)) return { ...assay };\n\n const nearest = nearestByMeasuredDepth(tracesByHole.get(assay.hole_id) || [], midMd);\n if (!nearest) return { ...assay };\n\n const merged = { ...assay };\n ['md', 'x', 'y', 'z', 'azimuth', 'dip'].forEach((key) => {\n if (nearest[key] === undefined) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = nearest[key];\n } else {\n merged[key] = nearest[key];\n }\n });\n return merged;\n });\n}","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { standardizeColumns } from './keying.js';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { HOLE_ID, EASTING, NORTHING, ELEVATION } from './datamodel.js';\n\n/**\n * Parse drillholes CSV with desurveyed trace points\n * Expect CSV columns: hole_id, x (easting), y (northing), z (elevation), order (optional) plus any attributes per point\n * @param {File|Blob|string} file - CSV file or data\n * @param {Object} [sourceColumnMap] - Optional user-provided column mappings\n * @returns {Promise<{holes: Array}>} - Parsed drillhole data\n */\nexport function parseDrillholesCSV(file, sourceColumnMap = null) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n complete: (results) => {\n const byHole = new Map();\n results.data.forEach((rawRow, idx) => {\n const row = standardizeColumns(rawRow, null, sourceColumnMap);\n \n const holeIdRaw = row[HOLE_ID];\n const holeId = holeIdRaw !== undefined ? `${holeIdRaw}`.trim() : '';\n const x = row[EASTING] ?? row.x;\n const y = row[NORTHING] ?? row.y;\n const z = row[ELEVATION] ?? row.z;\n const order = row.order ?? idx;\n\n if (!holeId || x === null || x === undefined || y === null || y === undefined || z === null || z === undefined) return;\n\n if (!byHole.has(holeId)) byHole.set(holeId, []);\n byHole.get(holeId).push({\n ...row,\n holeId,\n order,\n x: Number(x) ?? 0,\n y: Number(y) ?? 0,\n z: Number(z) ?? 0\n });\n });\n\n const holes = Array.from(byHole.entries()).map(([holeId, pts]) => ({\n id: holeId,\n points: pts\n .sort((a, b) => a.order - b.order)\n .map((p) => ({\n ...p,\n x: Number(p.x) || 0,\n y: Number(p.y) || 0,\n z: Number(p.z) || 0\n }))\n }));\n\n resolve({ holes });\n },\n error: (error) => reject(withDataErrorContext('parseDrillholesCSV', error))\n });\n });\n}\n","/*\n * Copyright (C) 2026 Darkmine Pty Ltd\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\nimport { standardizeColumns as standardizeRow } from './keying.js';\nimport {\n HOLE_ID,\n LATITUDE,\n LONGITUDE,\n ELEVATION,\n AZIMUTH,\n DIP,\n FROM,\n TO,\n MID,\n PROJECT_ID,\n EASTING,\n NORTHING,\n CRS,\n DEPTH,\n BASELODE_DATA_MODEL_DRILL_COLLAR,\n BASELODE_DATA_MODEL_DRILL_SURVEY,\n BASELODE_DATA_MODEL_DRILL_ASSAY\n} from './datamodel.js';\n\n// Re-export for backwards compatibility\nexport { DEFAULT_COLUMN_MAP } from './datamodel.js';\n\n/**\n * Convert source to array\n * @private\n */\nfunction toArray(source) {\n if (!source) return [];\n if (Array.isArray(source)) return [...source];\n return [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Sort rows by multiple columns\n * @private\n */\nfunction sortByColumns(rows = [], columns = []) {\n const out = [...rows];\n out.sort((a, b) => {\n for (let i = 0; i < columns.length; i += 1) {\n const col = columns[i];\n const av = a?.[col];\n const bv = b?.[col];\n if (av === bv) continue;\n if (av === undefined || av === null) return 1;\n if (bv === undefined || bv === null) return -1;\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return `${av}`.localeCompare(`${bv}`);\n }\n return 0;\n });\n return out;\n}\n\n/**\n * Parse CSV data using PapaParse\n * @private\n */\nfunction parseCsv(source, papaParseConfig = {}) {\n return new Promise((resolve, reject) => {\n Papa.parse(source, {\n header: true,\n dynamicTyping: true,\n skipEmptyLines: true,\n ...papaParseConfig,\n complete: (results) => resolve(Array.isArray(results?.data) ? results.data : []),\n error: (error) => reject(withDataErrorContext('loadTable(csv)', error))\n });\n });\n}\n\n/**\n * Standardize column names in an array of rows using the baselode data model\n * @param {Array<Object>} rows - Array of row objects\n * @param {Object} [columnMap] - Optional column map (unused, for backwards compatibility)\n * @param {Object} [sourceColumnMap] - Optional source column map for user-provided mappings\n * @returns {Array<Object>} - Array of rows with standardized column names\n */\nexport function standardizeColumns(rows = [], columnMap = null, sourceColumnMap = null) {\n return rows.map((row) => standardizeRow(row, columnMap, sourceColumnMap));\n}\n\nexport async function loadTable(source, options = {}) {\n const {\n kind = 'csv',\n columnMap = null,\n sourceColumnMap = null,\n papaParseConfig = {}\n } = options;\n\n let rows;\n if (Array.isArray(source)) {\n rows = toArray(source);\n } else if (kind === 'csv') {\n rows = await parseCsv(source, papaParseConfig);\n } else if (kind === 'parquet' || kind === 'sql') {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind in JS runtime: ${kind}`));\n } else {\n throw withDataErrorContext('loadTable', new Error(`Unsupported kind: ${kind}`));\n }\n\n return standardizeColumns(rows, columnMap, sourceColumnMap);\n}\n\n/**\n * Load and validate drillhole collar data\n * Requires hole_id and coordinates (either lat/lng or easting/northing)\n * @param {File|Blob|Array<Object>|string} source - Collar data source\n * @param {Object} options - Loading options\n * @param {string} options.crs - Coordinate reference system (optional)\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated collar rows\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadCollars(source, options = {}) {\n const {\n crs = null,\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Check for hole_id\n const hasHoleId = standardized.some(row => HOLE_ID in row);\n if (!hasHoleId) {\n throw withDataErrorContext('loadCollars', new Error(`Collar table missing column: ${HOLE_ID}`));\n }\n\n // Check for required coordinate columns\n const hasXY = standardized.some(row => EASTING in row && NORTHING in row);\n const hasLatLon = standardized.some(row => LATITUDE in row && LONGITUDE in row);\n\n if (!hasXY && !hasLatLon) {\n throw withDataErrorContext('loadCollars', new Error('Collar table missing coordinate columns (need easting/northing or latitude/longitude)'));\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert coordinates to numbers\n if (LATITUDE in result) result[LATITUDE] = toNumber(result[LATITUDE]);\n if (LONGITUDE in result) result[LONGITUDE] = toNumber(result[LONGITUDE]);\n if (ELEVATION in result) result[ELEVATION] = toNumber(result[ELEVATION]);\n if (EASTING in result) result[EASTING] = toNumber(result[EASTING]);\n if (NORTHING in result) result[NORTHING] = toNumber(result[NORTHING]);\n\n // If datasource_hole_id is missing, copy from hole_id\n if (!('datasource_hole_id' in result) && HOLE_ID in result) {\n result.datasource_hole_id = result[HOLE_ID];\n }\n\n return result;\n });\n\n // Validate required fields\n const required = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (hasLatLon && (!Number.isFinite(row[LATITUDE]) || !Number.isFinite(row[LONGITUDE]))) {\n return false;\n }\n if (hasXY && !hasLatLon && (!Number.isFinite(row[EASTING]) || !Number.isFinite(row[NORTHING]))) {\n return false;\n }\n return true;\n });\n\n if (!required) {\n throw withDataErrorContext('loadCollars', new Error('Collar table has missing required values'));\n }\n\n return normalized;\n}\n\n/**\n * Load and validate drillhole survey data\n * Requires hole_id, depth, azimuth, and dip columns\n * @param {File|Blob|Array<Object>|string} source - Survey data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated survey rows, sorted by hole_id and depth\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadSurveys(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, DEPTH, AZIMUTH, DIP];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadSurveys', new Error(`Survey table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (DEPTH in result) result[DEPTH] = toNumber(result[DEPTH]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n if (AZIMUTH in result) result[AZIMUTH] = toNumber(result[AZIMUTH]);\n if (DIP in result) result[DIP] = toNumber(result[DIP]);\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[DEPTH])) return false;\n if (!Number.isFinite(row[AZIMUTH])) return false;\n if (!Number.isFinite(row[DIP])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadSurveys', new Error('Survey table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, DEPTH]);\n}\n\n/**\n * Load and validate assay/interval data\n * Requires hole_id, from, and to columns. Calculates mid depth automatically.\n * @param {File|Blob|Array<Object>|string} source - Assay data source\n * @param {Object} options - Loading options\n * @param {Object} options.sourceColumnMap - Optional user-provided column mappings\n * @param {boolean} options.keepAll - Keep all columns (default: true)\n * @returns {Promise<Array<Object>>} Array of validated assay rows, sorted by hole_id, from, to\n * @throws {Error} If required columns or values are missing\n */\nexport async function loadAssays(source, options = {}) {\n const {\n sourceColumnMap = null,\n keepAll = true,\n ...tableOptions\n } = options;\n \n const standardized = await loadTable(source, { ...tableOptions, sourceColumnMap });\n\n // Validate required columns\n const required = [HOLE_ID, FROM, TO];\n for (const col of required) {\n const hasColumn = standardized.some(row => col in row);\n if (!hasColumn) {\n throw withDataErrorContext('loadAssays', new Error(`Assay table missing column: ${col}`));\n }\n }\n\n const normalized = standardized.map((row) => {\n const result = { ...row };\n \n // Ensure hole_id is a string\n if (HOLE_ID in result) {\n const val = result[HOLE_ID];\n result[HOLE_ID] = val === undefined || val === null ? '' : `${val}`.trim();\n }\n \n // Convert numeric fields\n if (FROM in result) result[FROM] = toNumber(result[FROM]);\n if (TO in result) result[TO] = toNumber(result[TO]);\n \n // Calculate midpoint\n if (FROM in result && TO in result && Number.isFinite(result[FROM]) && Number.isFinite(result[TO])) {\n result[MID] = 0.5 * (result[FROM] + result[TO]);\n }\n \n return result;\n });\n\n // Validate required values\n const allValid = normalized.every((row) => {\n if (!row[HOLE_ID]) return false;\n if (!Number.isFinite(row[FROM])) return false;\n if (!Number.isFinite(row[TO])) return false;\n return true;\n });\n\n if (!allValid) {\n throw withDataErrorContext('loadAssays', new Error('Assay table has missing required values'));\n }\n\n return sortByColumns(normalized, [HOLE_ID, FROM, TO]);\n}\n\n/**\n * Join assay data to desurveyed trace data by matching key columns\n * Adds trace fields to assay rows, suffixing with '_trace' if field already exists\n * @param {Array<Object>} assays - Array of assay rows\n * @param {Array<Object>} traces - Array of trace/desurvey rows\n * @param {Object} options - Join options\n * @param {Array<string>} options.onCols - Columns to join on (default: [hole_id])\n * @returns {Array<Object>} Assay rows enriched with trace data\n */\nexport function joinAssaysToTraces(assays = [], traces = [], options = {}) {\n const onCols = Array.isArray(options.onCols) && options.onCols.length ? options.onCols : [HOLE_ID];\n if (!traces.length) return [...assays];\n\n const keyOf = (row) => onCols.map((col) => `${row?.[col] ?? ''}`).join('|');\n const tracesByKey = new Map();\n traces.forEach((trace) => {\n tracesByKey.set(keyOf(trace), trace);\n });\n\n return assays.map((assay) => {\n const trace = tracesByKey.get(keyOf(assay));\n if (!trace) return { ...assay };\n const merged = { ...assay };\n Object.entries(trace).forEach(([key, value]) => {\n if (onCols.includes(key)) return;\n if (Object.prototype.hasOwnProperty.call(merged, key)) {\n merged[`${key}_trace`] = value;\n } else {\n merged[key] = value;\n }\n });\n return merged;\n });\n}\n\n/**\n * Filter rows by project ID\n * @param {Array<Object>} rows - Array of rows with project_id field\n * @param {string|null} projectId - Project ID to filter by (null returns all rows)\n * @returns {Array<Object>} Filtered rows\n */\nexport function filterByProject(rows = [], projectId = null) {\n if (projectId === null || projectId === undefined) return [...rows];\n if (!rows.length) return [];\n \n // Check if rows have project_id column\n const hasProjectId = rows.some(row => PROJECT_ID in row);\n if (!hasProjectId) return [...rows];\n \n return rows.filter((row) => row?.[PROJECT_ID] === projectId);\n}\n\n/**\n * Coerce specified columns to numeric (finite number or undefined)\n * @param {Array<Object>} rows - Array of rows\n * @param {Array<string>} columns - Column names to coerce to numeric\n * @returns {Array<Object>} Rows with coerced numeric columns\n */\nexport function coerceNumeric(rows = [], columns = []) {\n return rows.map((row) => {\n const next = { ...row };\n columns.forEach((column) => {\n if (!(column in next)) return;\n const n = toNumber(next[column]);\n next[column] = n;\n });\n return next;\n });\n}\n\n/**\n * Assemble a complete drillhole dataset from component tables\n * @param {Object} components - Dataset components\n * @param {Array<Object>} components.collars - Collar data\n * @param {Array<Object>} components.surveys - Survey data\n * @param {Array<Object>} components.assays - Assay data\n * @param {Array<Object>} components.structures - Structural data (optional)\n * @param {Object} components.metadata - Dataset metadata (optional)\n * @returns {{collars: Array, surveys: Array, assays: Array, structures: Array, metadata: Object}} Complete dataset\n */\nexport function assembleDataset({\n collars = [],\n surveys = [],\n assays = [],\n structures = [],\n metadata = {}\n} = {}) {\n return {\n collars: toArray(collars),\n surveys: toArray(surveys),\n assays: toArray(assays),\n structures: toArray(structures),\n metadata: metadata || {}\n };\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport Papa from 'papaparse';\nimport { withDataErrorContext } from './dataErrorUtils.js';\n\n/**\n * Parse block model CSV file containing 3D block/voxel data\n * Expected columns: center_x, center_y, center_z, size_x, size_y, size_z, plus property columns\n * @param {File|Blob} file - Block model CSV file\n * @returns {Promise<{data: Array<Object>, properties: Array<string>}>} Parsed blocks and property column names\n */\nexport function parseBlockModelCSV(file) {\n return new Promise((resolve, reject) => {\n Papa.parse(file, {\n header: true,\n dynamicTyping: true,\n complete: (results) => {\n const data = results.data.filter((row) =>\n row.center_x !== null &&\n row.center_y !== null &&\n row.center_z !== null\n );\n\n const excludeColumns = ['center_x', 'center_y', 'center_z', 'size_x', 'size_y', 'size_z'];\n const propertyColumns = Object.keys(data[0] || {}).filter(\n (key) => !excludeColumns.includes(key)\n );\n\n resolve({ data, properties: propertyColumns });\n },\n error: (error) => {\n reject(withDataErrorContext('parseBlockModelCSV', error));\n }\n });\n });\n}\n\n/**\n * Calculate statistics for a property column in block model data\n * @param {Array<Object>} data - Block model data array\n * @param {string} property - Property column name to analyze\n * @returns {{type: 'numeric'|'categorical', min?: number, max?: number, values: Array, categories?: Array}} Property statistics\n */\nexport function calculatePropertyStats(data, property) {\n const values = data\n .map((row) => row[property])\n .filter((v) => v !== null && v !== undefined);\n\n const isNumeric = values.every((v) => typeof v === 'number');\n\n if (isNumeric) {\n const min = Math.min(...values);\n const max = Math.max(...values);\n return { type: 'numeric', min, max, values };\n }\n\n const uniqueValues = [...new Set(values)];\n return { type: 'categorical', categories: uniqueValues, values };\n}\n\n/**\n * Generate a color for a property value based on its statistics\n * @param {*} value - Property value to colorize\n * @param {{type: 'numeric'|'categorical', min?: number, max?: number, categories?: Array}|null} stats - Property statistics\n * @param {Object} THREEInstance - THREE.js instance for creating Color objects\n * @returns {THREE.Color} Color object for the value\n */\nexport function getColorForValue(value, stats, THREEInstance) {\n if (!stats) return new THREEInstance.Color('#888888');\n\n if (stats.type === 'numeric') {\n const range = stats.max - stats.min;\n const normalized = range === 0 ? 0.5 : (value - stats.min) / range;\n const hue = (1 - normalized) * 240; // 240 is blue, 0 is red\n return new THREEInstance.Color().setHSL(hue / 360, 0.8, 0.5);\n }\n\n const index = stats.categories.indexOf(value);\n const hue = (index / Math.max(stats.categories.length, 1)) * 360;\n return new THREEInstance.Color().setHSL(hue / 360, 0.7, 0.5);\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n// Shared drillhole 2D visualization helpers for reuse beyond the UI layer.\n// These helpers build Plotly-ready data/layout objects based on interval points.\n\n/** Default color for numeric line traces */\nexport const NUMERIC_LINE_COLOR = '#8b1e3f';\n\n/** Default color for numeric markers */\nexport const NUMERIC_MARKER_COLOR = '#a8324f';\n\n/** Color for error bars */\nexport const ERROR_COLOR = '#6b7280';\n\n/**\n * Check if a hole has data for a specific property\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to check\n * @returns {boolean} True if hole has at least one valid value for the property\n */\nexport function holeHasData(hole, property) {\n if (!hole || !property) return false;\n const pts = hole.points || [];\n for (let i = 0; i < pts.length; i += 1) {\n const value = pts[i]?.[property];\n if (value === undefined || value === null) continue;\n if (typeof value === 'number' && Number.isFinite(value)) return true;\n if (typeof value === 'string' && value.trim() !== '') return true;\n }\n return false;\n}\n\n/**\n * Build array of interval points for visualization from hole data\n * Extracts depth intervals and property values, deduplicates, and sorts by depth\n * @param {Object} hole - Hole object with points array\n * @param {string} property - Property name to extract\n * @param {boolean} isCategorical - Whether property is categorical (vs numeric)\n * @returns {Array<{z: number, val: *, from: number, to: number, errorPlus: number, errorMinus: number}>} Array of interval points\n */\nexport function buildIntervalPoints(hole, property, isCategorical) {\n if (!hole || !property) return [];\n const rawPoints = hole?.points || [];\n const out = [];\n const seen = new Set();\n rawPoints.forEach((p) => {\n const fromVal = Number(\n p.from ??\n p.samp_from ??\n p.sample_from ??\n p.fromdepth ??\n p.from_depth ??\n p.depth_from\n );\n const toVal = Number(\n p.to ??\n p.samp_to ??\n p.sample_to ??\n p.todepth ??\n p.to_depth ??\n p.depth_to\n );\n const rawVal = p?.[property];\n if (!Number.isFinite(fromVal) || !Number.isFinite(toVal) || toVal <= fromVal) return;\n if (rawVal === undefined || rawVal === null || rawVal === '') return;\n const key = `${property}:${fromVal}-${toVal}`;\n if (seen.has(key)) return;\n seen.add(key);\n const mid = (fromVal + toVal) / 2;\n const val = isCategorical ? rawVal : Number(rawVal);\n if (!isCategorical && !Number.isFinite(val)) return;\n out.push({\n z: mid,\n val,\n from: fromVal,\n to: toVal,\n errorPlus: toVal - mid,\n errorMinus: mid - fromVal\n });\n });\n return out.sort((a, b) => b.z - a.z);\n}\n\n/**\n * Build Plotly configuration for categorical property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for title\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildCategoricalConfig(points, property) {\n if (!points.length) return { data: [], layout: {} };\n const sorted = [...points].sort((a, b) => b.z - a.z);\n const segments = [];\n for (let i = 0; i < sorted.length; i += 1) {\n const curr = sorted[i];\n const next = sorted[i + 1];\n const y0 = curr.z;\n const y1 = next ? next.z : curr.z - 20;\n if (y1 === y0) continue;\n segments.push({ y0, y1, category: curr.val || 'unknown' });\n }\n\n const palette = ['#8b1e3f', '#a8324f', '#b84c68', '#d16587', '#e07ba0', '#f091b6', '#f7a7c8', '#fbcfe8'];\n\n const shapes = segments.map((seg, idx) => ({\n type: 'rect',\n xref: 'x',\n yref: 'y',\n x0: 0,\n x1: 1,\n y0: seg.y0,\n y1: seg.y1,\n fillcolor: palette[idx % palette.length],\n line: { width: 0 }\n }));\n\n const textTrace = {\n x: segments.map(() => 0.5),\n y: segments.map((s) => (s.y0 + s.y1) / 2),\n mode: 'text',\n text: segments.map((s) => s.category),\n textposition: 'middle center',\n showlegend: false,\n hoverinfo: 'text',\n customdata: segments.map((s) => [s.y0, s.y1]),\n hovertemplate: `Category: %{text}<br>from: %{customdata[0]} to %{customdata[1]}<extra></extra>`\n };\n\n const layout = {\n height: 260,\n margin: { l: 50, r: 10, t: 10, b: 30 },\n xaxis: { range: [0, 1], visible: false, fixedrange: true },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n shapes,\n showlegend: false,\n title: property || undefined\n };\n\n return { data: [textTrace], layout };\n}\n\n/**\n * Build Plotly configuration for numeric property visualization\n * @private\n * @param {Array<Object>} points - Interval points array\n * @param {string} property - Property name for axis label\n * @param {string} chartType - Chart type ('bar', 'markers', 'line', 'markers+line')\n * @returns {{data: Array, layout: Object}} Plotly data and layout configuration\n */\nfunction buildNumericConfig(points, property, chartType) {\n if (!points.length) return { data: [], layout: {} };\n const isBar = chartType === 'bar';\n const isMarkersOnly = chartType === 'markers';\n const isLineOnly = chartType === 'line';\n\n const baseTrace = {\n x: points.map((p) => p.val),\n y: points.map((p) => p.z),\n hovertemplate: `${property}: %{x}<br>from: %{customdata[0]} to %{customdata[1]}<extra></extra>`,\n customdata: points.map((p) => [p.from, p.to])\n };\n\n const errorConfig = {\n type: 'data',\n symmetric: false,\n array: points.map((p) => p.errorPlus),\n arrayminus: points.map((p) => p.errorMinus),\n thickness: 1.5,\n width: 2,\n color: ERROR_COLOR\n };\n\n const trace = isBar\n ? {\n ...baseTrace,\n type: 'bar',\n orientation: 'h',\n marker: { color: NUMERIC_LINE_COLOR },\n error_y: errorConfig\n }\n : {\n ...baseTrace,\n type: 'scatter',\n mode: isMarkersOnly ? 'markers' : isLineOnly ? 'lines' : 'lines+markers',\n line: { color: NUMERIC_LINE_COLOR, width: 2 },\n marker: { size: 7, color: NUMERIC_MARKER_COLOR },\n error_y: isLineOnly ? undefined : errorConfig\n };\n\n const layout = {\n height: 260,\n margin: { l: 50, r: 10, t: 10, b: 30 },\n xaxis: { title: property, zeroline: false },\n yaxis: { title: 'Depth (m)', autorange: 'reversed', zeroline: false },\n barmode: 'overlay',\n showlegend: false\n };\n\n return { data: [trace], layout };\n}\n\n/**\n * Build complete Plotly configuration for property visualization\n * @param {Object} options - Configuration options\n * @param {Array<Object>} options.points - Interval points to visualize\n * @param {boolean} options.isCategorical - Whether property is categorical\n * @param {string} options.property - Property name\n * @param {string} options.chartType - Chart type ('bar', 'markers', 'line', 'categorical', etc.)\n * @returns {{data: Array, layout: Object}} Complete Plotly configuration\n */\nexport function buildPlotConfig({ points, isCategorical, property, chartType }) {\n if (!points || !points.length || !property) return { data: [], layout: {} };\n if (isCategorical || chartType === 'categorical') {\n return buildCategoricalConfig(points, property);\n }\n return buildNumericConfig(points, property, chartType);\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useRef, useState } from 'react';\nimport Plotly from 'plotly.js-dist-min';\nimport { buildPlotConfig } from './drillholeViz.js';\n\nconst DEFAULT_NUMERIC_CHART_TYPE = 'markers+line';\nconst CATEGORICAL_CHART_OPTIONS = [{ value: 'categorical', label: 'Categorical bands' }];\nconst NUMERIC_CHART_OPTIONS = [\n { value: 'bar', label: 'Bars' },\n { value: 'markers', label: 'Markers' },\n { value: DEFAULT_NUMERIC_CHART_TYPE, label: 'Markers + Line' },\n { value: 'line', label: 'Line only' }\n];\n\n/**\n * Resolve chart type from available options\n * @private\n */\nfunction resolveChartType(chartOptions, requestedChartType) {\n if (chartOptions.some((opt) => opt.value === requestedChartType)) return requestedChartType;\n return chartOptions[0]?.value || DEFAULT_NUMERIC_CHART_TYPE;\n}\n\n/**\n * Plotly-based trace plot component for drillhole assay data\n * Renders 1D strip log or 2D trace with configurable chart types\n * @param {Object} props - Component props\n * @param {Object} props.config - Plot configuration {holeId, property, chartType}\n * @param {Object} props.graph - Graph data {hole, points, isCategorical, loading}\n * @param {Array} props.holeOptions - Available holes for dropdown\n * @param {Array} props.propertyOptions - Available properties for dropdown\n * @param {Function} props.onConfigChange - Handler for configuration changes\n * @returns {JSX.Element} Trace plot component\n */\nfunction TracePlot({ config, graph, holeOptions = [], propertyOptions = [], onConfigChange }) {\n const containerRef = useRef(null);\n const hole = graph?.hole;\n const points = graph?.points || [];\n const property = config?.property || '';\n const chartType = config?.chartType || DEFAULT_NUMERIC_CHART_TYPE;\n const selectedHoleId = config?.holeId || '';\n const isCategorical = graph?.isCategorical || false;\n\n const chartOptions = isCategorical ? CATEGORICAL_CHART_OPTIONS : NUMERIC_CHART_OPTIONS;\n\n const effectiveChartType = resolveChartType(chartOptions, chartType);\n\n const [renderError, setRenderError] = useState('');\n\n useEffect(() => {\n if (!hole || !property || points.length === 0) return;\n const target = containerRef.current;\n if (!target) return;\n const { data, layout } = buildPlotConfig({ points, isCategorical, property, chartType: effectiveChartType });\n if (!data || data.length === 0) return;\n const plotConfig = {\n displayModeBar: true,\n responsive: true,\n useResizeHandler: true,\n modeBarButtonsToRemove: ['select2d', 'lasso2d', 'zoom2d', 'zoomIn2d', 'zoomOut2d', 'autoScale2d']\n };\n\n try {\n setRenderError('');\n Plotly.react(target, data, layout, plotConfig);\n requestAnimationFrame(() => {\n if (target && target.parentElement) {\n Plotly.Plots.resize(target);\n }\n });\n } catch (err) {\n console.error('Plot render error', err);\n setRenderError(err?.message || 'Plot render error');\n }\n\n return () => {\n if (target) {\n try {\n Plotly.purge(target);\n } catch (err) {\n console.warn('Plot purge error', err);\n }\n }\n };\n }, [hole, property, effectiveChartType, isCategorical, points]);\n\n useEffect(() => {\n const target = containerRef.current;\n if (!target || typeof ResizeObserver === 'undefined') return undefined;\n const resizeObserver = new ResizeObserver(() => {\n try {\n if (target && target.data) {\n Plotly.Plots.resize(target);\n }\n } catch (err) {\n console.warn('Plot resize error', err);\n }\n });\n resizeObserver.observe(target);\n return () => resizeObserver.disconnect();\n }, []);\n\n if (!hole || !property) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">{config?.holeId ? (graph?.loading ? `Loading ${config.holeId}...` : 'Select a property') : 'Loading demo data...'}</div>\n </div>\n );\n }\n\n if (points.length === 0) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">No numeric data for {property}</div>\n </div>\n );\n }\n\n if (renderError) {\n return (\n <div className=\"plot-card empty\">\n <div className=\"placeholder\">Plot error: {renderError}</div>\n </div>\n );\n }\n\n return (\n <div className=\"plot-card\">\n <div className=\"plot-title\">\n <select\n className=\"plot-select\"\n value={selectedHoleId}\n onChange={(e) => onConfigChange && onConfigChange({ holeId: e.target.value })}\n >\n {holeOptions.map((h) => {\n const holeId = typeof h === 'string' ? h : h.holeId;\n const label = typeof h === 'string' ? h : h.label || h.holeId;\n return (\n <option key={holeId} value={holeId}>{label}</option>\n );\n })}\n </select>\n </div>\n <div className=\"plot-controls column\">\n {propertyOptions.length > 0 && (\n <select\n className=\"plot-select\"\n value={property}\n onChange={(e) => onConfigChange && onConfigChange({ property: e.target.value })}\n >\n {propertyOptions.map((p) => (\n <option key={p} value={p}>{p}</option>\n ))}\n </select>\n )}\n <select\n className=\"plot-select\"\n value={effectiveChartType}\n onChange={(e) => onConfigChange && onConfigChange({ chartType: e.target.value })}\n >\n {chartOptions.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n </div>\n <div className=\"plotly-chart\" ref={containerRef} />\n </div>\n );\n}\n\nexport default TracePlot;\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport { useEffect, useMemo, useState } from 'react';\nimport {\n deriveAssayProps,\n loadAssayHole,\n loadAssayMetadata\n} from '../data/assayDataLoader.js';\nimport {\n buildTraceConfigsForHoleIds,\n coerceChartTypeForProperty,\n reorderHoleIds\n} from '../data/traceGridConfig.js';\nimport { buildIntervalPoints, holeHasData } from './drillholeViz.js';\n\n/**\n * React hook for managing a grid of drillhole trace plots\n * Handles loading assay data, managing plot configurations, and coordinating state\n * @param {Object} options - Hook options\n * @param {string} options.initialFocusedHoleId - Initial focused hole ID\n * @param {File|Blob} options.sourceFile - Assay data CSV file\n * @param {number} options.plotCount - Number of plots in grid (default: 4)\n * @returns {{traceGraphs: Array, traceConfigs: Array, updateTraceConfig: Function, propertyOptions: Array, holeOptions: Array, loadHole: Function}} Hook state and actions\n */\nexport default function useDrillholeTraceGrid({\n initialFocusedHoleId = '',\n sourceFile = null,\n plotCount = 4\n} = {}) {\n const [holes, setHoles] = useState([]);\n const [holeIds, setHoleIds] = useState([]);\n const [numericProps, setNumericProps] = useState([]);\n const [categoricalProps, setCategoricalProps] = useState([]);\n const [defaultProp, setDefaultProp] = useState('');\n const [traceConfigs, setTraceConfigs] = useState([]);\n const [error, setError] = useState('');\n const [focusedHoleId, setFocusedHoleId] = useState(initialFocusedHoleId || '');\n const [loadingHoles, setLoadingHoles] = useState([]);\n\n const applyAssayState = (state) => {\n if (!state) return;\n setError('');\n setHoles(state.holes || []);\n setHoleIds((state.holes || []).map((h) => ({\n holeId: h.id || h.holeId\n })).filter((h) => h.holeId));\n setNumericProps(state.numericProps || []);\n setCategoricalProps(state.categoricalProps || []);\n setDefaultProp(state.defaultProp || '');\n setTraceConfigs(state.traceConfigs || []);\n };\n\n useEffect(() => {\n if (!sourceFile || holeIds.length > 0) return;\n loadAssayMetadata(sourceFile)\n .then((ids) => {\n if (!ids) return;\n const uniqueIds = Array.from(new Map(ids.map((h) => [h.holeId, h])).values());\n setHoleIds(uniqueIds);\n setTraceConfigs(buildTraceConfigsForHoleIds({\n holeIds: uniqueIds.map((h) => h.holeId),\n focusedHoleId,\n plotCount,\n defaultProp: '',\n categoricalProps,\n numericDefaultChartType: 'markers+line'\n }));\n })\n .catch((err) => {\n console.info('Assay metadata load skipped:', err.message);\n });\n }, [sourceFile, holeIds.length, focusedHoleId, plotCount, categoricalProps]);\n\n useEffect(() => {\n setError((prev) => (prev && prev.startsWith('Loading assays for hole') ? prev : ''));\n }, [traceConfigs]);\n\n useEffect(() => {\n if (!holeIds.length) {\n setTraceConfigs([]);\n return;\n }\n const orderedHoleIds = reorderHoleIds(holeIds.map((h) => h.holeId), focusedHoleId);\n setTraceConfigs((prev) => {\n const next = Array.from({ length: plotCount }).map((_, idx) => {\n const existing = prev[idx] || {};\n const holeId = holeIds.some((h) => h.holeId === existing.holeId) ? existing.holeId : orderedHoleIds[idx] || holeIds[idx]?.holeId || '';\n const property = existing.property || defaultProp;\n const chartType = coerceChartTypeForProperty({\n property,\n chartType: existing.chartType,\n categoricalProps,\n numericDefaultChartType: 'markers+line'\n });\n return { holeId, property, chartType };\n });\n return next;\n });\n }, [holeIds, focusedHoleId, defaultProp, categoricalProps, plotCount]);\n\n useEffect(() => {\n if (!sourceFile) return;\n const needed = traceConfigs.map((cfg) => cfg.holeId).filter(Boolean);\n needed.forEach((holeId) => {\n const already = holes.some((h) => (h.id || h.holeId) === holeId);\n const loading = loadingHoles.includes(holeId);\n if (already || loading) return;\n setLoadingHoles((prev) => [...prev, holeId]);\n loadAssayHole(sourceFile, holeId)\n .then((hole) => {\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n if (!hole) return;\n setHoles((prev) => {\n const next = [...prev.filter((h) => (h.id || h.holeId) !== holeId), hole];\n const props = deriveAssayProps(next);\n setNumericProps(props.numericProps);\n setCategoricalProps(props.categoricalProps);\n if (!defaultProp && props.defaultProp) {\n setDefaultProp(props.defaultProp);\n setTraceConfigs((configs) => configs.map((cfg) => ({\n ...cfg,\n property: cfg.property || props.defaultProp,\n chartType: coerceChartTypeForProperty({\n property: cfg.property || props.defaultProp,\n chartType: cfg.chartType,\n categoricalProps: props.categoricalProps,\n numericDefaultChartType: 'markers+line'\n })\n })));\n }\n return next;\n });\n })\n .catch((err) => {\n console.error(err);\n setLoadingHoles((prev) => prev.filter((id) => id !== holeId));\n setError(err.message || `Error loading hole ${holeId}`);\n });\n });\n }, [traceConfigs, sourceFile, holes, loadingHoles, defaultProp]);\n\n const propertyOptions = useMemo(() => [...numericProps, ...categoricalProps], [numericProps, categoricalProps]);\n\n const labeledHoleOptions = useMemo(\n () => holeIds\n .map((h) => ({ holeId: h.holeId, label: h.holeId }))\n .sort((a, b) => a.label.localeCompare(b.label)),\n [holeIds]\n );\n\n const traceGraphs = useMemo(() => {\n return Array.from({ length: plotCount }).map((_, idx) => {\n const cfg = traceConfigs[idx] || {};\n const hole = holes.find((h) => (h.id || h.holeId) === cfg.holeId) || null;\n let property = cfg.property || defaultProp;\n if (hole && (!property || !holeHasData(hole, property))) {\n const fallback = [...numericProps, ...categoricalProps].find((p) => holeHasData(hole, p));\n if (fallback) property = fallback;\n }\n const chartType = cfg.chartType || (property && categoricalProps.includes(property) ? 'categorical' : 'markers+line');\n const holeId = cfg.holeId || hole?.id || hole?.holeId || '';\n const isCategorical = categoricalProps.includes(property);\n const points = buildIntervalPoints(hole, property, isCategorical);\n return {\n config: { holeId, property, chartType },\n hole,\n loading: loadingHoles.includes(cfg.holeId),\n isCategorical,\n points,\n label: holeId\n };\n });\n }, [traceConfigs, holes, defaultProp, categoricalProps, loadingHoles, plotCount, numericProps]);\n\n const handleConfigChange = (index, patch) => {\n setTraceConfigs((prev) => {\n const next = [...prev];\n const base = next[index] || {};\n const merged = { ...base, ...patch };\n if (patch.property) {\n merged.chartType = coerceChartTypeForProperty({\n property: patch.property,\n chartType: merged.chartType,\n categoricalProps,\n numericDefaultChartType: 'markers+line'\n });\n }\n next[index] = merged;\n return next;\n });\n };\n\n return {\n error,\n focusedHoleId,\n setFocusedHoleId,\n setError,\n holeCount: holeIds.length,\n numericProps,\n categoricalProps,\n propertyOptions,\n labeledHoleOptions,\n traceGraphs,\n handleConfigChange\n };\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/** Default 10-color palette for assay visualization (blue to red gradient) */\nexport const ASSAY_COLOR_PALETTE_10 = [\n '#313695',\n '#4575b4',\n '#74add1',\n '#abd9e9',\n '#e0f3f8',\n '#fee090',\n '#fdae61',\n '#f46d43',\n '#d73027',\n '#a50026'\n];\n\n/**\n * Build an equal-range color scale from numeric values\n * Uses percentile-based binning for better distribution with outliers\n * @param {Array<number>} values - Array of numeric values to analyze\n * @param {Array<string>} colors - Array of color hex strings to use for bins\n * @returns {{min: number|null, max: number|null, step: number|null, bins: Array<{index: number, min: number, max: number, label: string}>, colors: Array<string>}} Color scale object\n */\nexport function buildEqualRangeColorScale(values = [], colors = ASSAY_COLOR_PALETTE_10) {\n // Filter to finite values and sort\n const finiteValues = values.filter((v) => Number.isFinite(v));\n \n if (!finiteValues.length) {\n return {\n min: null,\n max: null,\n step: null,\n bins: [],\n colors\n };\n }\n\n const sorted = finiteValues.slice().sort((a, b) => a - b);\n const min = sorted[0];\n const max = sorted[sorted.length - 1];\n const binCount = colors.length;\n\n if (max === min) {\n const bins = colors.map((_, index) => ({\n index,\n min,\n max,\n label: `${min}`\n }));\n return {\n min,\n max,\n step: 0,\n bins,\n colors\n };\n }\n\n // Use percentile-based binning for better distribution\n const bins = colors.map((_, index) => {\n const percentileLow = index / binCount;\n const percentileHigh = (index + 1) / binCount;\n const idxLow = Math.floor(percentileLow * sorted.length);\n const idxHigh = Math.min(sorted.length - 1, Math.floor(percentileHigh * sorted.length));\n const lower = sorted[idxLow];\n const upper = index === binCount - 1 ? max : sorted[idxHigh];\n \n return {\n index,\n min: lower,\n max: upper,\n label: formatBinLabel(lower, upper)\n };\n });\n\n const step = (max - min) / binCount;\n\n return {\n min,\n max,\n step,\n bins,\n colors\n };\n}\n\n/**\n * Format bin label with appropriate precision\n * @private\n */\nfunction formatBinLabel(min, max) {\n const formatVal = (v) => {\n if (!Number.isFinite(v)) return 'n/a';\n if (Math.abs(v) >= 1000) return v.toFixed(0);\n if (Math.abs(v) >= 10) return v.toFixed(1);\n if (Math.abs(v) >= 0.1) return v.toFixed(2);\n return v.toFixed(3);\n };\n return `${formatVal(min)} – ${formatVal(max)}`;\n}\n\n/**\n * Get the bin index for a value in a color scale\n * @param {number} value - Value to find bin for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @returns {number} Bin index (0 to bins.length-1) or -1 if invalid\n */\nexport function getEqualRangeBinIndex(value, scale) {\n if (!Number.isFinite(value) || !scale || !Array.isArray(scale.bins) || !scale.bins.length) {\n return -1;\n }\n\n if (scale.max === scale.min) {\n return value === scale.min ? 0 : -1;\n }\n\n // Find the bin that contains this value\n for (let i = 0; i < scale.bins.length; i += 1) {\n const bin = scale.bins[i];\n if (value >= bin.min && (value <= bin.max || i === scale.bins.length - 1)) {\n return i;\n }\n }\n\n // Value is out of range\n return -1;\n}\n\n/**\n * Get the color for a value using an equal-range color scale\n * @param {number} value - Value to get color for\n * @param {Object} scale - Color scale object from buildEqualRangeColorScale\n * @param {string} fallbackColor - Color to use if value is invalid or out of range\n * @returns {string} Color hex string\n */\nexport function getEqualRangeColor(value, scale, fallbackColor = '#8b1e3f') {\n const index = getEqualRangeBinIndex(value, scale);\n if (index < 0) return fallbackColor;\n return scale.colors[index] || fallbackColor;\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n * @param {*} rows - Value to convert to array\n * @returns {Array} Array\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n * @param {*} value - Value to convert\n * @returns {number|undefined} Finite number or undefined\n */\nfunction toNumber(value) {\n const n = Number(value);\n return Number.isFinite(n) ? n : undefined;\n}\n\n/**\n * Normalize a point to ensure x, y, z are finite numbers\n * @private\n * @param {Object} point - Point object\n * @returns {Object} Normalized point\n */\nfunction normalizePoint(point = {}) {\n return {\n ...point,\n x: toNumber(point.x),\n y: toNumber(point.y),\n z: toNumber(point.z)\n };\n}\n\n/**\n * Project 3D trace points onto a 2D cross-section plane\n * @param {Array<Object>} traces - Array of trace points with x, y, z coordinates\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @returns {Array<Object>} Trace points with added 'along' and 'across' section coordinates\n */\nexport function projectTraceToSection(traces = [], origin = [0, 0], azimuth = 0) {\n const [ox, oy] = origin;\n const azRad = (Number(azimuth) * Math.PI) / 180;\n const cosA = Math.cos(azRad);\n const sinA = Math.sin(azRad);\n\n return toArray(traces)\n .map(normalizePoint)\n .map((row) => {\n if (!Number.isFinite(row.x) || !Number.isFinite(row.y)) return { ...row };\n const dx = row.x - ox;\n const dy = row.y - oy;\n return {\n ...row,\n along: (dx * sinA) + (dy * cosA),\n across: (dx * cosA) - (dy * sinA)\n };\n });\n}\n\n/**\n * Filter trace points to those within a width window around a section line\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width (points within ±width/2 are included)\n * @returns {Array<Object>} Filtered trace points within section window\n */\nexport function sectionWindow(traces = [], origin = [0, 0], azimuth = 0, width = 50) {\n const projected = projectTraceToSection(traces, origin, azimuth);\n const half = 0.5 * Number(width || 0);\n if (!Number.isFinite(half) || half <= 0) return projected;\n return projected.filter((row) => Number.isFinite(row.across) && Math.abs(row.across) <= half);\n}\n\n/**\n * Generate plan view of traces, optionally filtered by depth slice\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>|null} depthSlice - Optional [top, bottom] depth range to filter\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Trace points for plan view\n */\nexport function planView(traces = [], depthSlice = null, colorBy = null) {\n let rows = toArray(traces).map(normalizePoint);\n if (Array.isArray(depthSlice) && depthSlice.length === 2) {\n const [top, bottom] = depthSlice;\n rows = rows.filter((row) => Number.isFinite(row.z) && row.z <= Number(top) && row.z >= Number(bottom));\n }\n if (colorBy) {\n rows = rows.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return rows;\n}\n\n/**\n * Generate cross-section view of traces within a window\n * @param {Array<Object>} traces - Array of trace points\n * @param {Array<number>} origin - Section origin [x, y] coordinates\n * @param {number} azimuth - Section azimuth in degrees\n * @param {number} width - Section width\n * @param {string|null} colorBy - Optional property name for color_value\n * @returns {Array<Object>} Section trace points with along/across coordinates\n */\nexport function sectionView(traces = [], origin = [0, 0], azimuth = 0, width = 50, colorBy = null) {\n let section = sectionWindow(traces, origin, azimuth, width);\n if (colorBy) {\n section = section.map((row) => ({\n ...row,\n color_value: row?.[colorBy]\n }));\n }\n return section;\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Ensure value is an array\n * @private\n */\nfunction toArray(rows) {\n return Array.isArray(rows) ? rows : [];\n}\n\n/**\n * Extract hole ID from a row using common field name variations\n * @private\n */\nfunction getHoleId(row = {}) {\n return row.hole_id ?? row.holeId ?? row.id;\n}\n\n/**\n * Convert value to finite number or undefined\n * @private\n */\nfunction toNumber(value, fallback = undefined) {\n const n = Number(value);\n return Number.isFinite(n) ? n : fallback;\n}\n\n/**\n * Convert trace points to line segments grouped by hole ID\n * @param {Array<Object>} traces - Array of trace points with x, y, z, md coordinates\n * @param {string|null} colorBy - Optional property name for per-point color values\n * @returns {Array<{hole_id: string, x: Array<number>, y: Array<number>, z: Array<number>, color: Array|null}>} Array of line segments\n */\nexport function tracesAsSegments(traces = [], colorBy = null) {\n const grouped = new Map();\n\n toArray(traces).forEach((row) => {\n const holeId = getHoleId(row);\n if (holeId === undefined || holeId === null || `${holeId}`.trim() === '') return;\n const key = `${holeId}`;\n if (!grouped.has(key)) grouped.set(key, []);\n grouped.get(key).push(row);\n });\n\n const segments = [];\n grouped.forEach((rows, holeId) => {\n const sorted = [...rows].sort((a, b) => toNumber(a.md, 0) - toNumber(b.md, 0));\n const payload = {\n hole_id: holeId,\n x: sorted.map((row) => toNumber(row.x, 0)),\n y: sorted.map((row) => toNumber(row.y, 0)),\n z: sorted.map((row) => toNumber(row.z, 0)),\n color: null\n };\n if (colorBy) {\n payload.color = sorted.map((row) => row?.[colorBy]);\n }\n segments.push(payload);\n });\n\n return segments;\n}\n\n/**\n * Convert interval data to tube/cylinder representations for 3D rendering\n * @param {Array<Object>} intervals - Array of interval objects with from/to depths and x, y, z coordinates\n * @param {number} radius - Tube radius\n * @param {string|null} colorBy - Optional property name for color value\n * @returns {Array<{hole_id: string, x: number, y: number, z: number, from: number, to: number, radius: number, color_value: *}>} Array of tube specifications\n */\nexport function intervalsAsTubes(intervals = [], radius = 1, colorBy = null) {\n return toArray(intervals).map((row) => ({\n hole_id: getHoleId(row),\n from: row?.from,\n to: row?.to,\n radius,\n color: colorBy ? row?.[colorBy] : null,\n value: colorBy ? row?.[colorBy] : null\n }));\n}\n\n/**\n * Extract annotation labels from interval data for 3D text display\n * @param {Array<Object>} intervals - Array of interval objects\n * @param {string|null} labelCol - Property name to use for label text\n * @returns {Array<{hole_id: string, label: string, depth: number}>} Array of annotations with computed mid-depth\n */\nexport function annotationsFromIntervals(intervals = [], labelCol = null) {\n if (!labelCol) return [];\n return toArray(intervals)\n .filter((row) => Object.prototype.hasOwnProperty.call(row || {}, labelCol))\n .map((row) => ({\n hole_id: getHoleId(row),\n label: row?.[labelCol],\n depth: 0.5 * ((toNumber(row?.from, 0)) + (toNumber(row?.to, 0)))\n }));\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/**\n * Build a string signature from view state for comparison (to detect changes)\n * @param {Object} viewState - View state object with camera, target, up vectors\n * @returns {string} String signature representing the view state\n */\nexport function buildViewSignature(viewState) {\n if (!viewState) return '';\n const toNum = (v) => Number.isFinite(v) ? v.toFixed(3) : 'nan';\n return [\n toNum(viewState.camera?.x),\n toNum(viewState.camera?.y),\n toNum(viewState.camera?.z),\n toNum(viewState.target?.x),\n toNum(viewState.target?.y),\n toNum(viewState.target?.z),\n toNum(viewState.up?.x),\n toNum(viewState.up?.y),\n toNum(viewState.up?.z)\n ].join('|');\n}\n\n/**\n * Extract current view state from 3D scene state\n * @param {Object} state - Baselode3D scene state with camera and controls\n * @returns {Object|null} View state object or null if state invalid\n */\nexport function getViewState(state) {\n if (!state.camera || !state.controls) return null;\n return {\n camera: {\n x: state.camera.position.x,\n y: state.camera.position.y,\n z: state.camera.position.z\n },\n target: {\n x: state.controls.target.x,\n y: state.controls.target.y,\n z: state.controls.target.z\n },\n up: {\n x: state.camera.up.x,\n y: state.camera.up.y,\n z: state.camera.up.z\n }\n };\n}\n\n/**\n * Apply a view state to the 3D scene camera and controls\n * @param {Object} state - Baselode3D scene state\n * @param {Object} viewState - View state to apply\n * @returns {boolean} True if successfully applied\n */\nexport function setViewState(state, viewState) {\n if (!state.camera || !state.controls || !viewState) return false;\n const camera = viewState.camera || {};\n const target = viewState.target || {};\n const up = viewState.up || {};\n\n const values = [camera.x, camera.y, camera.z, target.x, target.y, target.z, up.x, up.y, up.z];\n if (!values.every(Number.isFinite)) return false;\n\n state.camera.position.set(camera.x, camera.y, camera.z);\n state.controls.target.set(target.x, target.y, target.z);\n state.camera.up.set(up.x, up.y, up.z);\n state.camera.lookAt(target.x, target.y, target.z);\n state.controls.update();\n state._lastViewSignature = buildViewSignature(viewState);\n return true;\n}\n\n/**\n * Emit view change event if view has changed (throttled to 250ms)\n * @param {Object} state - Baselode3D scene state with viewChangeHandler\n */\nexport function emitViewChangeIfNeeded(state) {\n if (!state.viewChangeHandler) return;\n const now = Date.now();\n if (now - state._lastViewEmitMs < 250) return;\n const viewState = getViewState(state);\n if (!viewState) return;\n const signature = buildViewSignature(viewState);\n if (signature === state._lastViewSignature) return;\n state._lastViewSignature = signature;\n state._lastViewEmitMs = now;\n state.viewChangeHandler(viewState);\n}\n\n/**\n * Fit camera to view all content within specified bounds\n * @param {Object} state - Baselode3D scene state\n * @param {Object} bounds - Bounding box {minX, maxX, minY, maxY, minZ, maxZ}\n */\nexport function fitCameraToBounds(state, { minX, maxX, minY, maxY, minZ, maxZ }) {\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const sizeX = maxX - minX;\n const sizeY = maxY - minY;\n const sizeZ = maxZ - minZ;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/**\n * Recenter camera to origin at specified distance\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance from origin\n */\nexport function recenterCameraToOrigin(state, distance = 1000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(distance, distance, distance);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Position camera looking straight down from above\n * @param {Object} state - Baselode3D scene state\n * @param {number} distance - Distance above origin\n */\nexport function lookDown(state, distance = 2000) {\n if (!state.camera || !state.controls) return;\n state.controls.target.set(0, 0, 0);\n state.camera.position.set(0, 0, distance);\n state.camera.up.set(0, 1, 0);\n state.camera.lookAt(0, 0, 0);\n state.controls.update();\n}\n\n/**\n * Pan the camera view by screen-space delta\n * @param {Object} state - Baselode3D scene state\n * @param {number} dx - Horizontal pan delta\n * @param {number} dy - Vertical pan delta\n */\nexport function pan(state, dx = 0, dy = 0) {\n if (!state.controls) return;\n if (typeof state.controls.pan === 'function') {\n state.controls.pan(dx, dy);\n state.controls.update();\n }\n}\n\n/**\n * Zoom camera in or out by scale factor\n * @param {Object} state - Baselode3D scene state\n * @param {number} scale - Scale factor (>1 zooms out, <1 zooms in)\n */\nexport function dolly(state, scale = 1.1) {\n if (!state.controls || typeof state.controls.dollyIn !== 'function' || typeof state.controls.dollyOut !== 'function') return;\n if (scale > 1) {\n state.controls.dollyOut(scale);\n } else {\n state.controls.dollyIn(1 / scale);\n }\n state.controls.update();\n}\n\n/**\n * Focus camera on last computed bounds with optional padding\n * @param {Object} state - Baselode3D scene state with lastBounds property\n * @param {number} padding - Padding multiplier for bounds (1.2 = 20% larger view)\n */\nexport function focusOnLastBounds(state, padding = 1.2) {\n if (!state.lastBounds) return;\n const {\n minX, maxX, minY, maxY, minZ, maxZ\n } = state.lastBounds;\n const sizeX = (maxX - minX) * padding;\n const sizeY = (maxY - minY) * padding;\n const sizeZ = (maxZ - minZ) * padding;\n const centerX = (minX + maxX) / 2;\n const centerY = (minY + maxY) / 2;\n const centerZ = (minZ + maxZ) / 2;\n const maxDim = Math.max(sizeX, sizeY, sizeZ, 1);\n const distance = maxDim * 2;\n state.controls.target.set(centerX, centerY, centerZ);\n state.camera.position.set(centerX + distance, centerY + distance, centerZ + distance);\n state.camera.lookAt(centerX, centerY, centerZ);\n state.controls.update();\n}\n\n/**\n * Switch between orbit and fly camera control modes\n * @param {Object} state - Baselode3D scene state with orbit and fly controls\n * @param {string} mode - Control mode ('orbit' or 'fly')\n */\nexport function setControlMode(state, mode = 'orbit') {\n state.controlMode = mode === 'fly' ? 'fly' : 'orbit';\n if (state.controlMode === 'fly') {\n if (state.controls) state.controls.enabled = false;\n if (state.flyControls) state.flyControls.enabled = true;\n } else {\n if (state.flyControls) state.flyControls.enabled = false;\n if (state.controls) {\n state.controls.enabled = true;\n state.camera.getWorldDirection(state._tmpDir);\n const target = state.camera.position.clone().addScaledVector(state._tmpDir, 10);\n state.controls.target.copy(target);\n state.controls.update();\n }\n }\n}","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport * as THREE from 'three';\nimport { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';\nimport { FlyControls } from 'three/examples/jsm/controls/FlyControls';\nimport { ViewportGizmo } from 'three-viewport-gizmo';\nimport { getColorForValue } from '../data/blockModelLoader.js';\nimport { buildEqualRangeColorScale, getEqualRangeBinIndex, getEqualRangeColor } from './assayColorScale.js';\nimport {\n buildViewSignature,\n emitViewChangeIfNeeded,\n fitCameraToBounds,\n focusOnLastBounds,\n getViewState,\n lookDown,\n pan,\n dolly,\n recenterCameraToOrigin,\n setControlMode,\n setViewState\n} from './baselode3dCameraControls.js';\n\n/** Default color for low or zero assay values */\nconst LOW_ASSAY_GREY = '#9ca3af';\n\n/**\n * Get measured depth range for a segment between two points\n * @private\n */\nfunction getMeasuredDepthRange(p1, p2) {\n const md1 = Number(p1?.md);\n const md2 = Number(p2?.md);\n if (!Number.isFinite(md1) || !Number.isFinite(md2)) return null;\n const segStart = Math.min(md1, md2);\n const segEnd = Math.max(md1, md2);\n if (segEnd <= segStart) return null;\n return { segStart, segEnd };\n}\n\n/**\n * Calculate weighted average assay value for a segment overlapping with assay intervals\n * @private\n */\nfunction getWeightedIntervalValue(assayIntervals, segStart, segEnd) {\n let weightedSum = 0;\n let weightTotal = 0;\n\n for (let i = 0; i < assayIntervals.length; i += 1) {\n const candidate = assayIntervals[i];\n const from = Number(candidate?.from);\n const to = Number(candidate?.to);\n const value = Number(candidate?.value);\n if (!Number.isFinite(from) || !Number.isFinite(to) || !Number.isFinite(value) || to <= from) continue;\n const overlapStart = Math.max(segStart, from);\n const overlapEnd = Math.min(segEnd, to);\n const overlap = overlapEnd - overlapStart;\n if (overlap <= 0) continue;\n weightedSum += value * overlap;\n weightTotal += overlap;\n }\n\n if (weightTotal <= 0) return null;\n const value = weightedSum / weightTotal;\n return Number.isFinite(value) ? value : null;\n}\n\n/**\n * Get THREE.Color for an assay value based on color scale\n * @private\n */\nfunction getAssaySegmentColor(value, assayScale) {\n if (!Number.isFinite(value)) return new THREE.Color(LOW_ASSAY_GREY);\n const binIndex = getEqualRangeBinIndex(value, assayScale);\n if (binIndex < 0) return new THREE.Color(LOW_ASSAY_GREY);\n const colorHex = getEqualRangeColor(value, assayScale, LOW_ASSAY_GREY);\n return new THREE.Color(colorHex);\n}\n\n/**\n * Normalize drillhole rendering options with defaults\n * @private\n */\nfunction normalizeDrillholeRenderOptions(options = {}) {\n return {\n preserveView: Boolean(options.preserveView),\n assayIntervalsByHole: options.assayIntervalsByHole || null,\n selectedAssayVariable: options.selectedAssayVariable || ''\n };\n}\n\n/**\n * Collect all numeric assay values from interval data\n * @private\n */\nfunction collectAssayValues(assayIntervalsByHole, selectedAssayVariable) {\n if (!assayIntervalsByHole || !selectedAssayVariable) return [];\n const allAssayValues = [];\n Object.values(assayIntervalsByHole).forEach((intervals) => {\n (intervals || []).forEach((interval) => {\n const value = Number(interval?.value);\n if (Number.isFinite(value)) allAssayValues.push(value);\n });\n });\n return allAssayValues;\n}\n\n/**\n * Build user data object for drillhole mesh\n * @private\n */\nfunction buildHoleUserData(hole) {\n return {\n holeId: hole.id,\n project: hole.project\n };\n}\n\n/**\n * Baselode 3D Scene Manager\n * Manages THREE.js scene for rendering drillholes and block models in 3D\n * Supports orbit and fly camera controls, assay coloring, and interactive selection\n */\nclass Baselode3DScene {\n constructor() {\n this.container = null;\n this.scene = null;\n this.camera = null;\n this.renderer = null;\n this.controls = null;\n this.flyControls = null;\n this.gizmo = null;\n this.blocks = [];\n this.drillLines = [];\n this.drillMeshes = [];\n this.frameId = null;\n this.clock = new THREE.Clock();\n this.handleCanvasClick = null;\n this.raycaster = new THREE.Raycaster();\n this.pointer = new THREE.Vector2();\n this.drillholeClickHandler = null;\n this.controlMode = 'orbit';\n this._tmpDir = new THREE.Vector3();\n this.viewChangeHandler = null;\n this._lastViewSignature = '';\n this._lastViewEmitMs = 0;\n }\n\n init(container) {\n if (!container) return;\n this.container = container;\n\n const width = container.clientWidth;\n const height = container.clientHeight;\n\n // Scene\n this.scene = new THREE.Scene();\n this.scene.background = new THREE.Color(0xffffff);\n\n // Camera\n // Lower near plane to allow ultra-close zoom without clipping\n this.camera = new THREE.PerspectiveCamera(28, width / height, 0.001, 100000);\n this.camera.up.set(0, 0, 1);\n this.camera.position.set(50, 50, 50);\n this.camera.lookAt(0, 0, 0);\n\n // Renderer\n this.renderer = new THREE.WebGLRenderer({ antialias: true });\n this.renderer.setSize(width, height);\n this.renderer.setPixelRatio(window.devicePixelRatio);\n this.renderer.autoClear = false;\n container.appendChild(this.renderer.domElement);\n\n // Lighting - balanced for clean colors without bleeding\n const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);\n this.scene.add(ambientLight);\n\n const directionalLight = new THREE.DirectionalLight(0xffffff, 0.6);\n directionalLight.position.set(10, 10, 5);\n this.scene.add(directionalLight);\n\n // Axes helper\n const axesHelper = new THREE.AxesHelper(20);\n this.scene.add(axesHelper);\n\n // Controls\n this.controls = new OrbitControls(this.camera, this.renderer.domElement);\n this.controls.enableDamping = false; // remove inertial damping for snappier response\n this.controls.screenSpacePanning = true;\n this.controls.enableZoom = true;\n this.controls.zoomSpeed = 1.2;\n this.controls.minDistance = 0.003; // 10x closer zoom\n this.controls.maxDistance = 40000;\n // Controls: left drag = pan, scroll = zoom, right drag = rotate\n this.controls.mouseButtons = {\n LEFT: THREE.MOUSE.PAN,\n MIDDLE: THREE.MOUSE.DOLLY,\n RIGHT: THREE.MOUSE.ROTATE\n };\n this.controls.touches = {\n ONE: THREE.TOUCH.ROTATE,\n TWO: THREE.TOUCH.PAN\n };\n this.controls.maxPolarAngle = Math.PI;\n\n // Fly controls (disabled by default, toggled on demand)\n this.flyControls = new FlyControls(this.camera, this.renderer.domElement);\n this.flyControls.movementSpeed = 2000; // faster fly speed\n this.flyControls.rollSpeed = Math.PI / 12;\n this.flyControls.dragToLook = true;\n this.flyControls.enabled = false;\n\n // Viewport gizmo (interactive axis helper)\n this.gizmo = new ViewportGizmo(this.camera, this.renderer, {\n container: this.container,\n placement: 'top-right',\n size: 110,\n offset: { top: 12, right: 12 },\n animated: true,\n speed: 1.5\n });\n this.gizmo.attachControls(this.controls);\n this._attachCanvasClickHandler();\n\n // Animation loop\n const animate = () => {\n this.frameId = requestAnimationFrame(animate);\n const delta = this.clock.getDelta();\n this.renderer.clear();\n if (this.controlMode === 'fly' && this.flyControls?.enabled) {\n this.flyControls.update(delta);\n } else if (this.controls) {\n this.controls.update();\n }\n this._emitViewChangeIfNeeded();\n this.renderer.render(this.scene, this.camera);\n if (this.gizmo) this.gizmo.render();\n };\n animate();\n }\n\n setViewChangeHandler(handler) {\n this.viewChangeHandler = typeof handler === 'function' ? handler : null;\n }\n\n getViewState() {\n return getViewState(this);\n }\n\n setViewState(viewState) {\n return setViewState(this, viewState);\n }\n\n _buildViewSignature(viewState) {\n return buildViewSignature(viewState);\n }\n\n _emitViewChangeIfNeeded() {\n emitViewChangeIfNeeded(this);\n }\n\n _attachCanvasClickHandler() {\n const renderer = this.renderer;\n if (!renderer) return;\n\n this.handleCanvasClick = (event) => {\n if (event.button !== 0) return; // left click only\n\n // Ignore clicks inside the gizmo area to avoid conflicts\n if (this.gizmo?.domElement) {\n const gizmoRect = this.gizmo.domElement.getBoundingClientRect();\n if (\n event.clientX >= gizmoRect.left &&\n event.clientX <= gizmoRect.right &&\n event.clientY >= gizmoRect.top &&\n event.clientY <= gizmoRect.bottom\n ) {\n return;\n }\n }\n\n const rect = renderer.domElement.getBoundingClientRect();\n const localX = event.clientX - rect.left;\n const localY = event.clientY - rect.top;\n\n this.pointer.x = ((localX / rect.width) * 2) - 1;\n this.pointer.y = -((localY / rect.height) * 2) + 1;\n\n this.raycaster.setFromCamera(this.pointer, this.camera);\n const intersects = this.raycaster.intersectObjects(this.drillMeshes, true);\n if (intersects.length === 0) return;\n let obj = intersects[0].object;\n while (obj && obj.parent && !obj.userData?.holeId) {\n obj = obj.parent;\n }\n const holeId = obj?.userData?.holeId;\n const project = obj?.userData?.project;\n if (holeId && this.drillholeClickHandler) {\n this.drillholeClickHandler({ holeId, project });\n }\n };\n\n renderer.domElement.addEventListener('click', this.handleCanvasClick);\n }\n\n resize() {\n if (!this.container || !this.camera || !this.renderer) return;\n const width = this.container.clientWidth;\n const height = this.container.clientHeight;\n this.camera.aspect = width / height;\n this.camera.updateProjectionMatrix();\n this.renderer.setSize(width, height);\n if (this.gizmo) this.gizmo.update();\n }\n\n setBlocks(data, selectedProperty, stats) {\n if (!this.scene) return;\n\n this._clearBlocks();\n\n if (!data || !selectedProperty || !stats) return;\n\n let minX = Infinity, maxX = -Infinity;\n let minY = Infinity, maxY = -Infinity;\n let minZ = Infinity, maxZ = -Infinity;\n\n data.forEach((row) => {\n const {\n center_x = 0,\n center_y = 0,\n center_z = 0,\n size_x = 1,\n size_y = 1,\n size_z = 1\n } = row;\n\n minX = Math.min(minX, center_x - size_x / 2);\n maxX = Math.max(maxX, center_x + size_x / 2);\n minY = Math.min(minY, center_y - size_y / 2);\n maxY = Math.max(maxY, center_y + size_y / 2);\n minZ = Math.min(minZ, center_z - size_z / 2);\n maxZ = Math.max(maxZ, center_z + size_z / 2);\n\n const geometry = new THREE.BoxGeometry(size_x, size_y, size_z);\n const color = getColorForValue(row[selectedProperty], stats, THREE);\n const material = new THREE.MeshStandardMaterial({\n color,\n transparent: true,\n opacity: 0.7,\n side: THREE.DoubleSide\n });\n\n const block = new THREE.Mesh(geometry, material);\n block.position.set(center_x, center_y, center_z);\n this.scene.add(block);\n this.blocks.push(block);\n });\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n\n setDrillholes(holes, options = {}) {\n if (!this.scene) return;\n\n this._clearDrillholes();\n if (!holes || holes.length === 0) return;\n\n const { preserveView, assayIntervalsByHole, selectedAssayVariable } = normalizeDrillholeRenderOptions(options);\n const allAssayValues = collectAssayValues(assayIntervalsByHole, selectedAssayVariable);\n const assayScale = buildEqualRangeColorScale(allAssayValues);\n\n let minX = Infinity, maxX = -Infinity;\n let minY = Infinity, maxY = -Infinity;\n let minZ = Infinity, maxZ = -Infinity;\n\n const tmpVec = new THREE.Vector3();\n const up = new THREE.Vector3(0, 1, 0); // cylinder default axis\n\n holes.forEach((hole, idx) => {\n // Improved color palette: use golden angle for better distribution\n const goldenAngle = 137.5;\n const hue = ((idx * goldenAngle) % 360) / 360;\n const defaultColor = new THREE.Color().setHSL(hue, 0.75, 0.55);\n const points = (hole.points || []).map((p) => {\n minX = Math.min(minX, p.x);\n maxX = Math.max(maxX, p.x);\n minY = Math.min(minY, p.y);\n maxY = Math.max(maxY, p.y);\n minZ = Math.min(minZ, p.z);\n maxZ = Math.max(maxZ, p.z);\n const point = new THREE.Vector3(p.x, p.y, p.z);\n point.md = p.md;\n return point;\n });\n\n if (points.length < 2) {\n if (points.length === 1) {\n const sphereGeom = new THREE.SphereGeometry(5, 12, 12);\n const sphereMat = new THREE.MeshLambertMaterial({ \n color: defaultColor,\n emissive: defaultColor,\n emissiveIntensity: 0.2\n });\n const sphere = new THREE.Mesh(sphereGeom, sphereMat);\n sphere.position.copy(points[0]);\n sphere.userData = buildHoleUserData(hole);\n this.scene.add(sphere);\n this.drillLines.push(sphere);\n this.drillMeshes.push(sphere);\n }\n return;\n }\n\n const group = new THREE.Group();\n group.userData = buildHoleUserData(hole);\n const assayIntervals = selectedAssayVariable\n ? this._resolveAssayIntervalsForHole(hole, assayIntervalsByHole)\n : [];\n\n for (let i = 0; i < points.length - 1; i += 1) {\n const p1 = points[i];\n const p2 = points[i + 1];\n const dir = tmpVec.subVectors(p2, p1);\n const len = dir.length();\n if (len <= 0.001) continue;\n const radius = 2.2;\n const cylinderGeom = new THREE.CylinderGeometry(radius, radius, len, 6, 1, false);\n const segmentColor = this._getSegmentColor({\n selectedAssayVariable,\n assayIntervals,\n assayScale,\n holeId: hole.id,\n segmentIndex: i,\n p1,\n p2\n });\n const cylinderMat = new THREE.MeshLambertMaterial({ \n color: segmentColor,\n flatShading: true,\n emissive: segmentColor,\n emissiveIntensity: 0.15\n });\n const mesh = new THREE.Mesh(cylinderGeom, cylinderMat);\n mesh.position.copy(p1.clone().addScaledVector(dir, 0.5));\n mesh.quaternion.setFromUnitVectors(up, dir.clone().normalize());\n mesh.userData = buildHoleUserData(hole);\n group.add(mesh);\n this.drillMeshes.push(mesh);\n }\n\n this.scene.add(group);\n this.drillLines.push(group);\n });\n\n if (this.camera && this.controls) {\n this.lastBounds = { minX, maxX, minY, maxY, minZ, maxZ };\n if (!preserveView) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n }\n }\n\n _getSegmentColor({ selectedAssayVariable, assayIntervals, assayScale, holeId, segmentIndex, p1, p2 }) {\n if (!selectedAssayVariable) {\n return randomSegmentColor(holeId, segmentIndex);\n }\n // Special mode: color by presence of any assay data\n if (selectedAssayVariable === '__HAS_ASSAY__') {\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n // Check for ANY overlap with assay intervals\n const hasData = assayIntervals.some((interval) => {\n const from = Number(interval?.from);\n const to = Number(interval?.to);\n if (!Number.isFinite(from) || !Number.isFinite(to)) return false;\n const overlapStart = Math.max(depthRange.segStart, from);\n const overlapEnd = Math.min(depthRange.segEnd, to);\n return overlapEnd > overlapStart;\n });\n return hasData ? new THREE.Color('#ff8c42') : new THREE.Color(LOW_ASSAY_GREY);\n }\n if (!assayIntervals?.length) return new THREE.Color(LOW_ASSAY_GREY);\n const depthRange = getMeasuredDepthRange(p1, p2);\n if (!depthRange) return new THREE.Color(LOW_ASSAY_GREY);\n const value = getWeightedIntervalValue(assayIntervals, depthRange.segStart, depthRange.segEnd);\n return getAssaySegmentColor(value, assayScale);\n }\n\n _resolveAssayIntervalsForHole(hole, assayIntervalsByHole) {\n if (!assayIntervalsByHole || !hole) return [];\n const holeId = hole.id || hole.holeId;\n if (!holeId) return [];\n\n // Try exact match first\n const exact = assayIntervalsByHole[holeId];\n if (Array.isArray(exact) && exact.length) return exact;\n\n // Try normalized (case-insensitive) match\n const normalized = normalizeHoleKey(holeId);\n if (normalized) {\n const byNormalized = assayIntervalsByHole[normalized];\n if (Array.isArray(byNormalized) && byNormalized.length) return byNormalized;\n }\n\n return [];\n }\n\n _fitCameraToBounds({ minX, maxX, minY, maxY, minZ, maxZ }) {\n fitCameraToBounds(this, { minX, maxX, minY, maxY, minZ, maxZ });\n }\n\n recenterCameraToOrigin(distance = 1000) {\n recenterCameraToOrigin(this, distance);\n }\n\n lookDown(distance = 2000) {\n lookDown(this, distance);\n }\n\n pan(dx = 0, dy = 0) {\n pan(this, dx, dy);\n }\n\n dolly(scale = 1.1) {\n dolly(this, scale);\n }\n\n focusOnLastBounds(padding = 1.2) {\n focusOnLastBounds(this, padding);\n }\n\n _clearBlocks() {\n this.blocks.forEach((block) => {\n this.scene.remove(block);\n block.geometry.dispose();\n block.material.dispose();\n });\n this.blocks = [];\n }\n\n _clearDrillholes() {\n this.drillLines.forEach((line) => {\n this.scene.remove(line);\n if (line.isGroup) {\n line.traverse((child) => {\n if (child.isMesh) {\n child.geometry.dispose();\n child.material.dispose();\n }\n });\n } else if (line.isMesh) {\n line.geometry.dispose();\n line.material.dispose();\n }\n });\n this.drillLines = [];\n this.drillMeshes = [];\n }\n\n dispose() {\n if (this.frameId) cancelAnimationFrame(this.frameId);\n if (this.renderer && this.handleCanvasClick) {\n this.renderer.domElement.removeEventListener('click', this.handleCanvasClick);\n }\n\n if (this.gizmo) {\n this.gizmo.dispose();\n this.gizmo = null;\n }\n\n this.viewChangeHandler = null;\n\n this._clearBlocks();\n this._clearDrillholes();\n\n if (this.controls) this.controls.dispose();\n if (this.flyControls) this.flyControls.dispose();\n if (this.renderer) {\n this.renderer.dispose();\n if (this.container && this.renderer.domElement) {\n this.container.removeChild(this.renderer.domElement);\n }\n }\n }\n\n setDrillholeClickHandler(handler) {\n this.drillholeClickHandler = handler;\n }\n\n setControlMode(mode = 'orbit') {\n setControlMode(this, mode);\n }\n}\n\nexport default Baselode3DScene;\n\nfunction normalizeHoleKey(value) {\n return `${value ?? ''}`.trim().toLowerCase();\n}\n\nfunction randomSegmentColor(holeId, segmentIndex) {\n const seed = `${holeId ?? ''}:${segmentIndex ?? 0}`;\n const base = seededUnit(seed);\n const band = ((segmentIndex ?? 0) % 14) / 14;\n const hue = (base * 0.15 + band * 0.85) % 1;\n const color = new THREE.Color();\n color.setHSL(hue, 1.0, 0.5);\n return color;\n}\n\nfunction seededUnit(input) {\n const text = `${input ?? ''}`;\n let hash = 2166136261;\n for (let i = 0; i < text.length; i += 1) {\n hash ^= text.charCodeAt(i);\n hash = Math.imul(hash, 16777619);\n }\n return (hash >>> 0) / 4294967295;\n}\n","/*\n * Copyright (C) 2026 Tamara Vasey\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\nimport './Baselode3DControls.css';\n\n/**\n * 3D scene control buttons component\n * Provides UI controls for camera manipulation in the 3D drillhole viewer\n * @param {Object} props - Component props\n * @param {string} props.controlMode - Current control mode ('orbit' or 'fly')\n * @param {Function} props.onToggleFly - Handler for toggling fly mode\n * @param {Function} props.onRecenter - Handler for recentering camera\n * @param {Function} props.onLookDown - Handler for top-down view\n * @param {Function} props.onFit - Handler for fitting camera to scene\n * @returns {JSX.Element} Control buttons component\n */\nfunction Baselode3DControls({\n controlMode = 'orbit',\n onToggleFly = () => {},\n onRecenter = () => {},\n onLookDown = () => {},\n onFit = () => {}\n}) {\n return (\n <div className=\"baselode-3d-controls\">\n <button type=\"button\" className=\"ghost-button\" onClick={onRecenter}>\n Recenter to (0,0,0)\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onLookDown}>\n Look down\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onFit}>\n Fit to scene\n </button>\n <button type=\"button\" className=\"ghost-button\" onClick={onToggleFly}>\n {controlMode === 'orbit' ? 'Enable fly controls' : 'Disable fly controls'}\n </button>\n </div>\n );\n}\n\nexport default Baselode3DControls;\n"],"names":["HOLE_ID","LATITUDE","LONGITUDE","ELEVATION","AZIMUTH","DIP","FROM","TO","MID","PROJECT_ID","EASTING","NORTHING","CRS","DEPTH","BASELODE_DATA_MODEL_DRILL_COLLAR","BASELODE_DATA_MODEL_DRILL_SURVEY","BASELODE_DATA_MODEL_DRILL_ASSAY","DEFAULT_COLUMN_MAP","_COLUMN_LOOKUP","standardCol","variations","variation","normalized","normalizeFieldName","name","standardizeColumns","row","columnMap","sourceColumnMap","lookup","rawName","expectedName","normalizedKey","normalizedValue","renamed","col","value","key","mapped","standardizeRowArray","rows","ASSAY_NON_VALUE_FIELDS","DATA_LOG_PREFIX","toError","error","fallbackMessage","message","withDataErrorContext","context","baseError","wrapped","logDataWarning","logDataInfo","normalizeRow","rawRow","extractIdFields","extractInterval","holeIdRaw","holeId","project","from","to","intervalsToHole","intervals","sorted","a","b","points","iv","rest","pointData","_a","parseAssayHoleIds","file","resolve","reject","holeIds","Papa","results","hid","hasAssayValue","k","v","parseAssayHoleIdsWithAssays","byHole","parseAssayHole","config","wanted","interval","hole","parseAssaysCSV","holes","normalizeCsvRow","pickFirstPresent","keys","fallback","DEFAULT_TRACE_PLOT_COUNT","reorderHoleIds","ids","focusId","matchIdx","id","selected","_","idx","coerceChartTypeForProperty","property","chartType","categoricalProps","numericDefaultChartType","buildTraceConfigsForHoleIds","focusedHoleId","plotCount","defaultProp","ordered","deriveAssayProps","h","candidates","p","numericProps","hasNumber","hasValue","i","loadAssayMetadata","loadAssayHole","buildAssayState","traceConfigs","loadAssayFile","state","parseSurveyCSV","err","norm","lat","toNumber","lng","surveyDepth","dip","azimuth","maxDepth","n","desurveyTraces","collars","surveys","collarByKey","c","refLat","_b","refLng","_c","_d","refMetersPerDegLat","refMetersPerDegLon","grouped","s","stations","collar","lat0","lng0","metersPerDegLat","metersPerDegLon","baseX","baseY","accX","accY","accZ","curr","prev","currDepth","currAzimuth","currDip","prevDepth","prevAzimuth","prevDip","deltaMD","inc1","toInclination","inc2","az1","degToRad","az2","beta","rf","dx","dy","dzDown","pointsWithGeo","d","dipDeg","val","incDeg","clamped","normalizeHoleIdValue","canonicalizeHoleIdRows","holeIdCol","preferred","resolved","angle","directionCosines","azRad","dipRad","ca","cb","cc","segmentDisplacement","deltaMd","az0","dip0","dip1","method","dc0","dc1","azAvg","dipAvg","dcAvg","dot","dogleg","desurvey","rowsCollars","rowsSurveys","options","step","safeStep","collarsCanonical","surveysCanonical","collarsByHole","surveysByHole","out","x","y","z","mdCursor","azPrev","dipPrev","firstRecord","s0","s1","md0","segmentSteps","mdIncrement","stepIdx","weight","azInterp","dipInterp","disp","record","minimumCurvatureDesurvey","tangentialDesurvey","balancedTangentialDesurvey","buildTraces","nearestByMeasuredDepth","traceRows","midMd","best","bestDist","md","dist","attachAssayPositions","assays","traces","assaysCanonical","tracesCanonical","tracesByHole","assay","nearest","merged","parseDrillholesCSV","order","pts","toArray","source","sortByColumns","columns","av","bv","parseCsv","papaParseConfig","standardizeRow","loadTable","kind","loadCollars","crs","keepAll","tableOptions","standardized","hasXY","hasLatLon","result","loadSurveys","required","loadAssays","joinAssaysToTraces","onCols","keyOf","tracesByKey","trace","filterByProject","projectId","coerceNumeric","next","column","assembleDataset","structures","metadata","parseBlockModelCSV","data","excludeColumns","propertyColumns","calculatePropertyStats","values","min","max","getColorForValue","stats","THREEInstance","range","hue","NUMERIC_LINE_COLOR","NUMERIC_MARKER_COLOR","ERROR_COLOR","holeHasData","buildIntervalPoints","isCategorical","rawPoints","seen","fromVal","toVal","rawVal","mid","buildCategoricalConfig","segments","y0","y1","palette","shapes","seg","buildNumericConfig","isBar","isMarkersOnly","isLineOnly","baseTrace","errorConfig","buildPlotConfig","DEFAULT_NUMERIC_CHART_TYPE","CATEGORICAL_CHART_OPTIONS","NUMERIC_CHART_OPTIONS","resolveChartType","chartOptions","requestedChartType","opt","TracePlot","graph","holeOptions","propertyOptions","onConfigChange","containerRef","useRef","selectedHoleId","effectiveChartType","renderError","setRenderError","useState","useEffect","target","layout","plotConfig","Plotly","resizeObserver","jsx","jsxs","e","label","useDrillholeTraceGrid","initialFocusedHoleId","sourceFile","setHoles","setHoleIds","setNumericProps","setCategoricalProps","setDefaultProp","setTraceConfigs","setError","setFocusedHoleId","loadingHoles","setLoadingHoles","uniqueIds","orderedHoleIds","existing","cfg","already","loading","props","configs","useMemo","labeledHoleOptions","traceGraphs","handleConfigChange","index","patch","ASSAY_COLOR_PALETTE_10","buildEqualRangeColorScale","colors","finiteValues","binCount","bins","percentileLow","percentileHigh","idxLow","idxHigh","lower","upper","formatBinLabel","formatVal","getEqualRangeBinIndex","scale","bin","getEqualRangeColor","fallbackColor","normalizePoint","point","projectTraceToSection","origin","ox","oy","cosA","sinA","sectionWindow","width","projected","half","planView","depthSlice","colorBy","top","bottom","sectionView","section","getHoleId","tracesAsSegments","payload","intervalsAsTubes","radius","annotationsFromIntervals","labelCol","buildViewSignature","viewState","toNum","_e","_f","_g","_h","_i","getViewState","setViewState","camera","up","emitViewChangeIfNeeded","now","signature","fitCameraToBounds","minX","maxX","minY","maxY","minZ","maxZ","centerX","centerY","centerZ","sizeX","sizeY","sizeZ","distance","recenterCameraToOrigin","lookDown","pan","dolly","focusOnLastBounds","padding","setControlMode","mode","LOW_ASSAY_GREY","getMeasuredDepthRange","p1","p2","md1","md2","segStart","segEnd","getWeightedIntervalValue","assayIntervals","weightedSum","weightTotal","candidate","overlapStart","overlap","getAssaySegmentColor","assayScale","THREE","colorHex","normalizeDrillholeRenderOptions","collectAssayValues","assayIntervalsByHole","selectedAssayVariable","allAssayValues","buildHoleUserData","Baselode3DScene","container","height","ambientLight","directionalLight","axesHelper","OrbitControls","FlyControls","ViewportGizmo","animate","delta","handler","renderer","event","gizmoRect","rect","localX","localY","intersects","obj","selectedProperty","center_x","center_y","center_z","size_x","size_y","size_z","geometry","color","material","block","preserveView","tmpVec","defaultColor","sphereGeom","sphereMat","sphere","group","dir","len","cylinderGeom","segmentColor","cylinderMat","mesh","segmentIndex","randomSegmentColor","depthRange","exact","normalizeHoleKey","byNormalized","line","child","seed","base","seededUnit","band","input","text","hash","Baselode3DControls","controlMode","onToggleFly","onRecenter","onLookDown","onFit"],"mappings":"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"}
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .baselode-3d-controls{position:absolute;bottom:20px;left:20px;display:flex;gap:10px;flex-wrap:wrap;background:#ffffffe6;border:1px solid #e0e0e0;border-radius:10px;padding:10px;box-shadow:0 8px 24px #0000001a}.baselode-3d-controls .ghost-button,.baselode-3d-controls button{border:1px solid #d0d0d0;background:#fff;color:#333;cursor:pointer;transition:background .15s,border-color .15s}.baselode-3d-controls button:hover{background:#f2f2f2;border-color:silver}.baselode-3d-controls .ghost-button{padding:8px 12px;border-radius:8px}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "baselode",
3
+ "version": "0.1.1",
4
+ "description": "Geoscience drillhole data processing and visualization toolkit",
5
+ "license": "GPL-3.0-or-later",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/darkmine-oss/baselode.git",
9
+ "directory": "javascript/packages/baselode"
10
+ },
11
+ "homepage": "https://github.com/darkmine-oss/baselode",
12
+ "bugs": {
13
+ "url": "https://github.com/darkmine-oss/baselode/issues"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "type": "module",
19
+ "main": "./dist/baselode.js",
20
+ "module": "./dist/baselode.js",
21
+ "exports": {
22
+ ".": {
23
+ "import": "./dist/baselode.js"
24
+ },
25
+ "./style.css": "./dist/style.css"
26
+ },
27
+ "files": [
28
+ "dist/baselode.js",
29
+ "dist/baselode.js.map",
30
+ "dist/style.css",
31
+ "README.md"
32
+ ],
33
+ "sideEffects": [
34
+ "**/*.css"
35
+ ],
36
+ "scripts": {
37
+ "version:from-tag": "node ./scripts/set-version-from-tag.mjs",
38
+ "build": "vite build",
39
+ "prepack": "npm run version:from-tag && npm run build",
40
+ "pack:check": "npm pack --dry-run",
41
+ "publish:npm": "npm publish",
42
+ "publish:npm:provenance": "npm publish --provenance",
43
+ "dev": "vite build --watch",
44
+ "test": "vitest run",
45
+ "test:watch": "vitest"
46
+ },
47
+ "peerDependencies": {
48
+ "papaparse": ">=5.0.0",
49
+ "plotly.js-dist-min": ">=2.0.0",
50
+ "react": ">=18.0.0",
51
+ "react-dom": ">=18.0.0",
52
+ "three": ">=0.150.0",
53
+ "three-viewport-gizmo": ">=2.0.0"
54
+ },
55
+ "devDependencies": {
56
+ "@vitejs/plugin-react": "^4.2.1",
57
+ "vite": "^5.0.8",
58
+ "vitest": "^2.1.8"
59
+ }
60
+ }