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

tem_loader.py (3840B)


      1 from pathlib import Path
      2 
      3 from qgis.PyQt.QtWidgets import QAction, QFileDialog, QMessageBox
      4 from qgis.core import (
      5     QgsProject,
      6     QgsVectorLayer,
      7     QgsCoordinateReferenceSystem,
      8 )
      9 
     10 from . import core
     11 
     12 
     13 STYLES_DIR = Path(__file__).parent / 'styles'
     14 
     15 
     16 def _build_delimited_text_uri(csv_path, geom_type, crs_str):
     17     base_uri = csv_path.resolve().as_uri()
     18     return (
     19         f'{base_uri}?type={geom_type}'
     20         f'&crs={crs_str}'
     21         f'&wktField=Geometry'
     22         f'&delimiter=,'
     23         f'&xField=X&yField=Y'
     24     )
     25 
     26 
     27 class TEMLoaderPlugin:
     28     def __init__(self, iface):
     29         self.iface = iface
     30         self._action = None
     31 
     32     def initGui(self):
     33         self._action = QAction('Load TEM XYZ files\u2026', self.iface.mainWindow())
     34         self._action.triggered.connect(self.run)
     35         self.iface.addPluginToMenu('TEM Loader', self._action)
     36 
     37     def unload(self):
     38         self.iface.removePluginMenu('TEM Loader', self._action)
     39         self._action = None
     40 
     41     def run(self):
     42         paths, _ = QFileDialog.getOpenFileNames(
     43             self.iface.mainWindow(),
     44             'Select TEM XYZ files',
     45             '',
     46             'XYZ files (*.xyz);;All files (*)',
     47         )
     48         failed = []
     49         for path in paths:
     50             filepath = Path(path)
     51             try:
     52                 self._load_xyz(filepath)
     53             except Exception as exc:
     54                 failed.append(f'{filepath.name}: {exc}')
     55         if failed:
     56             QMessageBox.warning(
     57                 self.iface.mainWindow(),
     58                 'TEM Loader',
     59                 '\n'.join(failed),
     60             )
     61 
     62     def _load_xyz(self, filepath):
     63         points, doi_points, layers = core.process_xyz(filepath)
     64         pts_csv = core.write_csv(points, filepath, '.points.csv')
     65         doi_csv = core.write_csv(doi_points, filepath, '.doi.csv')
     66         lyr_csv = core.write_csv(layers, filepath, '.layers.csv')
     67 
     68         project = QgsProject.instance()
     69         crs = None
     70         source_epsg = core.detect_source_epsg(filepath)
     71         if source_epsg:
     72             candidate = QgsCoordinateReferenceSystem()
     73             if candidate.createFromString(source_epsg) and candidate.isValid():
     74                 crs = candidate
     75 
     76         if crs is None:
     77             crs = project.crs()
     78         if not crs.isValid():
     79             crs = QgsCoordinateReferenceSystem()
     80             crs.createFromString('EPSG:4326')
     81         crs_str = crs.authid()
     82 
     83         loaded_layers = {}
     84         source_layers = [
     85             ('layers', lyr_csv, 'LineString'),
     86             ('doi', doi_csv, 'Point'),
     87             ('points', pts_csv, 'Point'),
     88         ]
     89         for name, csv_path, geom_type in source_layers:
     90             uri = _build_delimited_text_uri(csv_path, geom_type, crs_str)
     91             layer = QgsVectorLayer(uri, name, 'delimitedtext')
     92             if not layer.isValid():
     93                 continue
     94 
     95             qml = STYLES_DIR / f'{name}.qml'
     96             if qml.exists():
     97                 layer.loadNamedStyle(str(qml))
     98 
     99             project.addMapLayer(layer, False)
    100             loaded_layers[name] = layer
    101 
    102         if not loaded_layers:
    103             raise ValueError('failed to load any layers')
    104 
    105         group_name = filepath.stem
    106         root = project.layerTreeRoot()
    107         group = root.insertGroup(0, group_name)
    108         insert_index = 0
    109         for name in ('points', 'doi', 'layers'):
    110             layer = loaded_layers.get(name)
    111             if layer is None:
    112                 continue
    113             group.insertLayer(insert_index, layer)
    114             insert_index += 1
    115 
    116         failed = [name for name, _, _ in source_layers if name not in loaded_layers]
    117         if failed:
    118             QMessageBox.warning(
    119                 self.iface.mainWindow(),
    120                 'TEM Loader',
    121                 f'{filepath.name}: failed to load layers: {", ".join(failed)}',
    122             )