qgis-tem-loader

qgis plugin for loading TEM geophysical inversion XYZ files as 3D objects
git clone git://src.adamsgaard.dk/qgis-tem-loader # fast
git clone https://src.adamsgaard.dk/qgis-tem-loader.git # slow
Log | Files | Refs | README | LICENSE Back to index

commit a0568d22390c72d4fcab7086e6cfe382dbc2d770
parent 9fc5d8ba8d2e114a14bcd444c891956747fa3497
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Thu, 23 Apr 2026 16:37:04 +0200

feat(core): add SCI Workbench XYZ format support

Diffstat:
Mtem_loader/core.py | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtem_loader/tem_loader.py | 7++++---
Atest/data/sci_workbench_2026_1.xyz | 173+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/test_core.py | 38++++++++++++++++++++++++++++++++++++++
4 files changed, 317 insertions(+), 3 deletions(-)

diff --git a/tem_loader/core.py b/tem_loader/core.py @@ -16,6 +16,17 @@ AARHUS_REQUIRED_COLUMNS = { 'THK_1', 'DOI_STANDARD', } +SCI_REQUIRED_COLUMNS = { + 'Line_No', + 'Layer_No', + 'X', + 'Y', + 'Elevation_Cell', + 'Resistivity', + 'Depth_top', + 'Depth_bottom', + 'Thickness', +} EPSG_PATTERN = re.compile(r"\bepsg\s*:\s*(\d+)\b", re.IGNORECASE) HEADER_ALIASES = { @@ -40,6 +51,8 @@ def detect_format(headers): return 'temimage' if AARHUS_REQUIRED_COLUMNS.issubset(header_set): return 'aarhus' + if SCI_REQUIRED_COLUMNS.issubset(header_set): + return 'sci' return None @@ -111,6 +124,8 @@ def process_xyz(path): if is_header_line(left_stripped): headers = normalize_header_tokens(left_stripped) source_format = detect_format(headers) + if source_format == 'sci': + return _process_sci_rows(f, headers, line_number) if source_format == 'temimage': res_cols = get_numbered_columns(headers, 'Res_') thick_cols = get_numbered_columns(headers, 'Thick_') @@ -240,6 +255,93 @@ def process_xyz(path): return points, doi_points, layers +def _process_sci_rows(f, headers, header_line_number): + soundings = {} + for offset, raw_line in enumerate(f, start=1): + line_number = header_line_number + offset + stripped = raw_line.strip() + if not stripped: + continue + + values = stripped.split() + if len(values) == len(headers) + 1: + values = values[1:] + if len(values) != len(headers): + raise ValueError( + f'Row {line_number} has {len(values)} columns, ' + f'expected {len(headers)}' + ) + + row = dict(zip(headers, values)) + key = (int(float(row['Line_No'])), row['X'], row['Y']) + soundings.setdefault(key, []).append(row) + + points = [] + layers = [] + line_ordinals = {} + + for (line_no, x_str, y_str), rows in soundings.items(): + rows.sort(key=lambda r: int(float(r['Layer_No']))) + ordinal = line_ordinals.get(line_no, 0) + 1 + line_ordinals[line_no] = ordinal + + line = str(line_no) + station_no = f'{line}_{ordinal:05d}' + x = float(x_str) + y = float(y_str) + + layer1 = rows[0] + surface_z = float(layer1['Elevation_Cell']) + float(layer1['Thickness']) / 2.0 + + n_layers = 0 + for r in rows: + try: + res = float(r['Resistivity']) + thick = float(r['Thickness']) + depth_top = float(r['Depth_top']) + depth_bottom = float(r['Depth_bottom']) + except (ValueError, TypeError): + break + if math.isnan(res) or math.isnan(thick): + break + if math.isnan(depth_top) or math.isnan(depth_bottom): + break + + z_top = surface_z - depth_top + z_bot = surface_z - depth_bottom + z_mid = (z_top + z_bot) / 2 + layer_no = int(float(r['Layer_No'])) + + layer_wkt = f'LINESTRING Z ({x} {y} {z_top}, {x} {y} {z_bot})' + layers.append({ + 'X': x, + 'Y': y, + 'Z': surface_z, + 'ZTop': z_top, + 'ZMid': z_mid, + 'ZBottom': z_bot, + 'DepthTop': depth_top, + 'DepthBottom': depth_bottom, + 'Resistivity': res, + 'Layer': layer_no, + 'Geometry': layer_wkt, + }) + n_layers += 1 + + point_wkt = f'POINT Z ({x} {y} {surface_z})' + points.append({ + 'X': x, + 'Y': y, + 'Z': surface_z, + 'Line': line, + 'StationNo': station_no, + 'NumLayers': n_layers, + 'Geometry': point_wkt, + }) + + return points, [], layers + + def write_csv(rows, base_path, suffix): out_path = Path(base_path).with_suffix(suffix) if not rows: diff --git a/tem_loader/tem_loader.py b/tem_loader/tem_loader.py @@ -62,7 +62,7 @@ class TEMLoaderPlugin: def _load_xyz(self, filepath): points, doi_points, layers = core.process_xyz(filepath) pts_csv = core.write_csv(points, filepath, '.points.csv') - doi_csv = core.write_csv(doi_points, filepath, '.doi.csv') + doi_csv = core.write_csv(doi_points, filepath, '.doi.csv') if doi_points else None lyr_csv = core.write_csv(layers, filepath, '.layers.csv') project = QgsProject.instance() @@ -83,9 +83,10 @@ class TEMLoaderPlugin: loaded_layers = {} source_layers = [ ('layers', lyr_csv, 'LineString'), - ('doi', doi_csv, 'Point'), - ('points', pts_csv, 'Point'), ] + if doi_csv is not None: + source_layers.append(('doi', doi_csv, 'Point')) + source_layers.append(('points', pts_csv, 'Point')) for name, csv_path, geom_type in source_layers: uri = _build_delimited_text_uri(csv_path, geom_type, crs_str) layer = QgsVectorLayer(uri, name, 'delimitedtext') diff --git a/test/data/sci_workbench_2026_1.xyz b/test/data/sci_workbench_2026_1.xyz @@ -0,0 +1,173 @@ +/INFO +/AGS Workbench export file. File created: 23-03-2026 12:03:17. Exported from /Workbench64 . User: Terese +/WB VERSION +/2026.1 +/WORKSPACE NAME +/C:\Users\teres\OneDrive - Aarhus universitet\Speciale Aroe\Workbench_2.0\Workspace_Speciale_Aroe_2.0\ +/NODE NAME(S) +/Q_SCI_sTEM_og_Arne_tTEM +/DUMMY +/9999 +/DATA TYPE +/DTSTEM +/COORDINATE SYSTEM +/ETRS89 UTM zone 32N (epsg:25832) +/SOURCE +/Model selection +/MODEL UNIT +/Resistivity (Ohm-m) / conductivity (mS/m) +/NUMBER OF LAYERS +/30 +/LENGTH UNIT +/Meter +/ ID Line_No Layer_No X Y Elevation_Cell Resistivity Resistivity_STD Conductivity Depth_top Depth_bottom Thickness Thickness_STD + 1 1 1 585623.06 6080634.65 9.34666E+00 3.11000E+01 2.36000E+00 3.21543E+01 0.00000E+00 1.00000E+00 1.00000E+00 1.00100E+00 + 2 1 1 585685.73 6080652.58 2.18899E+01 3.40600E+01 2.33000E+00 2.93600E+01 0.00000E+00 1.00000E+00 1.00000E+00 1.00100E+00 + 3 1 1 585528.09 6080886.55 2.02113E+01 5.44600E+01 2.85000E+00 1.83621E+01 0.00000E+00 1.00000E+00 1.00000E+00 1.00100E+00 + 4 1 1 585208.91 6081489.01 2.77181E+01 5.40900E+01 2.72000E+00 1.84877E+01 0.00000E+00 1.00000E+00 1.00000E+00 1.00100E+00 + 5 1 1 585505.78 6080913.69 1.89971E+01 5.83600E+01 2.91000E+00 1.71350E+01 0.00000E+00 1.00000E+00 1.00000E+00 1.00100E+00 + 1 1 2 585623.06 6080634.65 8.30266E+00 3.14500E+01 1.92900E+00 3.17965E+01 1.00000E+00 2.08800E+00 1.08800E+00 1.00100E+00 + 2 1 2 585685.73 6080652.58 2.08459E+01 3.63200E+01 1.92100E+00 2.75330E+01 1.00000E+00 2.08800E+00 1.08800E+00 1.00100E+00 + 3 1 2 585528.09 6080886.55 1.91673E+01 5.33600E+01 2.36000E+00 1.87406E+01 1.00000E+00 2.08800E+00 1.08800E+00 1.00100E+00 + 4 1 2 585208.91 6081489.01 2.66741E+01 5.42200E+01 2.24000E+00 1.84434E+01 1.00000E+00 2.08800E+00 1.08800E+00 1.00100E+00 + 5 1 2 585505.78 6080913.69 1.79531E+01 5.68900E+01 2.41000E+00 1.75778E+01 1.00000E+00 2.08800E+00 1.08800E+00 1.00100E+00 + 1 1 3 585623.06 6080634.65 7.16666E+00 3.26900E+01 1.72400E+00 3.05904E+01 2.08800E+00 3.27200E+00 1.18400E+00 1.00100E+00 + 2 1 3 585685.73 6080652.58 1.97099E+01 4.06500E+01 1.74400E+00 2.46002E+01 2.08800E+00 3.27200E+00 1.18400E+00 1.00100E+00 + 3 1 3 585528.09 6080886.55 1.80313E+01 5.10600E+01 2.02000E+00 1.95848E+01 2.08800E+00 3.27200E+00 1.18400E+00 1.00100E+00 + 4 1 3 585208.91 6081489.01 2.55381E+01 5.41600E+01 1.93900E+00 1.84638E+01 2.08800E+00 3.27200E+00 1.18400E+00 1.00100E+00 + 5 1 3 585505.78 6080913.69 1.68171E+01 5.37000E+01 2.06000E+00 1.86220E+01 2.08800E+00 3.27200E+00 1.18400E+00 1.00100E+00 + 1 1 4 585623.06 6080634.65 5.93066E+00 3.45000E+01 1.71300E+00 2.89855E+01 3.27200E+00 4.56000E+00 1.28800E+00 1.00100E+00 + 2 1 4 585685.73 6080652.58 1.84739E+01 4.61600E+01 1.75500E+00 2.16638E+01 3.27200E+00 4.56000E+00 1.28800E+00 1.00100E+00 + 3 1 4 585528.09 6080886.55 1.67953E+01 4.75000E+01 1.83000E+00 2.10526E+01 3.27200E+00 4.56000E+00 1.28800E+00 1.00100E+00 + 4 1 4 585208.91 6081489.01 2.43021E+01 5.40200E+01 1.79800E+00 1.85117E+01 3.27200E+00 4.56000E+00 1.28800E+00 1.00100E+00 + 5 1 4 585505.78 6080913.69 1.55811E+01 4.90400E+01 1.84600E+00 2.03915E+01 3.27200E+00 4.56000E+00 1.28800E+00 1.00100E+00 + 1 1 5 585623.06 6080634.65 4.58566E+00 3.62200E+01 1.80900E+00 2.76091E+01 4.56000E+00 5.96200E+00 1.40200E+00 1.00100E+00 + 2 1 5 585685.73 6080652.58 1.71289E+01 5.15500E+01 1.86600E+00 1.93986E+01 4.56000E+00 5.96200E+00 1.40200E+00 1.00100E+00 + 3 1 5 585528.09 6080886.55 1.54503E+01 4.28700E+01 1.76800E+00 2.33263E+01 4.56000E+00 5.96200E+00 1.40200E+00 1.00100E+00 + 4 1 5 585208.91 6081489.01 2.29571E+01 5.40300E+01 1.79400E+00 1.85082E+01 4.56000E+00 5.96200E+00 1.40200E+00 1.00100E+00 + 5 1 5 585505.78 6080913.69 1.42361E+01 4.34100E+01 1.76400E+00 2.30362E+01 4.56000E+00 5.96200E+00 1.40200E+00 1.00100E+00 + 1 1 6 585623.06 6080634.65 3.12216E+00 3.69900E+01 1.91000E+00 2.70343E+01 5.96200E+00 7.48700E+00 1.52500E+00 1.00100E+00 + 2 1 6 585685.73 6080652.58 1.56654E+01 5.53400E+01 1.98400E+00 1.80701E+01 5.96200E+00 7.48700E+00 1.52500E+00 1.00100E+00 + 3 1 6 585528.09 6080886.55 1.39868E+01 3.76900E+01 1.79200E+00 2.65322E+01 5.96200E+00 7.48700E+00 1.52500E+00 1.00100E+00 + 4 1 6 585208.91 6081489.01 2.14936E+01 5.43600E+01 1.87100E+00 1.83959E+01 5.96200E+00 7.48700E+00 1.52500E+00 1.00100E+00 + 5 1 6 585505.78 6080913.69 1.27726E+01 3.78100E+01 1.77800E+00 2.64480E+01 5.96200E+00 7.48700E+00 1.52500E+00 1.00100E+00 + 1 1 7 585623.06 6080634.65 1.52966E+00 3.60300E+01 1.94300E+00 2.77546E+01 7.48700E+00 9.14700E+00 1.66000E+00 1.00100E+00 + 2 1 7 585685.73 6080652.58 1.40729E+01 5.61400E+01 2.05000E+00 1.78126E+01 7.48700E+00 9.14700E+00 1.66000E+00 1.00100E+00 + 3 1 7 585528.09 6080886.55 1.23943E+01 3.25200E+01 1.83300E+00 3.07503E+01 7.48700E+00 9.14700E+00 1.66000E+00 1.00100E+00 + 4 1 7 585208.91 6081489.01 1.99011E+01 5.49700E+01 1.96100E+00 1.81917E+01 7.48700E+00 9.14700E+00 1.66000E+00 1.00100E+00 + 5 1 7 585505.78 6080913.69 1.11801E+01 3.30500E+01 1.82400E+00 3.02572E+01 7.48700E+00 9.14700E+00 1.66000E+00 1.00100E+00 + 1 1 8 585623.06 6080634.65 -2.03339E-01 3.30600E+01 1.88000E+00 3.02480E+01 9.14700E+00 1.09530E+01 1.80600E+00 1.00100E+00 + 2 1 8 585685.73 6080652.58 1.23399E+01 5.35500E+01 2.02000E+00 1.86741E+01 9.14700E+00 1.09530E+01 1.80600E+00 1.00100E+00 + 3 1 8 585528.09 6080886.55 1.06613E+01 2.77300E+01 1.82300E+00 3.60620E+01 9.14700E+00 1.09530E+01 1.80600E+00 1.00100E+00 + 4 1 8 585208.91 6081489.01 1.81681E+01 5.56900E+01 2.01000E+00 1.79565E+01 9.14700E+00 1.09530E+01 1.80600E+00 1.00100E+00 + 5 1 8 585505.78 6080913.69 9.44711E+00 2.92900E+01 1.83200E+00 3.41413E+01 9.14700E+00 1.09530E+01 1.80600E+00 1.00100E+00 + 1 1 9 585623.06 6080634.65 -2.08884E+00 2.80200E+01 1.74200E+00 3.56888E+01 1.09530E+01 1.29180E+01 1.96500E+00 1.00100E+00 + 2 1 9 585685.73 6080652.58 1.04544E+01 4.70700E+01 1.91100E+00 2.12450E+01 1.09530E+01 1.29180E+01 1.96500E+00 1.00100E+00 + 3 1 9 585528.09 6080886.55 8.77584E+00 2.33900E+01 1.72900E+00 4.27533E+01 1.09530E+01 1.29180E+01 1.96500E+00 1.00100E+00 + 4 1 9 585208.91 6081489.01 1.62826E+01 5.60300E+01 1.98900E+00 1.78476E+01 1.09530E+01 1.29180E+01 1.96500E+00 1.00100E+00 + 5 1 9 585505.78 6080913.69 7.56161E+00 2.58700E+01 1.75800E+00 3.86548E+01 1.09530E+01 1.29180E+01 1.96500E+00 1.00100E+00 + 1 1 10 585623.06 6080634.65 -4.14034E+00 2.15900E+01 1.59200E+00 4.63177E+01 1.29180E+01 1.50560E+01 2.13800E+00 1.00100E+00 + 2 1 10 585685.73 6080652.58 8.40292E+00 3.75100E+01 1.74600E+00 2.66596E+01 1.29180E+01 1.50560E+01 2.13800E+00 1.00100E+00 + 3 1 10 585528.09 6080886.55 6.72434E+00 1.96000E+01 1.59200E+00 5.10204E+01 1.29180E+01 1.50560E+01 2.13800E+00 1.00100E+00 + 4 1 10 585208.91 6081489.01 1.42311E+01 5.49300E+01 1.90000E+00 1.82050E+01 1.29180E+01 1.50560E+01 2.13800E+00 1.00100E+00 + 5 1 10 585505.78 6080913.69 5.51011E+00 2.20000E+01 1.62600E+00 4.54545E+01 1.29180E+01 1.50560E+01 2.13800E+00 1.00100E+00 + 1 1 11 585623.06 6080634.65 -6.37234E+00 1.53000E+01 1.50700E+00 6.53595E+01 1.50560E+01 1.73820E+01 2.32600E+00 1.00100E+00 + 2 1 11 585685.73 6080652.58 6.17092E+00 2.69800E+01 1.59700E+00 3.70645E+01 1.50560E+01 1.73820E+01 2.32600E+00 1.00100E+00 + 3 1 11 585528.09 6080886.55 4.49234E+00 1.63700E+01 1.50900E+00 6.10874E+01 1.50560E+01 1.73820E+01 2.32600E+00 1.00100E+00 + 4 1 11 585208.91 6081489.01 1.19991E+01 5.06000E+01 1.77400E+00 1.97628E+01 1.50560E+01 1.73820E+01 2.32600E+00 1.00100E+00 + 5 1 11 585505.78 6080913.69 3.27811E+00 1.76400E+01 1.52100E+00 5.66893E+01 1.50560E+01 1.73820E+01 2.32600E+00 1.00100E+00 + 1 1 12 585623.06 6080634.65 -8.80084E+00 1.07200E+01 1.47100E+00 9.32836E+01 1.73820E+01 1.99130E+01 2.53100E+00 1.00100E+00 + 2 1 12 585685.73 6080652.58 3.74242E+00 1.83200E+01 1.50000E+00 5.45852E+01 1.73820E+01 1.99130E+01 2.53100E+00 1.00100E+00 + 3 1 12 585528.09 6080886.55 2.06384E+00 1.38500E+01 1.50500E+00 7.22022E+01 1.73820E+01 1.99130E+01 2.53100E+00 1.00100E+00 + 4 1 12 585208.91 6081489.01 9.57063E+00 4.21700E+01 1.65900E+00 2.37135E+01 1.73820E+01 1.99130E+01 2.53100E+00 1.00100E+00 + 5 1 12 585505.78 6080913.69 8.49606E-01 1.38400E+01 1.49300E+00 7.22543E+01 1.73820E+01 1.99130E+01 2.53100E+00 1.00100E+00 + 1 1 13 585623.06 6080634.65 -1.14433E+01 8.65900E+00 1.41800E+00 1.15487E+02 1.99130E+01 2.26670E+01 2.75400E+00 1.00100E+00 + 2 1 13 585685.73 6080652.58 1.09992E+00 1.31600E+01 1.45700E+00 7.59878E+01 1.99130E+01 2.26670E+01 2.75400E+00 1.00100E+00 + 3 1 13 585528.09 6080886.55 -5.78658E-01 1.23100E+01 1.48400E+00 8.12348E+01 1.99130E+01 2.26670E+01 2.75400E+00 1.00100E+00 + 4 1 13 585208.91 6081489.01 6.92813E+00 3.20300E+01 1.58200E+00 3.12207E+01 1.99130E+01 2.26670E+01 2.75400E+00 1.00100E+00 + 5 1 13 585505.78 6080913.69 -1.79289E+00 1.16300E+01 1.47100E+00 8.59845E+01 1.99130E+01 2.26670E+01 2.75400E+00 1.00100E+00 + 1 1 14 585623.06 6080634.65 -1.43188E+01 8.53800E+00 1.41700E+00 1.17123E+02 2.26670E+01 2.56640E+01 2.99700E+00 1.00100E+00 + 2 1 14 585685.73 6080652.58 -1.77558E+00 1.13200E+01 1.44200E+00 8.83392E+01 2.26670E+01 2.56640E+01 2.99700E+00 1.00100E+00 + 3 1 14 585528.09 6080886.55 -3.45416E+00 1.17900E+01 1.45100E+00 8.48176E+01 2.26670E+01 2.56640E+01 2.99700E+00 1.00100E+00 + 4 1 14 585208.91 6081489.01 4.05263E+00 2.36700E+01 1.54200E+00 4.22476E+01 2.26670E+01 2.56640E+01 2.99700E+00 1.00100E+00 + 5 1 14 585505.78 6080913.69 -4.66839E+00 1.11200E+01 1.43800E+00 8.99281E+01 2.26670E+01 2.56640E+01 2.99700E+00 1.00100E+00 + 1 1 15 585623.06 6080634.65 -1.74478E+01 8.73400E+00 1.44200E+00 1.14495E+02 2.56640E+01 2.89250E+01 3.26100E+00 1.00100E+00 + 2 1 15 585685.73 6080652.58 -4.90458E+00 1.11500E+01 1.42600E+00 8.96861E+01 2.56640E+01 2.89250E+01 3.26100E+00 1.00100E+00 + 3 1 15 585528.09 6080886.55 -6.58316E+00 1.21300E+01 1.46700E+00 8.24402E+01 2.56640E+01 2.89250E+01 3.26100E+00 1.00100E+00 + 4 1 15 585208.91 6081489.01 9.23633E-01 2.10400E+01 1.50600E+00 4.75285E+01 2.56640E+01 2.89250E+01 3.26100E+00 1.00100E+00 + 5 1 15 585505.78 6080913.69 -7.79739E+00 1.18800E+01 1.45900E+00 8.41751E+01 2.56640E+01 2.89250E+01 3.26100E+00 1.00100E+00 + 1 1 16 585623.06 6080634.65 -2.08523E+01 8.52400E+00 1.44800E+00 1.17316E+02 2.89250E+01 3.24730E+01 3.54800E+00 1.00100E+00 + 2 1 16 585685.73 6080652.58 -8.30908E+00 1.13400E+01 1.44900E+00 8.81834E+01 2.89250E+01 3.24730E+01 3.54800E+00 1.00100E+00 + 3 1 16 585528.09 6080886.55 -9.98766E+00 1.29500E+01 1.49500E+00 7.72201E+01 2.89250E+01 3.24730E+01 3.54800E+00 1.00100E+00 + 4 1 16 585208.91 6081489.01 -2.48087E+00 2.39500E+01 1.50400E+00 4.17537E+01 2.89250E+01 3.24730E+01 3.54800E+00 1.00100E+00 + 5 1 16 585505.78 6080913.69 -1.12019E+01 1.31200E+01 1.49300E+00 7.62195E+01 2.89250E+01 3.24730E+01 3.54800E+00 1.00100E+00 + 1 1 17 585623.06 6080634.65 -2.45568E+01 8.36700E+00 1.46500E+00 1.19517E+02 3.24730E+01 3.63340E+01 3.86100E+00 1.00100E+00 + 2 1 17 585685.73 6080652.58 -1.20136E+01 1.11700E+01 1.46100E+00 8.95255E+01 3.24730E+01 3.63340E+01 3.86100E+00 1.00100E+00 + 3 1 17 585528.09 6080886.55 -1.36922E+01 1.37000E+01 1.51600E+00 7.29927E+01 3.24730E+01 3.63340E+01 3.86100E+00 1.00100E+00 + 4 1 17 585208.91 6081489.01 -6.18537E+00 2.84800E+01 1.54100E+00 3.51124E+01 3.24730E+01 3.63340E+01 3.86100E+00 1.00100E+00 + 5 1 17 585505.78 6080913.69 -1.49064E+01 1.40100E+01 1.51500E+00 7.13776E+01 3.24730E+01 3.63340E+01 3.86100E+00 1.00100E+00 + 1 1 18 585623.06 6080634.65 -2.85878E+01 8.04800E+00 1.48200E+00 1.24254E+02 3.63340E+01 4.05350E+01 4.20100E+00 1.00100E+00 + 2 1 18 585685.73 6080652.58 -1.60446E+01 1.05600E+01 1.46400E+00 9.46970E+01 3.63340E+01 4.05350E+01 4.20100E+00 1.00100E+00 + 3 1 18 585528.09 6080886.55 -1.77232E+01 1.38600E+01 1.53100E+00 7.21501E+01 3.63340E+01 4.05350E+01 4.20100E+00 1.00100E+00 + 4 1 18 585208.91 6081489.01 -1.02164E+01 3.03200E+01 1.57600E+00 3.29815E+01 3.63340E+01 4.05350E+01 4.20100E+00 1.00100E+00 + 5 1 18 585505.78 6080913.69 -1.89374E+01 1.42100E+01 1.53100E+00 7.03730E+01 3.63340E+01 4.05350E+01 4.20100E+00 1.00100E+00 + 1 1 19 585623.06 6080634.65 -3.29738E+01 7.23000E+00 1.48600E+00 1.38313E+02 4.05350E+01 4.51060E+01 4.57100E+00 1.00100E+00 + 2 1 19 585685.73 6080652.58 -2.04306E+01 1.00300E+01 1.47200E+00 9.97009E+01 4.05350E+01 4.51060E+01 4.57100E+00 1.00100E+00 + 3 1 19 585528.09 6080886.55 -2.21092E+01 1.33700E+01 1.53700E+00 7.47943E+01 4.05350E+01 4.51060E+01 4.57100E+00 1.00100E+00 + 4 1 19 585208.91 6081489.01 -1.46024E+01 2.83300E+01 1.57100E+00 3.52983E+01 4.05350E+01 4.51060E+01 4.57100E+00 1.00100E+00 + 5 1 19 585505.78 6080913.69 -2.33234E+01 1.38300E+01 1.54000E+00 7.23066E+01 4.05350E+01 4.51060E+01 4.57100E+00 1.00100E+00 + 1 1 20 585623.06 6080634.65 -3.77463E+01 6.06200E+00 1.47900E+00 1.64962E+02 4.51060E+01 5.00800E+01 4.97400E+00 1.00100E+00 + 2 1 20 585685.73 6080652.58 -2.52031E+01 1.00400E+01 1.49000E+00 9.96016E+01 4.51060E+01 5.00800E+01 4.97400E+00 1.00100E+00 + 3 1 20 585528.09 6080886.55 -2.68817E+01 1.28000E+01 1.54300E+00 7.81250E+01 4.51060E+01 5.00800E+01 4.97400E+00 1.00100E+00 + 4 1 20 585208.91 6081489.01 -1.93749E+01 2.32300E+01 1.53900E+00 4.30478E+01 4.51060E+01 5.00800E+01 4.97400E+00 1.00100E+00 + 5 1 20 585505.78 6080913.69 -2.80959E+01 1.31300E+01 1.54500E+00 7.61615E+01 4.51060E+01 5.00800E+01 4.97400E+00 1.00100E+00 + 1 1 21 585623.06 6080634.65 -4.29393E+01 4.84200E+00 1.46700E+00 2.06526E+02 5.00800E+01 5.54920E+01 5.41200E+00 1.00100E+00 + 2 1 21 585685.73 6080652.58 -3.03961E+01 1.02600E+01 1.51700E+00 9.74659E+01 5.00800E+01 5.54920E+01 5.41200E+00 1.00100E+00 + 3 1 21 585528.09 6080886.55 -3.20747E+01 1.24900E+01 1.55800E+00 8.00641E+01 5.00800E+01 5.54920E+01 5.41200E+00 1.00100E+00 + 4 1 21 585208.91 6081489.01 -2.45679E+01 1.72700E+01 1.50000E+00 5.79039E+01 5.00800E+01 5.54920E+01 5.41200E+00 1.00100E+00 + 5 1 21 585505.78 6080913.69 -3.32889E+01 1.21400E+01 1.55000E+00 8.23723E+01 5.00800E+01 5.54920E+01 5.41200E+00 1.00100E+00 + 1 1 22 585623.06 6080634.65 -4.85898E+01 3.78300E+00 1.46300E+00 2.64340E+02 5.54920E+01 6.13810E+01 5.88900E+00 1.00100E+00 + 2 1 22 585685.73 6080652.58 -3.60466E+01 9.72100E+00 1.53500E+00 1.02870E+02 5.54920E+01 6.13810E+01 5.88900E+00 1.00100E+00 + 3 1 22 585528.09 6080886.55 -3.77252E+01 1.18800E+01 1.57700E+00 8.41751E+01 5.54920E+01 6.13810E+01 5.88900E+00 1.00100E+00 + 4 1 22 585208.91 6081489.01 -3.02184E+01 1.24000E+01 1.46100E+00 8.06452E+01 5.54920E+01 6.13810E+01 5.88900E+00 1.00100E+00 + 5 1 22 585505.78 6080913.69 -3.89394E+01 1.05600E+01 1.55000E+00 9.46970E+01 5.54920E+01 6.13810E+01 5.88900E+00 1.00100E+00 + 1 1 23 585623.06 6080634.65 -5.47383E+01 3.02600E+00 1.49100E+00 3.30469E+02 6.13810E+01 6.77890E+01 6.40800E+00 1.00100E+00 + 2 1 23 585685.73 6080652.58 -4.21951E+01 8.04900E+00 1.54300E+00 1.24239E+02 6.13810E+01 6.77890E+01 6.40800E+00 1.00100E+00 + 3 1 23 585528.09 6080886.55 -4.38737E+01 1.02400E+01 1.59800E+00 9.76563E+01 6.13810E+01 6.77890E+01 6.40800E+00 1.00100E+00 + 4 1 23 585208.91 6081489.01 -3.63669E+01 9.17500E+00 1.43200E+00 1.08992E+02 6.13810E+01 6.77890E+01 6.40800E+00 1.00100E+00 + 5 1 23 585505.78 6080913.69 -4.50879E+01 8.27100E+00 1.54800E+00 1.20904E+02 6.13810E+01 6.77890E+01 6.40800E+00 1.00100E+00 + 1 1 24 585623.06 6080634.65 -6.14288E+01 2.57900E+00 1.54300E+00 3.87747E+02 6.77890E+01 7.47620E+01 6.97300E+00 1.00100E+00 + 2 1 24 585685.73 6080652.58 -4.88856E+01 5.85300E+00 1.56200E+00 1.70853E+02 6.77890E+01 7.47620E+01 6.97300E+00 1.00100E+00 + 3 1 24 585528.09 6080886.55 -5.05642E+01 7.75500E+00 1.62900E+00 1.28949E+02 6.77890E+01 7.47620E+01 6.97300E+00 1.00100E+00 + 4 1 24 585208.91 6081489.01 -4.30574E+01 7.08500E+00 1.42900E+00 1.41143E+02 6.77890E+01 7.47620E+01 6.97300E+00 1.00100E+00 + 5 1 24 585505.78 6080913.69 -5.17784E+01 5.78100E+00 1.56000E+00 1.72980E+02 6.77890E+01 7.47620E+01 6.97300E+00 1.00100E+00 + 1 1 25 585623.06 6080634.65 -6.87088E+01 2.35700E+00 1.56100E+00 4.24268E+02 7.47620E+01 8.23490E+01 7.58700E+00 1.00100E+00 + 2 1 25 585685.73 6080652.58 -5.61656E+01 3.97300E+00 1.58900E+00 2.51699E+02 7.47620E+01 8.23490E+01 7.58700E+00 1.00100E+00 + 3 1 25 585528.09 6080886.55 -5.78442E+01 5.28700E+00 1.66200E+00 1.89143E+02 7.47620E+01 8.23490E+01 7.58700E+00 1.00100E+00 + 4 1 25 585208.91 6081489.01 -5.03374E+01 5.54400E+00 1.45300E+00 1.80375E+02 7.47620E+01 8.23490E+01 7.58700E+00 1.00100E+00 + 5 1 25 585505.78 6080913.69 -5.90584E+01 3.76800E+00 1.59000E+00 2.65393E+02 7.47620E+01 8.23490E+01 7.58700E+00 1.00100E+00 + 1 1 26 585623.06 6080634.65 -7.66298E+01 2.22400E+00 1.53500E+00 4.49640E+02 8.23490E+01 9.06040E+01 8.25500E+00 1.00100E+00 + 2 1 26 585685.73 6080652.58 -6.40866E+01 2.81200E+00 1.56300E+00 3.55619E+02 8.23490E+01 9.06040E+01 8.25500E+00 1.00100E+00 + 3 1 26 585528.09 6080886.55 -6.57652E+01 3.51300E+00 1.64600E+00 2.84657E+02 8.23490E+01 9.06040E+01 8.25500E+00 1.00100E+00 + 4 1 26 585208.91 6081489.01 -5.82584E+01 4.22300E+00 1.46600E+00 2.36798E+02 8.23490E+01 9.06040E+01 8.25500E+00 1.00100E+00 + 5 1 26 585505.78 6080913.69 -6.69794E+01 2.54300E+00 1.56800E+00 3.93236E+02 8.23490E+01 9.06040E+01 8.25500E+00 1.00100E+00 + 1 1 27 585623.06 6080634.65 -8.52488E+01 2.18000E+00 1.54700E+00 4.58716E+02 9.06040E+01 9.95870E+01 8.98300E+00 1.00100E+00 + 2 1 27 585685.73 6080652.58 -7.27056E+01 2.34200E+00 1.47000E+00 4.26985E+02 9.06040E+01 9.95870E+01 8.98300E+00 1.00100E+00 + 3 1 27 585528.09 6080886.55 -7.43842E+01 2.57500E+00 1.54400E+00 3.88350E+02 9.06040E+01 9.95870E+01 8.98300E+00 1.00100E+00 + 4 1 27 585208.91 6081489.01 -6.68774E+01 3.44000E+00 1.41500E+00 2.90698E+02 9.06040E+01 9.95870E+01 8.98300E+00 1.00100E+00 + 5 1 27 585505.78 6080913.69 -7.55984E+01 1.99400E+00 1.45600E+00 5.01505E+02 9.06040E+01 9.95870E+01 8.98300E+00 1.00100E+00 + 1 1 28 585623.06 6080634.65 -9.46273E+01 2.36100E+00 1.75400E+00 4.23549E+02 9.95870E+01 1.09361E+02 9.77400E+00 1.00100E+00 + 2 1 28 585685.73 6080652.58 -8.20841E+01 2.40600E+00 1.46500E+00 4.15628E+02 9.95870E+01 1.09361E+02 9.77400E+00 1.00100E+00 + 3 1 28 585528.09 6080886.55 -8.37627E+01 2.37000E+00 1.50300E+00 4.21941E+02 9.95870E+01 1.09361E+02 9.77400E+00 1.00100E+00 + 4 1 28 585208.91 6081489.01 -7.62559E+01 3.48700E+00 1.38400E+00 2.86779E+02 9.95870E+01 1.09361E+02 9.77400E+00 1.00100E+00 + 5 1 28 585505.78 6080913.69 -8.49769E+01 2.03100E+00 1.43600E+00 4.92368E+02 9.95870E+01 1.09361E+02 9.77400E+00 1.00100E+00 + 1 1 29 585623.06 6080634.65 -1.04834E+02 2.94000E+00 2.27000E+00 3.40136E+02 1.09361E+02 1.20001E+02 1.06400E+01 1.00100E+00 + 2 1 29 585685.73 6080652.58 -9.22911E+01 2.92400E+00 1.72900E+00 3.41997E+02 1.09361E+02 1.20001E+02 1.06400E+01 1.00100E+00 + 3 1 29 585528.09 6080886.55 -9.39697E+01 2.80400E+00 1.74000E+00 3.56633E+02 1.09361E+02 1.20001E+02 1.06400E+01 1.00100E+00 + 4 1 29 585208.91 6081489.01 -8.64629E+01 4.21600E+00 1.52300E+00 2.37192E+02 1.09361E+02 1.20001E+02 1.06400E+01 1.00100E+00 + 5 1 29 585505.78 6080913.69 -9.51839E+01 2.69500E+00 1.68400E+00 3.71058E+02 1.09361E+02 1.20001E+02 1.06400E+01 1.00100E+00 + 1 1 30 585623.06 6080634.65 -1.40155E+02 4.17300E+00 3.06000E+00 2.39636E+02 1.20001E+02 1.80001E+02 6.00005E+01 9999 + 2 1 30 585685.73 6080652.58 -1.27611E+02 3.74900E+00 2.32000E+00 2.66738E+02 1.20001E+02 1.80001E+02 6.00005E+01 9999 + 3 1 30 585528.09 6080886.55 -1.29290E+02 3.58200E+00 2.30000E+00 2.79174E+02 1.20001E+02 1.80001E+02 6.00005E+01 9999 + 4 1 30 585208.91 6081489.01 -1.21783E+02 5.02200E+00 1.86300E+00 1.99124E+02 1.20001E+02 1.80001E+02 6.00005E+01 9999 + 5 1 30 585505.78 6080913.69 -1.30504E+02 4.23900E+00 2.22000E+00 2.35905E+02 1.20001E+02 1.80001E+02 6.00005E+01 9999 diff --git a/test/test_core.py b/test/test_core.py @@ -110,6 +110,25 @@ class ProcessXYZTests(unittest.TestCase): self.assertEqual(layers[0]["DepthTop"], 0.0) self.assertEqual(layers[0]["DepthBottom"], 2.0) + def test_sci_workbench_2026_1_fixture(self): + path = FIXTURE_DIR / "sci_workbench_2026_1.xyz" + points, doi_points, layers = process_xyz(path) + + self.assertEqual(len(points), 5) + self.assertEqual(len(doi_points), 0) + self.assertEqual(len(layers), 5 * 30) + self.assertEqual(points[0]["Line"], "1") + self.assertEqual(points[0]["StationNo"], "1_00001") + self.assertEqual(points[0]["NumLayers"], 30) + self.assertAlmostEqual(points[0]["Z"], 9.84666, places=4) + self.assertEqual(layers[0]["DepthTop"], 0.0) + self.assertEqual(layers[0]["DepthBottom"], 1.0) + self.assertAlmostEqual(layers[0]["Resistivity"], 31.1, places=6) + + def test_detect_source_epsg_for_sci_fixture(self): + path = FIXTURE_DIR / "sci_workbench_2026_1.xyz" + self.assertEqual(detect_source_epsg(path), "EPSG:25832") + def test_detect_source_epsg_for_aarhus_workbench_fixture(self): path = FIXTURE_DIR / "stem_workbench_2026_1.xyz" self.assertEqual(detect_source_epsg(path), "EPSG:32637") @@ -167,9 +186,28 @@ class ProcessXYZTests(unittest.TestCase): ): process_xyz(path) + def test_process_xyz_rejects_mismatched_sci_row_length(self): + with TemporaryDirectory() as tmp: + path = Path(tmp) / "broken_sci.xyz" + path.write_text( + "/epsg:25832\n" + "/ ID Line_No Layer_No X Y Elevation_Cell Resistivity " + "Resistivity_STD Conductivity Depth_top Depth_bottom " + "Thickness Thickness_STD\n" + "1 1 1 500000 6000000 10 20 1 50 0 1 1 1\n" + "2 1 2 500000 6000000\n" + ) + + with self.assertRaisesRegex( + ValueError, r"Row 4 has 5 columns, expected 13" + ): + process_xyz(path) + def test_fixture_doi_values_fit_fixed_scale(self): for path in sorted(FIXTURE_DIR.glob("*.xyz")): _, doi_points, _ = process_xyz(path) + if not doi_points: + continue values = [row["DOI"] for row in doi_points] self.assertGreaterEqual(min(values), 0.0, path.name)