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 8c22a5490b8306a038164278800dcfbd731195d0
parent f145156c365a6cb10397150ba2859752e75374d4
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Fri, 15 May 2026 22:49:21 +0200

feat(parser): apply DOI masking options to layers

Diffstat:
Mtem_loader/core.py | 48++++++++++++++++++++++++++++++++++++++++--------
Mtest/test_core.py | 27+++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 8 deletions(-)

diff --git a/tem_loader/core.py b/tem_loader/core.py @@ -59,10 +59,16 @@ def validate_opacity(value): return opacity -def layer_opacity(depth_mid, doi): - if doi is None: +def layer_opacity( + depth_mid, + doi, + mask_below_doi=True, + below_doi_opacity=BELOW_DOI_OPACITY, +): + below_doi_opacity = validate_opacity(below_doi_opacity) + if doi is None or not mask_below_doi: return ABOVE_DOI_OPACITY - return ABOVE_DOI_OPACITY if depth_mid <= doi else BELOW_DOI_OPACITY + return ABOVE_DOI_OPACITY if depth_mid <= doi else below_doi_opacity # Resistivity ranges mirror the graduated symbology in styles/layers.qml. # Each entry is (upper_exclusive, '#RRGGBB'); the final entry is the catch-all. @@ -187,7 +193,7 @@ def count_valid_layers(row, res_cols, thick_cols): def process_xyz(path, mask_below_doi=True, below_doi_opacity=BELOW_DOI_OPACITY): - validate_opacity(below_doi_opacity) + below_doi_opacity = validate_opacity(below_doi_opacity) points = [] doi_points = [] @@ -239,7 +245,14 @@ def process_xyz(path, mask_below_doi=True, below_doi_opacity=BELOW_DOI_OPACITY): row = dict(zip(headers, values)) if source_format == 'atem_sci': _append_atem_sci_row( - row, res_cols, elev_cols, points, doi_points, layers + row, + res_cols, + elev_cols, + points, + doi_points, + layers, + mask_below_doi, + below_doi_opacity, ) continue @@ -336,7 +349,12 @@ def process_xyz(path, mask_below_doi=True, below_doi_opacity=BELOW_DOI_OPACITY): 'DepthBottom': depth_bottom, 'Resistivity': res, 'Color': resistivity_color(res), - 'Opacity': layer_opacity(depth_mid, doi), + 'Opacity': layer_opacity( + depth_mid, + doi, + mask_below_doi, + below_doi_opacity, + ), 'Layer': i + 1, 'Geometry': layer_wkt, }) @@ -347,7 +365,16 @@ def process_xyz(path, mask_below_doi=True, below_doi_opacity=BELOW_DOI_OPACITY): return points, doi_points, layers -def _append_atem_sci_row(row, res_cols, elev_cols, points, doi_points, layers): +def _append_atem_sci_row( + row, + res_cols, + elev_cols, + points, + doi_points, + layers, + mask_below_doi=True, + below_doi_opacity=BELOW_DOI_OPACITY, +): x = float(row['E']) y = float(row['N']) z = float(row['DEM']) @@ -391,7 +418,12 @@ def _append_atem_sci_row(row, res_cols, elev_cols, points, doi_points, layers): 'DepthBottom': depth_bottom, 'Resistivity': res, 'Color': resistivity_color(res), - 'Opacity': layer_opacity(depth_mid, doi), + 'Opacity': layer_opacity( + depth_mid, + doi, + mask_below_doi, + below_doi_opacity, + ), 'Layer': i + 1, 'Geometry': f'LINESTRING Z ({x} {y} {z_top}, {x} {y} {z_bot})', }) diff --git a/test/test_core.py b/test/test_core.py @@ -195,6 +195,33 @@ class ProcessXYZTests(unittest.TestCase): with self.assertRaisesRegex(ValueError, "opacity must be between 0 and 100"): process_xyz(path, below_doi_opacity=101) + def test_process_xyz_can_disable_below_doi_mask(self): + path = FIXTURE_DIR / "profiler_temimager_4_0_4_6.xyz" + _, _, layers = process_xyz(path, mask_below_doi=False) + + self.assertTrue(all(row["Opacity"] == ABOVE_DOI_OPACITY for row in layers)) + + def test_atem_sci_process_xyz_uses_custom_below_doi_opacity(self): + path = FIXTURE_DIR / "atem_sci_workbench.xyz" + _, _, layers = process_xyz(path, below_doi_opacity=25) + opacities = {row["Opacity"] for row in layers} + + self.assertIn(ABOVE_DOI_OPACITY, opacities) + self.assertIn(25, opacities) + self.assertNotIn(BELOW_DOI_OPACITY, opacities) + + def test_layer_opacity_can_disable_below_doi_mask(self): + self.assertEqual( + layer_opacity(200.0, 50.0, mask_below_doi=False), + ABOVE_DOI_OPACITY, + ) + + def test_layer_opacity_uses_custom_below_doi_opacity(self): + self.assertEqual( + layer_opacity(200.0, 50.0, below_doi_opacity=25), + 25, + ) + def test_layer_opacity_returns_above_when_doi_is_none(self): self.assertEqual(layer_opacity(50.0, None), ABOVE_DOI_OPACITY) self.assertEqual(layer_opacity(0.0, None), ABOVE_DOI_OPACITY)