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 4360e88195d3a7ba001b866900d7fe367980a3d8
parent 2bb698a6e440dbd9a3091a573dc735b0ae903f30
Author: Anders Damsgaard <anders@adamsgaard.dk>
Date:   Fri, 15 May 2026 23:35:19 +0200

feat(qgis): add DEM row adjustment helper

Diffstat:
Mtem_loader/tem_loader.py | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/test_core.py | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 115 insertions(+), 0 deletions(-)

diff --git a/tem_loader/tem_loader.py b/tem_loader/tem_loader.py @@ -130,6 +130,57 @@ def _sample_dem_elevation(raster_layer, source_crs, project, x, y): return elevation +def _point_wkt(x, y, z): + return f'POINT Z ({x} {y} {z})' + + +def _line_wkt(x, y, z_top, z_bottom): + return f'LINESTRING Z ({x} {y} {z_top}, {x} {y} {z_bottom})' + + +def _adjust_rows_to_dem(points, doi_points, layers, raster_layer, source_crs, + project): + if raster_layer is None: + return + + dem_z_by_xy = {} + for point in points: + x = point['X'] + y = point['Y'] + dem_z = _sample_dem_elevation(raster_layer, source_crs, project, x, y) + if dem_z is None: + continue + dem_z_by_xy[(x, y)] = dem_z + point['Z'] = dem_z + point['Geometry'] = _point_wkt(x, y, dem_z) + + for doi_point in doi_points: + x = doi_point['X'] + y = doi_point['Y'] + dem_z = dem_z_by_xy.get((x, y)) + if dem_z is None: + continue + z_doi = dem_z - doi_point['DOI'] + doi_point['Z'] = z_doi + doi_point['ZDOI'] = z_doi + doi_point['Geometry'] = _point_wkt(x, y, z_doi) + + for layer in layers: + x = layer['X'] + y = layer['Y'] + dem_z = dem_z_by_xy.get((x, y)) + if dem_z is None: + continue + z_top = dem_z - layer['DepthTop'] + z_bottom = dem_z - layer['DepthBottom'] + z_mid = (z_top + z_bottom) / 2 + layer['Z'] = dem_z + layer['ZTop'] = z_top + layer['ZMid'] = z_mid + layer['ZBottom'] = z_bottom + layer['Geometry'] = _line_wkt(x, y, z_top, z_bottom) + + def _write_geopackage_layer(rows, gpkg_path, layer_name, wkb_type, crs, transform_context, action): fields = _build_fields(rows) diff --git a/test/test_core.py b/test/test_core.py @@ -1146,6 +1146,70 @@ class PluginTests(unittest.TestCase): "to DEM raster DEM: bad transform" ) + def test_adjust_rows_to_dem_updates_vertical_fields_and_geometry(self): + module, _, _ = self._import_plugin_module() + provider = Mock() + provider.sample.return_value = "123.0", True + raster_layer = Mock() + raster_layer.crs.return_value = module.QgsCoordinateReferenceSystem( + "EPSG:3857" + ) + raster_layer.dataProvider.return_value = provider + points = [ + { + "X": 1.0, + "Y": 2.0, + "Z": 100.0, + "Geometry": "POINT Z (1.0 2.0 100.0)", + } + ] + doi_points = [ + { + "X": 1.0, + "Y": 2.0, + "Z": 90.0, + "DOI": 10.0, + "ZDOI": 90.0, + "Geometry": "POINT Z (1.0 2.0 90.0)", + } + ] + layers = [ + { + "X": 1.0, + "Y": 2.0, + "Z": 100.0, + "DepthTop": 0.0, + "DepthBottom": 5.0, + "ZTop": 100.0, + "ZMid": 97.5, + "ZBottom": 95.0, + "Geometry": "LINESTRING Z (1.0 2.0 100.0, 1.0 2.0 95.0)", + } + ] + + module._adjust_rows_to_dem( + points, + doi_points, + layers, + raster_layer, + module.QgsCoordinateReferenceSystem("EPSG:25832"), + module.QgsProject.instance(), + ) + + self.assertEqual(points[0]["Z"], 123.0) + self.assertEqual(points[0]["Geometry"], "POINT Z (1.0 2.0 123.0)") + self.assertEqual(doi_points[0]["Z"], 113.0) + self.assertEqual(doi_points[0]["ZDOI"], 113.0) + self.assertEqual(doi_points[0]["Geometry"], "POINT Z (1.0 2.0 113.0)") + self.assertEqual(layers[0]["Z"], 123.0) + self.assertEqual(layers[0]["ZTop"], 123.0) + self.assertEqual(layers[0]["ZMid"], 120.5) + self.assertEqual(layers[0]["ZBottom"], 118.0) + self.assertEqual( + layers[0]["Geometry"], + "LINESTRING Z (1.0 2.0 123.0, 1.0 2.0 118.0)", + ) + def test_exec_dialog_supports_exec_and_exec_apis(self): module, _, _ = self._import_plugin_module() exec_dialog = Mock()