├── pzero ├── __init__.py ├── ui │ ├── __init__.py │ ├── assign_ui.py │ ├── README.md │ └── navigator_window_ui.py ├── views │ ├── __init__.py │ └── README.md ├── helpers │ ├── __init__.py │ ├── helper_signals.py │ └── README.md ├── imports │ ├── __init__.py │ ├── AbstractImporter.py │ ├── cesium2vtk.py │ ├── gltf2vtk.py │ ├── ply2vtk.py │ ├── obj2vtk.py │ ├── dem2vtk.py │ ├── stl2vtk.py │ └── README.md ├── collections │ ├── __init__.py │ ├── fluid_collection.py │ ├── background_collection.py │ ├── geological_collection.py │ ├── README.md │ ├── mesh3d_collection.py │ ├── image_collection.py │ └── dom_collection.py ├── processing │ └── README.md └── README.md ├── style ├── __init__.py └── imgs │ ├── __init__.py │ ├── uparrow2.svg │ ├── downarrow2.svg │ ├── leftarrow2.svg │ └── rightarrow2.svg ├── tests └── __init__.py ├── old_tests ├── __init__.py ├── test_collections │ ├── __init__.py │ ├── test_fluid_collection.py │ ├── test_background_collection.py │ ├── test_mesh3d_collection.py │ ├── test_image_collection.py │ ├── test_geological_collection.py │ ├── test_xsection_collection.py │ ├── test_well_collection.py │ └── test_dom_collection.py └── test_helpers │ ├── __init__.py │ └── test_helper_dialogs.py ├── gui ├── _use Designer - Form - View Python Code ├── designer.exe - Shortcut.lnk ├── preview_window.ui └── navigator_window.ui ├── pytest.ini ├── icons ├── dip.png ├── dip_sym.png ├── dip_trasp.png ├── SelectEntity.svg ├── HomeView.svg ├── MergeLines.svg ├── SplitLineLine.svg ├── DrawLine.svg ├── SplitLinePoint.svg ├── RemoveEntity.svg ├── MoveLine.svg ├── ExtendLine.svg ├── BoundaryFrom2Points.svg ├── ExportAsGLTF.svg ├── ExportAsHTML.svg ├── ExportAsOBJ.svg ├── SortLineNodes.svg ├── EditLine.svg ├── ExportAsVTKjs.svg ├── SaveHomeView.svg ├── ClearSelection.svg ├── ZoomToActive.svg ├── dip.svg ├── RotateLine.svg ├── SnapLine.svg ├── ResampleDistance.svg ├── TakeScreenshot.svg ├── ResampleNumber.svg ├── SimplifyLine.svg ├── CopyKink.svg ├── SectionFromAzimuth.svg └── CleanIntersections.svg ├── images ├── pzero.ico ├── 3d_view.jpg ├── mosaic.jpg ├── PZero_GUI.pptx ├── map_view.jpg ├── pzero_QR.png ├── screenshot.png ├── ArgandStereo.jpg ├── pzero_splash.jpg ├── splash_image.jpg ├── xsection_view.jpg └── pzero-architecture.jpg ├── envs ├── pzero-env.yml ├── pzero-env-from-history.yml └── notes_on_pzero_environment_2025-03-12.txt ├── _internal ├── pzero.ico └── pzero_splash.png ├── docs ├── _static │ └── .gitignore ├── _templates │ └── .gitignore ├── index.rst ├── Makefile ├── make.bat ├── pzero.helpers.rst ├── pzero.ui.rst ├── pzero.rst ├── conf.py ├── pzero.imports.rst └── pzero.collections.rst ├── .ipynb_checkpoints └── Untitled-checkpoint.ipynb ├── .gitignore ├── helper_scripts └── vtk_search.py ├── requirements.in ├── .github ├── workflows │ └── prepare_pr.yml └── old_workflow │ ├── class_refactoring_pyside6-testing.yml │ └── release.yml └── pzero.py /pzero/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /style/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /old_tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pzero/ui/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pzero/views/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pzero/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pzero/imports/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /style/imgs/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pzero/collections/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /old_tests/test_collections/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /old_tests/test_helpers/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/_use Designer - Form - View Python Code: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --ignore=old_tests/ -------------------------------------------------------------------------------- /icons/dip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/icons/dip.png -------------------------------------------------------------------------------- /images/pzero.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/pzero.ico -------------------------------------------------------------------------------- /envs/pzero-env.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/envs/pzero-env.yml -------------------------------------------------------------------------------- /icons/dip_sym.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/icons/dip_sym.png -------------------------------------------------------------------------------- /images/3d_view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/3d_view.jpg -------------------------------------------------------------------------------- /images/mosaic.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/mosaic.jpg -------------------------------------------------------------------------------- /_internal/pzero.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/_internal/pzero.ico -------------------------------------------------------------------------------- /icons/dip_trasp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/icons/dip_trasp.png -------------------------------------------------------------------------------- /images/PZero_GUI.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/PZero_GUI.pptx -------------------------------------------------------------------------------- /images/map_view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/map_view.jpg -------------------------------------------------------------------------------- /images/pzero_QR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/pzero_QR.png -------------------------------------------------------------------------------- /images/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/screenshot.png -------------------------------------------------------------------------------- /images/ArgandStereo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/ArgandStereo.jpg -------------------------------------------------------------------------------- /images/pzero_splash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/pzero_splash.jpg -------------------------------------------------------------------------------- /images/splash_image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/splash_image.jpg -------------------------------------------------------------------------------- /_internal/pzero_splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/_internal/pzero_splash.png -------------------------------------------------------------------------------- /images/xsection_view.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/xsection_view.jpg -------------------------------------------------------------------------------- /docs/_static/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /docs/_templates/.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything in this directory 2 | * 3 | # Except this file 4 | !.gitignore 5 | -------------------------------------------------------------------------------- /gui/designer.exe - Shortcut.lnk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/gui/designer.exe - Shortcut.lnk -------------------------------------------------------------------------------- /images/pzero-architecture.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gecos-lab/PZero/HEAD/images/pzero-architecture.jpg -------------------------------------------------------------------------------- /.ipynb_checkpoints/Untitled-checkpoint.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [], 3 | "metadata": {}, 4 | "nbformat": 4, 5 | "nbformat_minor": 5 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /.venv 3 | /pzero/__pycache__ 4 | /tests/__pycache__ 5 | .DS_Store 6 | *.DS_Store 7 | /installers 8 | *.pyc 9 | /docs/_build 10 | /dist 11 | /build -------------------------------------------------------------------------------- /pzero/helpers/helper_signals.py: -------------------------------------------------------------------------------- 1 | """helper_signals.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | 5 | def disconnect_all_signals(signals): 6 | """This function disconnects all signals of a QObject""" 7 | for signal in signals: 8 | # for each signal inside the list, disconnect it 9 | signal.disconnect() 10 | return 11 | -------------------------------------------------------------------------------- /helper_scripts/vtk_search.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from vtkmodules.all import * 3 | import sys 4 | 5 | 6 | lib_dict = globals() 7 | 8 | input_name = sys.argv[1] 9 | 10 | if input_name in lib_dict: 11 | module = lib_dict[input_name] 12 | print(f"from {module.__module__} import {module.__name__}") 13 | else: 14 | print("No module found") 15 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | loopstructural 2 | black[d] 3 | cmocean 4 | colorcet 5 | ezdxf 6 | geopandas 7 | laspy 8 | lxml 9 | mplstereonet 10 | openpyxl 11 | pandas 12 | pyinstaller 13 | pyside6 14 | pytest 15 | pyvista 16 | pyvistaqt 17 | qtpy 18 | rasterio 19 | rioxarray 20 | seaborn 21 | segyio 22 | shapely 23 | sphinx 24 | sphinx_rtd_theme 25 | vtk>=9.3, <9.4 26 | xarray -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. PZero documentation master file, created by 2 | sphinx-quickstart on Tue May 2 15:29:51 2023. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to PZero's documentation! 7 | ================================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | modules 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | -------------------------------------------------------------------------------- /pzero/views/README.md: -------------------------------------------------------------------------------- 1 | # PZero Views Module 2 | 3 | This directory contains the view classes for the PZero application. 4 | 5 | ## Class Hierarchy 6 | 7 | - `BaseView` (abstract_base_view.py): The base class for all views 8 | - `ViewMPL` (abstract_mpl_view.py): Base class for Matplotlib-based views 9 | - `ViewStereoplot` (view_stereoplot.py): Stereoplot view using Matplotlib 10 | - `ViewVTK` (abstract_vtk_view.py): Base class for VTK/PyVista-based views 11 | - `View3D` (view_3d.py): 3D view using VTK/PyVista 12 | - `View2D` (abstract_view_2d.py): Base class for 2D views using VTK/PyVista 13 | - `ViewXsection` (view_xsection.py): Cross-section view 14 | - `ViewMap` (view_map.py): Map view 15 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line, and also 5 | # from the environment for the first two. 6 | SPHINXOPTS ?= 7 | SPHINXBUILD ?= sphinx-build 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 21 | -------------------------------------------------------------------------------- /pzero/collections/fluid_collection.py: -------------------------------------------------------------------------------- 1 | """fluid_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .GFB_collection import GFBCollection 5 | 6 | 7 | class FluidCollection(GFBCollection): 8 | """Collection for all fluid entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(FluidCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.valid_roles = [ 14 | "undef", 15 | "top", 16 | "base", 17 | "seal", 18 | ] 19 | 20 | self.collection_name = "fluid_coll" 21 | 22 | self.default_sequence = "fluid_0" 23 | 24 | self.initialize_df() 25 | -------------------------------------------------------------------------------- /pzero/collections/background_collection.py: -------------------------------------------------------------------------------- 1 | """background_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .GFB_collection import GFBCollection 5 | 6 | 7 | class BackgroundCollection(GFBCollection): 8 | """Collection for all background entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(BackgroundCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.valid_roles = [ 14 | "undef", 15 | "annotations", 16 | "imported", 17 | ] 18 | 19 | self.collection_name = "backgrnd_coll" 20 | 21 | self.default_sequence = "back_0" 22 | 23 | self.initialize_df() 24 | -------------------------------------------------------------------------------- /pzero/imports/AbstractImporter.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod, abstractclassmethod, abstractstaticmethod 2 | 3 | 4 | class BaseIO(ABC): 5 | 6 | def __init__(self, input_file): 7 | self._input_file: str = input_file 8 | self._output_file: str = "" 9 | self._curr_obj = None 10 | 11 | @property 12 | def input_file(self) -> str: 13 | return self._input_file 14 | 15 | @property 16 | def output_file(self) -> str: 17 | return self._output_file 18 | 19 | @output_file.setter 20 | def output_file(self, output_file: str): 21 | self._output_file = output_file 22 | 23 | @abstractmethod 24 | def import_from_file(self): 25 | pass 26 | 27 | @abstractmethod 28 | def output_to_file(self): 29 | pass 30 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | %SPHINXBUILD% >NUL 2>NUL 14 | if errorlevel 9009 ( 15 | echo. 16 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 17 | echo.installed, then set the SPHINXBUILD environment variable to point 18 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 19 | echo.may add the Sphinx directory to PATH. 20 | echo. 21 | echo.If you don't have Sphinx installed, grab it from 22 | echo.https://www.sphinx-doc.org/ 23 | exit /b 1 24 | ) 25 | 26 | if "%1" == "" goto help 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /pzero/processing/README.md: -------------------------------------------------------------------------------- 1 | # Processing 2 | 3 | This folder contains modules for data processing and standardization tasks within the PZero project, including SEG-Y seismic data standardization and coordinate reference system (CRS) utilities. 4 | 5 | ## File Overview 6 | 7 | - `segy_standardizer.py` 8 | Utility functions for standardizing SEG-Y files to ensure PZero compatibility. 9 | **Main functions:** 10 | - `analyze_segy_parameters(input_file)`: Extracts standard SEG-Y parameters (samples, interval, format, etc.). 11 | - `standardize_segy_for_pzero(input_file, output_file, print_fn=print)`: Converts a SEG-Y file to a standardized format for PZero, updating headers and trace data as needed. 12 | - `convert_to_standard_segy(input_file, output_file, print_fn=print)`: Main entry point for SEG-Y conversion. 13 | 14 | - `CRS.py` 15 | Utilities for handling coordinate reference systems (CRS) and spatial transformations, based on PROJ. 16 | **Typical contents:** 17 | - Functions and/or classes for parsing, converting, and applying CRS definitions to spatial data. 18 | -------------------------------------------------------------------------------- /docs/pzero.helpers.rst: -------------------------------------------------------------------------------- 1 | pzero.helpers package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pzero.helpers.helper\_dialogs module 8 | ------------------------------------ 9 | 10 | .. automodule:: pzero.helpers.helper_dialogs 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pzero.helpers.helper\_functions module 16 | -------------------------------------- 17 | 18 | .. automodule:: pzero.helpers.helper_functions 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pzero.helpers.helper\_signals module 24 | ------------------------------------ 25 | 26 | .. automodule:: pzero.helpers.helper_signals 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pzero.helpers.helper\_widgets module 32 | ------------------------------------ 33 | 34 | .. automodule:: pzero.helpers.helper_widgets 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | Module contents 40 | --------------- 41 | 42 | .. automodule:: pzero.helpers 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | -------------------------------------------------------------------------------- /pzero/collections/geological_collection.py: -------------------------------------------------------------------------------- 1 | """geological_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .GFB_collection import GFBCollection 5 | 6 | 7 | class GeologicalCollection(GFBCollection): 8 | """Collection for all geological entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(GeologicalCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.valid_roles = [ 14 | "undef", 15 | "fault", 16 | "tectonic", 17 | "intrusive", 18 | "unconformity", 19 | "top", 20 | "base", 21 | "bedding", 22 | "foliation", 23 | "lineation", 24 | "axial_surface", 25 | "fold_axis", 26 | "TM_unit", 27 | "TS_unit", 28 | "INT_unit", 29 | "formation", 30 | ] 31 | 32 | self.collection_name = "geol_coll" 33 | 34 | self.default_sequence = "strati_0" 35 | 36 | self.initialize_df() 37 | -------------------------------------------------------------------------------- /.github/workflows/prepare_pr.yml: -------------------------------------------------------------------------------- 1 | name: Prepare PR 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | prepare: 10 | runs-on: ubuntu-latest 11 | outputs: 12 | changes_detected: ${{ steps.commit.outputs.changes_detected }} 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | token: ${{ secrets.TOKEN }} 17 | - name: "Get config files from template repository" 18 | run: | 19 | curl -H "Accept: application/vnd.github.VERSION.raw" -H "Authorization: token ${{ secrets.TOKEN }}" https://api.github.com/repos/gecos-lab/templatepythonproject/contents/.pylintrc\?ref\=main > .pylintrc 20 | - name: Black 21 | run: | 22 | pip install black 23 | black . 24 | - uses: stefanzweifel/git-auto-commit-action@v5 25 | id: commit 26 | with: 27 | commit_message: Apply prepare changes 28 | pylint: 29 | needs: prepare 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: dciborow/action-pylint@main 34 | with: 35 | github_token: ${{ secrets.github_token }} 36 | reporter: github-pr-review 37 | glob_pattern: "**/*.py" 38 | -------------------------------------------------------------------------------- /envs/pzero-env-from-history.yml: -------------------------------------------------------------------------------- 1 | name: pzero 2 | channels: 3 | - conda-forge 4 | - defaults 5 | dependencies: 6 | - loop3d::loopstructural[version='>=v1.6.3'] 7 | - conda-forge::blackd[version='>=23.7.0'] 8 | - conda-forge::cmocean 9 | - conda-forge::colorcet 10 | - conda-forge::ezdxf[version='>=1.0.3'] 11 | - conda-forge::geopandas[version='>=1.0.1'] 12 | - conda-forge::laspy[version='>=2.5.4'] 13 | - conda-forge::lxml[version='>=5.3.0'] 14 | - conda-forge::mplstereonet[version='>=0.6.3'] 15 | - conda-forge::openpyxl[version='>=3.1'] 16 | - conda-forge::pandas[version='>=2.2.2'] 17 | - conda-forge::pyinstaller[version='>=6'] 18 | - conda-forge::pyside6[version='>=6.7.3'] 19 | - conda-forge::pytest[version='>=8'] 20 | - conda-forge::python[version='>=3.11'] 21 | - conda-forge::pyvista[version='>=0.44.1'] 22 | - conda-forge::pyvistaqt[version='>=0.11.1'] 23 | - conda-forge::qtpy[version='>=2.4'] 24 | - conda-forge::rasterio[version='>=1.3.6'] 25 | - conda-forge::rioxarray[version='>=0.17.0'] 26 | - conda-forge::seaborn 27 | - conda-forge::segyio[version='>=1.9.0'] 28 | - conda-forge::shapely[version='>=2.0.6'] 29 | - conda-forge::sphinx[version='>=5'] 30 | - conda-forge::sphinx_rtd_theme[version='>=2'] 31 | - conda-forge::vtk[version='=9.3.1'] 32 | - conda-forge::vtk-base[version='=9.3.1'] 33 | - conda-forge::xarray[version='>=2023.1.0'] 34 | -------------------------------------------------------------------------------- /docs/pzero.ui.rst: -------------------------------------------------------------------------------- 1 | pzero.ui package 2 | ================ 3 | 4 | Submodules 5 | ---------- 6 | 7 | pzero.ui.assign\_ui module 8 | -------------------------- 9 | 10 | .. automodule:: pzero.ui.assign_ui 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pzero.ui.base\_view\_window\_ui module 16 | -------------------------------------- 17 | 18 | .. automodule:: pzero.ui.base_view_window_ui 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pzero.ui.import\_window\_ui module 24 | ---------------------------------- 25 | 26 | .. automodule:: pzero.ui.import_window_ui 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pzero.ui.navigator\_window\_ui module 32 | ------------------------------------- 33 | 34 | .. automodule:: pzero.ui.navigator_window_ui 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pzero.ui.preview\_window\_ui module 40 | ----------------------------------- 41 | 42 | .. automodule:: pzero.ui.preview_window_ui 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pzero.ui.project\_window\_ui module 48 | ----------------------------------- 49 | 50 | .. automodule:: pzero.ui.project_window_ui 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | Module contents 56 | --------------- 57 | 58 | .. automodule:: pzero.ui 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | -------------------------------------------------------------------------------- /pzero.py: -------------------------------------------------------------------------------- 1 | """pzero.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from sys import argv, exit, platform 5 | 6 | from PySide6.QtWidgets import QApplication, QSplashScreen 7 | from PySide6.QtGui import QPixmap 8 | 9 | # from PySide6.QtCore import Qt 10 | 11 | # if hasattr(Qt, 'AA_EnableHighDpiScaling'): 12 | # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling, True) 13 | # 14 | # if hasattr(Qt, 'AA_UseHighDpiPixmaps'): 15 | # QApplication.setAttribute(Qt.AA_UseHighDpiPixmaps, True) 16 | 17 | # Start Qt application 18 | app = QApplication(argv) 19 | 20 | # Use Fusion style: this is a uniform style for all platforms, but the 21 | # real reason to use it is that the standard Windows 11 style has some bug 22 | # in Qt 6.7.2, including one related to tear-off submenus. 23 | # if platform == 'darwin': 24 | # elif platform == 'win32': 25 | # elif platform == 'linux': 26 | app.setStyle("Fusion") 27 | # app.setStyle('windows11') 28 | # app.setStyle('windowsvista') 29 | # app.setStyle('windows') 30 | 31 | # Display splash screen before doing anything else 32 | splash_image = QPixmap("_internal/pzero_splash.png") 33 | splash = QSplashScreen(splash_image) 34 | splash.show() 35 | 36 | # Load all libraries 37 | from pzero.project_window import ProjectWindow 38 | 39 | # Create project window and project 40 | project_window = ProjectWindow() 41 | 42 | # Show project window and close splash screen 43 | project_window.show() 44 | splash.finish(project_window) 45 | 46 | # This is to clean everything when exiting 47 | exit(app.exec_()) 48 | -------------------------------------------------------------------------------- /pzero/collections/README.md: -------------------------------------------------------------------------------- 1 | # Collections 2 | 3 | This folder contains classes for managing different types of collections and their metadata in PZero. 4 | 5 | ## Class Hierarchy 6 | 7 | - `BaseCollection` 8 | _Abstract base class for all collection types. Defines the common interface and required methods using ABC._ 9 | _(Defined in `AbstractCollection.py`.)_ 10 | 11 | - `WellCollection` 12 | _Manages a collection of wells and their metadata._ 13 | _(Defined in `well_collection.py`.)_ 14 | 15 | - `DIMCollection` 16 | _Intermediate base class for collections of DOM, Image and Mesh3d entities._ 17 | _(Defined in `DIM_collection.py`.)_ 18 | 19 | - `DomCollection` 20 | _Manages a collection of DOM entities and their metadata._ 21 | _(Defined in `dom_collection.py`.)_ 22 | 23 | - `ImageCollection` 24 | _Manages a collection of image entities and their metadata._ 25 | _(Defined in `image_collection.py`.)_ 26 | 27 | - `Mesh3DCollection` 28 | _Manages a collection of 3D mesh entities and their metadata._ 29 | _(Defined in `mesh3d_collection.py`.)_ 30 | 31 | - `GFBCollection` 32 | _Intermediate abstract class used as a base for geological, fluid, and background collections._ 33 | _(Defined in `GFB_collection.py`.)_ 34 | 35 | - `GeologicalCollection` 36 | _Manages a collection of geological entities and their metadata._ 37 | _(Defined in `geological_collection.py`.)_ 38 | 39 | - `FluidCollection` 40 | _Manages a collection of fluid entities and their metadata._ 41 | _(Defined in `fluid_collection.py`.)_ 42 | 43 | - `BackgroundCollection` 44 | _Manages a collection of background entities and their metadata._ 45 | _(Defined in `background_collection.py`.)_ 46 | -------------------------------------------------------------------------------- /pzero/ui/assign_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Form implementation generated from reading ui file 'assign_data.ui' 4 | # 5 | # Created by: PyQt5 UI code generator 5.9.2 6 | # 7 | # WARNING! All changes made in this file will be lost! 8 | 9 | from PyQt5 import QtCore, QtGui, QtWidgets 10 | 11 | 12 | class Ui_AssignWindow(object): 13 | def setupUi(self, AssignWindow): 14 | AssignWindow.setObjectName("AssignWindow") 15 | AssignWindow.resize(450, 620) 16 | self.AssignLayout = QtWidgets.QWidget(AssignWindow) 17 | self.AssignLayout.setObjectName("AssignLayout") 18 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.AssignLayout) 19 | self.verticalLayout_2.setObjectName("verticalLayout_2") 20 | self.AssignTable = QtWidgets.QTableWidget(self.AssignLayout) 21 | self.AssignTable.setObjectName("AssignTable") 22 | self.AssignTable.setColumnCount(0) 23 | self.AssignTable.setRowCount(0) 24 | self.AssignTable.verticalHeader().setVisible(True) 25 | self.verticalLayout_2.addWidget(self.AssignTable) 26 | self.ConfirmButton = QtWidgets.QDialogButtonBox(self.AssignLayout) 27 | self.ConfirmButton.setStandardButtons( 28 | QtWidgets.QDialogButtonBox.Close | QtWidgets.QDialogButtonBox.Ok 29 | ) 30 | self.ConfirmButton.setCenterButtons(True) 31 | self.ConfirmButton.setObjectName("ConfirmButton") 32 | self.verticalLayout_2.addWidget(self.ConfirmButton) 33 | AssignWindow.setCentralWidget(self.AssignLayout) 34 | 35 | self.retranslateUi(AssignWindow) 36 | QtCore.QMetaObject.connectSlotsByName(AssignWindow) 37 | 38 | def retranslateUi(self, AssignWindow): 39 | _translate = QtCore.QCoreApplication.translate 40 | AssignWindow.setWindowTitle(_translate("AssignWindow", "Assign data")) 41 | -------------------------------------------------------------------------------- /pzero/imports/cesium2vtk.py: -------------------------------------------------------------------------------- 1 | """cesium2vtk.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from vtk import vtkCesium3DTilesWriter, vtkMultiBlockDataSet 5 | 6 | from pzero.entities_factory import TriSurf 7 | 8 | 9 | def vtk2cesium(self=None, out_dir_name=None): 10 | """Exports all triangulated surfaces to a collection of GLTF binary surfaces (extension .glb). 11 | Note that saving in binary format is automatically set by using the .glb extension. 12 | https://vtk.org/doc/nightly/html//classvtkCesium3DTilesWriter.html 13 | https://github.com/Kitware/VTK/blob/master/IO/Geometry/vtkGLTFWriter.cxx 14 | 15 | https://github.com/Kitware/Danesfield/blob/master/tools/tiler.py 16 | https://github.com/Kitware/Danesfield/blob/master/tools/tiler-test.sh 17 | 18 | IN THE FUTURE extend to other entity classes such as DEM, polyline, etc.""" 19 | # File name 20 | out_file_name = str(out_dir_name) + "/" + "multi_block_dataset" + ".glb" 21 | # Create GLTF writer. 22 | multi_block = vtkMultiBlockDataSet() 23 | print("multi_block: ", multi_block) 24 | writer = vtkCesium3DTilesWriter() 25 | print("writer: ", writer) 26 | # Loop for each entity. 27 | i = 0 28 | for uid in self.geol_coll.df["uid"]: 29 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 30 | print("i: ", i) 31 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 32 | multi_block.SetBlock(i, vtk_entity) 33 | print("multi_block: ", multi_block) 34 | i = i + 1 35 | 36 | writer.SetInputDataObject(multi_block) 37 | writer.SetDirectoryName(out_dir_name) 38 | writer.SetTextureBaseDirectory(out_dir_name) 39 | writer.SetInputType(2) 40 | writer.SetContentGLTF(True) 41 | crs = "+proj=utm +zone=32 +north +datum=WGS84" 42 | writer.SetCRS(crs) 43 | print("writer: ", writer) 44 | writer.Write() 45 | 46 | print("All TSurfs saved.") 47 | -------------------------------------------------------------------------------- /pzero/collections/mesh3d_collection.py: -------------------------------------------------------------------------------- 1 | """mesh3d_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .DIM_collection import DIMCollection 5 | 6 | 7 | class Mesh3DCollection(DIMCollection): 8 | """Collection for all mesh entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(Mesh3DCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.entity_dict = { 14 | "uid": "", 15 | "name": "undef", 16 | "scenario": "undef", 17 | "parent_uid": "", # this is the uid of the cross section for "XsVertexSet", "XsPolyLine", and "XsImage", empty for all others 18 | "topology": "undef", 19 | "vtk_obj": None, 20 | "properties_names": [], 21 | "properties_components": [], 22 | "properties_types": [], 23 | } 24 | 25 | self.entity_dict_types = { 26 | "uid": str, 27 | "name": str, 28 | "scenario": str, 29 | "parent_uid": str, 30 | "topology": str, 31 | "vtk_obj": object, 32 | "properties_names": list, 33 | "properties_components": list, 34 | "properties_types": list, 35 | } 36 | 37 | self.valid_topologies = ["TetraSolid", "Voxet", "XsVoxet"] 38 | 39 | self.collection_name = "mesh3d_coll" 40 | 41 | self.default_colormap = "rainbow" 42 | 43 | self.initialize_df() 44 | 45 | # =================================== Obligatory methods =========================================== 46 | 47 | def get_uid_legend(self, uid: str = None) -> dict: 48 | """Get legend for a particular uid.""" 49 | legend_dict = self.parent.others_legend_df.loc[ 50 | self.parent.others_legend_df["other_collection"] == "Mesh3D" 51 | ].to_dict("records") 52 | return legend_dict[0] 53 | -------------------------------------------------------------------------------- /pzero/collections/image_collection.py: -------------------------------------------------------------------------------- 1 | """image_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .DIM_collection import DIMCollection 5 | 6 | 7 | class ImageCollection(DIMCollection): 8 | """Collection for all image entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(ImageCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.entity_dict = { 14 | "uid": "", 15 | "name": "undef", 16 | "scenario": "undef", 17 | "parent_uid": "", # this is the uid of the cross section for "XsVertexSet", "XsPolyLine", and "XsImage", empty for all others 18 | "topology": "undef", 19 | "vtk_obj": None, 20 | "properties_names": [], 21 | "properties_components": [], 22 | "properties_types": [], 23 | } 24 | 25 | self.entity_dict_types = { 26 | "uid": str, 27 | "name": str, 28 | "scenario": str, 29 | "parent_uid": str, 30 | "topology": str, 31 | "vtk_obj": object, 32 | "properties_names": list, 33 | "properties_components": list, 34 | "properties_types": list, 35 | } 36 | 37 | self.valid_topologies = [ 38 | "MapImage", 39 | "XsImage", 40 | "Seismics", 41 | "Image3D", 42 | ] 43 | 44 | self.collection_name = "image_coll" 45 | 46 | self.default_colormap = "gray" 47 | 48 | self.initialize_df() 49 | 50 | # =================================== Obligatory methods =========================================== 51 | 52 | def get_uid_legend(self, uid: str = None) -> dict: 53 | """Get legend for a particular uid.""" 54 | legend_dict = self.parent.others_legend_df.loc[ 55 | self.parent.others_legend_df["other_collection"] == "Image" 56 | ].to_dict("records") 57 | return legend_dict[0] 58 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_fluid_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.fluid_collection import FluidCollection 2 | from pzero.legend_manager import Legend 3 | from pzero.entities_factory import DEM 4 | 5 | from pandas import DataFrame as pd_DataFrame 6 | from PySide6.QtWidgets import QMainWindow 7 | 8 | 9 | # Class used as a substitute of pyqt-signals/emit 10 | class FakeSignal: 11 | def emit(self, uid): 12 | return 13 | 14 | 15 | # Class used as a substitute of Legend 16 | class FakeLegend: 17 | def update_widget(self, parent): 18 | return 19 | 20 | 21 | # Class used for test the main window (project_window) as a parent 22 | class FakeWindow(QMainWindow): 23 | def __init__(self): 24 | super(FakeWindow, self).__init__() 25 | 26 | fluidss_coll.legend_df = pd_DataFrame( 27 | columns=list(Legend.fluids_legend_dict.keys()) 28 | ) 29 | legend = FakeLegend() 30 | prop_legend = FakeLegend() 31 | fluid_coll.signals.added = FakeSignal() 32 | fluid_coll.signals.removed = FakeSignal() 33 | fluid_coll = FluidCollection() 34 | 35 | 36 | class TestFluidCollection: 37 | fluid_coll_istance = FluidCollection(FakeWindow) 38 | test_vtk_obj = DEM() 39 | test_vtk_obj2 = DEM() 40 | entity_dict = { 41 | "uid": "4", 42 | "name": "fluid-test", 43 | "topology": "undef", 44 | "role": "undef", 45 | "feature": "undef", 46 | "scenario": "undef", 47 | "properties_names": [], 48 | "properties_components": [], 49 | "x_section": "", 50 | "vtk_obj": test_vtk_obj, 51 | } 52 | 53 | def test_add_entity_from_dict(self): 54 | # add an entity 55 | self.fluid_coll_istance.add_entity_from_dict(self.entity_dict) 56 | 57 | # check if the entities number is equal to the add_entity calls 58 | # and if the uid inserted is in the uids of the collection 59 | assert ( 60 | self.fluid_coll_istance.get_number_of_entities() == 1 61 | and self.entity_dict["uid"] in self.fluid_coll_istance.get_uids() 62 | ) 63 | -------------------------------------------------------------------------------- /docs/pzero.rst: -------------------------------------------------------------------------------- 1 | pzero package 2 | ============= 3 | 4 | Subpackages 5 | ----------- 6 | 7 | .. toctree:: 8 | :maxdepth: 4 9 | 10 | pzero.collections 11 | pzero.helpers 12 | pzero.imports 13 | pzero.ui 14 | 15 | Submodules 16 | ---------- 17 | 18 | pzero.entities\_factory module 19 | ------------------------------ 20 | 21 | .. automodule:: pzero.entities_factory 22 | :members: 23 | :undoc-members: 24 | :show-inheritance: 25 | 26 | pzero.legend\_manager module 27 | ---------------------------- 28 | 29 | .. automodule:: pzero.legend_manager 30 | :members: 31 | :undoc-members: 32 | :show-inheritance: 33 | 34 | pzero.orientation\_analysis module 35 | ---------------------------------- 36 | 37 | .. automodule:: pzero.orientation_analysis 38 | :members: 39 | :undoc-members: 40 | :show-inheritance: 41 | 42 | pzero.point\_clouds module 43 | -------------------------- 44 | 45 | .. automodule:: pzero.point_clouds 46 | :members: 47 | :undoc-members: 48 | :show-inheritance: 49 | 50 | pzero.project\_window module 51 | ---------------------------- 52 | 53 | .. automodule:: pzero.project_window 54 | :members: 55 | :undoc-members: 56 | :show-inheritance: 57 | 58 | pzero.properties\_manager module 59 | -------------------------------- 60 | 61 | .. automodule:: pzero.properties_manager 62 | :members: 63 | :undoc-members: 64 | :show-inheritance: 65 | 66 | pzero.three\_d\_surfaces module 67 | ------------------------------- 68 | 69 | .. automodule:: pzero.three_d_surfaces 70 | :members: 71 | :undoc-members: 72 | :show-inheritance: 73 | 74 | pzero.two\_d\_lines module 75 | -------------------------- 76 | 77 | .. automodule:: pzero.two_d_lines 78 | :members: 79 | :undoc-members: 80 | :show-inheritance: 81 | 82 | pzero.windows\_factory module 83 | ----------------------------- 84 | 85 | .. automodule:: pzero.windows_factory 86 | :members: 87 | :undoc-members: 88 | :show-inheritance: 89 | 90 | Module contents 91 | --------------- 92 | 93 | .. automodule:: pzero 94 | :members: 95 | :undoc-members: 96 | :show-inheritance: 97 | -------------------------------------------------------------------------------- /icons/SelectEntity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 51 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_background_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.background_collection import BackgroundCollection 2 | from pzero.legend_manager import Legend 3 | 4 | from pandas import DataFrame as pd_DataFrame 5 | from PySide6.QtWidgets import QMainWindow 6 | 7 | # Global Variable 8 | entity_dict = { 9 | "uid": "53", 10 | "name": "background-test", 11 | "topology": "undef", 12 | "role": "undef", 13 | "feature": "undef", 14 | "properties_names": [], 15 | "properties_components": [], 16 | "x_section": "", 17 | "borehole": "", 18 | "vtk_obj": None, 19 | } 20 | 21 | 22 | # Class used as a substitute of pyqt-signals/emit 23 | class FakeSignal: 24 | def emit(self, uid): 25 | return 26 | 27 | 28 | # Class used as a substitute of Legend 29 | class FakeLegend: 30 | def update_widget(self, parent): 31 | return 32 | 33 | 34 | # Class used as a substitute of BackgroundCollection 35 | class FakeBGCollection: 36 | df = pd_DataFrame(columns=list(entity_dict.keys())) 37 | 38 | 39 | # Class used for test the main window (project_window) as a parent 40 | class FakeWindow(QMainWindow): 41 | def __init__(self): 42 | super(FakeWindow, self).__init__() 43 | 44 | backgrnd_coll.legend_df = pd_DataFrame( 45 | columns=list(Legend.backgrounds_legend_dict.keys()) 46 | ) 47 | backgrnd_coll = FakeBGCollection() 48 | 49 | legend = FakeLegend() 50 | prop_legend = FakeLegend() 51 | 52 | backgrnd_coll.signals.added = FakeSignal() 53 | backgrnd_coll.signals.removed = FakeSignal() 54 | 55 | 56 | class TestBackgroundCollection: 57 | background_coll_istance = BackgroundCollection(FakeWindow) 58 | 59 | def test_add_entity_from_dict(self): 60 | # add an entity 61 | self.background_coll_istance.add_entity_from_dict(entity_dict) 62 | 63 | # check if the entities number is equal to the add_entity calls 64 | # and if the uid inserted is in the uids of the collection 65 | assert ( 66 | self.background_coll_istance.get_number_of_entities() == 1 67 | and entity_dict["uid"] in self.background_coll_istance.get_uids() 68 | ) 69 | -------------------------------------------------------------------------------- /icons/HomeView.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 57 | -------------------------------------------------------------------------------- /icons/MergeLines.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 54 | -------------------------------------------------------------------------------- /icons/SplitLineLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 61 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | import os 14 | import sys 15 | 16 | sys.path.insert(0, os.path.abspath("..")) 17 | 18 | 19 | # -- Project information ----------------------------------------------------- 20 | 21 | project = "PZero" 22 | copyright = "2020, Andrea Bistacchi" 23 | author = "Andrea Bistacchi" 24 | 25 | # The full version, including alpha/beta/rc tags 26 | release = "0.0.1" 27 | 28 | 29 | # -- General configuration --------------------------------------------------- 30 | 31 | # Add any Sphinx extension module names here, as strings. They can be 32 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 33 | # ones. 34 | extensions = ["sphinx.ext.autodoc", "sphinx.ext.viewcode", "sphinx.ext.napoleon"] 35 | 36 | # Add any paths that contain templates here, relative to this directory. 37 | templates_path = ["_templates"] 38 | 39 | # List of patterns, relative to source directory, that match files and 40 | # directories to ignore when looking for source files. 41 | # This pattern also affects html_static_path and html_extra_path. 42 | exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] 43 | 44 | 45 | # -- Options for HTML output ------------------------------------------------- 46 | 47 | # The theme to use for HTML and HTML Help pages. See the documentation for 48 | # a list of builtin themes. 49 | # 50 | html_theme = "sphinx_rtd_theme" 51 | 52 | # Add any paths that contain custom static files (such as style sheets) here, 53 | # relative to this directory. They are copied after the builtin static files, 54 | # so a file named "default.css" will overwrite the builtin "default.css". 55 | html_static_path = ["_static"] 56 | -------------------------------------------------------------------------------- /icons/DrawLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 51 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_mesh3d_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.mesh3d_collection import Mesh3DCollection 2 | 3 | from PySide6.QtWidgets import QMainWindow 4 | 5 | 6 | # Class used as a substitute of pyqt-signals/emit 7 | class FakeSignal: 8 | def emit(self, uid): 9 | return 10 | 11 | 12 | # Class used as a substitute of Legend 13 | class FakeLegend: 14 | def update_widget(self, parent): 15 | return 16 | 17 | 18 | # Class used for test the main window (project_window) as a parent 19 | class FakeWindow(QMainWindow): 20 | def __init__(self): 21 | super(FakeWindow, self).__init__() 22 | 23 | mesh3d_coll.signals.added = FakeSignal() 24 | mesh3d_coll.signals.removed = FakeSignal() 25 | prop_legend = FakeLegend() 26 | 27 | 28 | class TestMesh3dCollection: 29 | mesh_3d_coll_istance = Mesh3DCollection(FakeWindow) 30 | 31 | entity_dict = { 32 | "uid": "0", 33 | "name": "meshtest", 34 | "topology": "undef", 35 | "properties_names": [], 36 | "properties_components": [], 37 | "x_section": "", 38 | "vtk_obj": None, 39 | } 40 | 41 | def test_add_entity_from_dict(self): 42 | # add an entity 43 | self.mesh_3d_coll_istance.add_entity_from_dict(self.entity_dict) 44 | 45 | # check if the entities number is equal to the add_entity calls 46 | # and if the uid inserted is in the uids of the collection 47 | assert ( 48 | self.mesh_3d_coll_istance.get_number_of_entities() == 1 49 | and self.entity_dict["uid"] in self.mesh_3d_coll_istance.get_uids() 50 | ) 51 | 52 | def test_remove_entity(self): 53 | # add an entity 54 | self.mesh_3d_coll_istance.add_entity_from_dict(self.entity_dict) 55 | 56 | # remove an entity 57 | self.mesh_3d_coll_istance.remove_entity(self.entity_dict["uid"]) 58 | 59 | # check if the entities number is equal to the add_entity calls minus the remove_entity calls 60 | # and if the uid inserted and then removed is not in the uids of the collection 61 | assert ( 62 | self.mesh_3d_coll_istance.get_number_of_entities() == 0 63 | and self.entity_dict["uid"] not in self.mesh_3d_coll_istance.get_uids() 64 | ) 65 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_image_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.image_collection import ImageCollection 2 | from pzero.collections.dom_collection import DomCollection 3 | 4 | from PySide6.QtWidgets import QMainWindow 5 | 6 | 7 | # Class used as a substitute of pyqt-signals/emit 8 | class FakeSignal: 9 | def emit(self, uid): 10 | return 11 | 12 | 13 | # Class used as a substitute of Legend 14 | class FakeLegend: 15 | def update_widget(self, parent): 16 | return 17 | 18 | 19 | # Class used for test the main window (project_window) as a parent 20 | class FakeWindow(QMainWindow): 21 | def __init__(self): 22 | super(FakeWindow, self).__init__() 23 | 24 | image_coll.signals.added = FakeSignal() 25 | image_coll.signals.removed = FakeSignal() 26 | dom_coll = DomCollection() 27 | prop_legend = FakeLegend() 28 | 29 | 30 | class TestXSectionCollection: 31 | image_coll_istance = ImageCollection(FakeWindow) 32 | 33 | entity_dict = { 34 | "uid": "22", 35 | "name": "image-test", 36 | "topology": "undef", 37 | "properties_names": [], 38 | "properties_components": [], 39 | "properties_types": [], 40 | "x_section": "", 41 | "vtk_obj": None, 42 | } 43 | 44 | def test_add_entity_from_dict(self): 45 | # add an entity 46 | self.image_coll_istance.add_entity_from_dict(self.entity_dict) 47 | 48 | # check if the entities number is equal to the add_entity calls 49 | # and if the uid inserted is in the uids of the collection 50 | assert ( 51 | self.image_coll_istance.get_number_of_entities() == 1 52 | and self.entity_dict["uid"] in self.image_coll_istance.get_uids() 53 | ) 54 | 55 | def test_remove_entity(self): 56 | # add an entity 57 | self.image_coll_istance.add_entity_from_dict(self.entity_dict) 58 | 59 | # remove an entity 60 | self.image_coll_istance.remove_entity(self.entity_dict["uid"]) 61 | 62 | # check if the entities number is equal to the add_entity calls minus the remove_entity calls 63 | # and if the uid inserted and then removed is not in the uids of the collection 64 | assert [ 65 | self.entity_dict["uid"] 66 | ] not in self.image_coll_istance.get_uids() and self.image_coll_istance.get_number_of_entities() == 0 67 | -------------------------------------------------------------------------------- /pzero/imports/gltf2vtk.py: -------------------------------------------------------------------------------- 1 | """gltf2vtk.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from vtk import vtkGLTFWriter, vtkMultiBlockDataSet 5 | 6 | from pzero.entities_factory import TriSurf 7 | 8 | 9 | def vtk2gltf(self=None, out_dir_name=None): 10 | """Exports all triangulated surfaces to a collection of GLTF binary surfaces (extension .glb). 11 | Note that saving in binary format is automatically set by using the .glb extension. 12 | IN THE FUTURE extendo to other entity classes such as DEM, polyline, etc.""" 13 | # File name 14 | out_file_name = str(out_dir_name) + "/" + "multi_block_dataset" + ".glb" 15 | # Create GLTF writer. 16 | multi_block = vtkMultiBlockDataSet() 17 | writer = vtkGLTFWriter() 18 | # Loop for each entity. 19 | i = 0 20 | for uid in self.geol_coll.df["uid"]: 21 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 22 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 23 | multi_block.SetBlock(i, vtk_entity) 24 | i = i + 1 25 | # for uid in self.boundary_coll.df['uid']: 26 | # if isinstance(self.boundary_coll.get_uid_vtk_obj(uid), TriSurf): 27 | # vtk_entity = self.boundary_coll.get_uid_vtk_obj(uid) 28 | # multi_block.SetBlock(i, vtk_entity) 29 | # i = i + 1 30 | writer.InlineDataOff() 31 | # writer.SetTextureBaseDirectory(str(out_dir_name)) 32 | # SetPropertyTextureFile(const char *) 33 | writer.SaveNormalOn() 34 | writer.SaveTexturesOn() 35 | # writer.CopyTexturesOn() # AttributeError: 'vtkmodules.vtkIOGeometry.vtkGLTFWriter' object has no attribute 'CopyTexturesOn' 36 | # writer.SaveActivePointColorOn() # saves RGB is an active 3 or 4 component scalar field is found 37 | # writer.RelativeCoordinatesOn() # Save mesh point coordinates relative to the bounding box origin and add the corresponding translation to the root node - AttributeError: 'vtkmodules.vtkIOGeometry.vtkGLTFWriter' object has no attribute 'RelativeCoordinatesOn' 38 | # writer.SetRelativeCoordinates(True) # AttributeError: 'vtkmodules.vtkIOGeometry.vtkGLTFWriter' object has no attribute 'SetRelativeCoordinates' 39 | writer.SetInputDataObject(multi_block) 40 | # writer.SetDirectoryName(out_dir_name) 41 | writer.SetFileName(out_file_name) 42 | print("test - writer: ", writer) 43 | writer.Write() 44 | 45 | print("All TSurfs saved.") 46 | -------------------------------------------------------------------------------- /gui/preview_window.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PreviewWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 845 10 | 538 11 | 12 | 13 | 14 | Preview Window 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 0 23 | 0 24 | 25 | 26 | 27 | 28 | 29 | 30 | 3 31 | 32 | 33 | 34 | 35 | 36 | 37 | Preview 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 0 46 | 0 47 | 48 | 49 | 50 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok 51 | 52 | 53 | false 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /icons/SplitLinePoint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | x 59 | -------------------------------------------------------------------------------- /pzero/helpers/README.md: -------------------------------------------------------------------------------- 1 | # Helpers 2 | 3 | This folder contains utility classes and functions that support interactive widgets, color management, signal handling, dialogs, and general helper routines for the PZero project. 4 | 5 | ## File Overview 6 | 7 | - `helper_widgets.py` 8 | Provides interactive VTK-based widgets for tracing, vector drawing, editing, and cutting lines in 3D scenes. 9 | **Main classes:** 10 | - `Tracer`: For drawing freehand lines. 11 | - `Vector`: For drawing and measuring vectors (length, azimuth, dip). 12 | - `Editor`: For editing and extending polylines interactively. 13 | - `Scissors`: For interactively cutting lines. 14 | 15 | - `helper_colors.py` 16 | Utilities for color conversion, palette management, and color-related helpers. 17 | 18 | - `helper_functions.py` 19 | General-purpose helper functions for profiling, math, geometry, and data manipulation. 20 | **Main functions:** 21 | - `profiler`: Decorator for profiling function execution time. 22 | - `angle_wrapper`: Wraps angles to \[0, 2π\]. 23 | - `PCA`: Principal Component Analysis on data arrays. 24 | - `best_fitting_plane`: Computes the best fitting plane for a set of 3D points. 25 | - `gen_frame`: Generates transparent frames for GIFs. 26 | - `rotate_vec_along`: Rotates a vector along a specified axis. 27 | - `srf`: Computes the mean resultant length of vectors. 28 | - `freeze_gui`: Decorator to freeze GUI during processing. 29 | 30 | - `helper_widgets_qt.py` 31 | Qt-based widget helpers for integrating custom controls and dialogs into the GUI. 32 | 33 | - `helper_signals.py` 34 | Utilities for managing Qt signals, such as disconnecting all signals from a list. 35 | **Main function:** 36 | - `disconnect_all_signals(signals)`: Disconnects all signals of a QObject. 37 | 38 | - `helper_dialogs.py` 39 | Dialog utilities for user input, file selection, progress, and data preview. 40 | **Main functions/classes:** 41 | - `options_dialog`, `input_text_dialog`, `input_combo_dialog`, `open_file_dialog`, etc.: Various input and message dialogs. 42 | - `multiple_input_dialog`, `general_input_dialog`, `input_checkbox_dialog`: Flexible dialogs for multiple or custom inputs. 43 | - `progress_dialog`: Progress bar dialog for long-running tasks. 44 | - `PCDataModel`: Qt table model for displaying pandas DataFrames. 45 | - `import_dialog`: Window for importing and previewing data files. 46 | - `NavigatorWidget`, `PreviewWidget`: Widgets for navigation and previewing data/meshes. 47 | -------------------------------------------------------------------------------- /icons/RemoveEntity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 61 | -------------------------------------------------------------------------------- /gui/navigator_window.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | NavWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 540 10 | 95 11 | 12 | 13 | 14 | 15 | 540 16 | 90 17 | 18 | 19 | 20 | 21 | 562 22 | 95 23 | 24 | 25 | 26 | Navigator 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | 40 | 540 41 | 45 42 | 43 | 44 | 45 | 46 | 815 47 | 65 48 | 49 | 50 | 51 | 52 | 53 | 54 | >> 55 | 56 | 57 | 58 | 59 | 60 | 61 | << 62 | 63 | 64 | 65 | 66 | 67 | 68 | Qt::LeftToRight 69 | 70 | 71 | 72 | 73 | 74 | Qt::AlignCenter 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /icons/MoveLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 69 | -------------------------------------------------------------------------------- /pzero/ui/README.md: -------------------------------------------------------------------------------- 1 | # UI 2 | 3 | This folder contains user interface (UI) modules for the PZero project. These modules define the graphical and interactive components of the application using PySide6. Generally the UI files are created with Designer and converted to Python code. 4 | 5 | ## File Overview 6 | 7 | - `project_window_ui.py` 8 | Auto-generated Python code from a Qt Designer `.ui` file for the main project window. 9 | Defines the layout, menus, actions, and widgets for the main application interface, including tables, tabs, and toolbars for managing geology, fluids, backgrounds, DEMs/DOMs, images, meshes, boundaries, cross-sections, wells, legends, properties, and a terminal. 10 | 11 | - `preview_window_ui.py` 12 | Auto-generated Python code from a Qt Designer `.ui` file for the preview window. 13 | Defines the layout and widgets for a preview dialog, including options, a preview button, OK/Cancel buttons, and a preview display area. 14 | 15 | - `navigator_window_ui.py` 16 | Auto-generated Python code from a Qt Designer `.ui` file for the navigator window. 17 | Provides a minimal navigation interface with forward/back buttons and a section label, typically used for navigating between sections or steps in the application. 18 | 19 | - `import_window_ui.py` 20 | Auto-generated Python code from a Qt Designer `.ui` file for the import options window. 21 | Defines the layout and widgets for importing data, including data preview, assignment tables, file path selection, separator options, and import controls. 22 | 23 | - `dock_view_ui.py` 24 | Auto-generated Python code from a Qt Designer `.ui` file for the dockable view window. 25 | Provides a split interface with a toolbox for navigating geology, topology, cross-sections, boundaries, meshes, DEMs/DOMs, wells, fluids, backgrounds, and images, alongside a main view frame for displaying content. 26 | 27 | - `base_view_window_ui.py` 28 | Auto-generated Python code from a Qt Designer `.ui` file for the base view window. 29 | Defines a main window with a horizontal splitter: a main view frame and a toolbox for navigating geology, fluids, backgrounds, DEMs/DOMs, images, meshes, boundaries, cross-sections, and wells. Includes a menu bar and status bar. 30 | 31 | - `assign_ui.py` 32 | Auto-generated Python code from a Qt Designer `.ui` file for the assign data window. 33 | Provides a main window with a table for data assignment and a dialog button box for confirming or closing the assignment. 34 | 35 | - `__init__.py` 36 | Marks the folder as a Python package. 37 | May contain package-level imports or initialization code (often empty). 38 | -------------------------------------------------------------------------------- /pzero/imports/ply2vtk.py: -------------------------------------------------------------------------------- 1 | """ply2vtk.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from vtk import vtkPLYWriter 5 | 6 | from pzero.entities_factory import TriSurf 7 | 8 | 9 | def vtk2ply(self=None, out_dir_name=None): 10 | """Exports all triangulated surfaces to a collection of PLY surfaces. 11 | IN THE FUTURE extend to other entity classes such as DEM, polyline, etc.""" 12 | # Create PLY writer. 13 | ply_writer = vtkPLYWriter() 14 | ply_writer.SetFileTypeToBinary() 15 | ply_writer.SetColorModeToUniformCellColor() 16 | # Loop for each entity. 17 | for uid in self.geol_coll.df["uid"]: 18 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 19 | out_file_name = ( 20 | str(out_dir_name) 21 | + "/" 22 | + uid 23 | + "_" 24 | + self.geol_coll.df.loc[self.geol_coll.df["uid"] == uid, "name"].values[ 25 | 0 26 | ] 27 | + ".stl" 28 | ) 29 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 30 | color_R = self.geol_coll.get_uid_legend(uid=uid)["color_R"] 31 | color_G = self.geol_coll.get_uid_legend(uid=uid)["color_G"] 32 | color_B = self.geol_coll.get_uid_legend(uid=uid)["color_B"] 33 | print("RGB: ", color_R, color_G, color_B) 34 | ply_writer.SetFileName(out_file_name) 35 | ply_writer.SetInputData(vtk_entity) 36 | ply_writer.SetColor(color_R, color_G, color_B) 37 | ply_writer.Write() 38 | for uid in self.boundary_coll.df["uid"]: 39 | if isinstance(self.boundary_coll.get_uid_vtk_obj(uid), TriSurf): 40 | out_file_name = ( 41 | str(out_dir_name) 42 | + "/" 43 | + uid 44 | + "_" 45 | + self.boundary_coll.df.loc[ 46 | self.boundary_coll.df["uid"] == uid, "name" 47 | ].values[0] 48 | + ".stl" 49 | ) 50 | vtk_entity = self.boundary_coll.get_uid_vtk_obj(uid) 51 | color_R = self.boundary_coll.get_uid_legend(uid=uid)["color_R"] 52 | color_G = self.boundary_coll.get_uid_legend(uid=uid)["color_G"] 53 | color_B = self.boundary_coll.get_uid_legend(uid=uid)["color_B"] 54 | print("RGB: ", color_R, color_G, color_B) 55 | ply_writer.SetFileName(out_file_name) 56 | ply_writer.SetInputData(vtk_entity) 57 | ply_writer.SetColor(color_R, color_G, color_B) 58 | ply_writer.Write() 59 | print("All TSurfs saved.") 60 | -------------------------------------------------------------------------------- /icons/ExtendLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 65 | -------------------------------------------------------------------------------- /icons/BoundaryFrom2Points.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 64 | -------------------------------------------------------------------------------- /envs/notes_on_pzero_environment_2025-03-12.txt: -------------------------------------------------------------------------------- 1 | =================================== 2 | Notes on pzero conda environments 3 | 4 | THIS DOCUMENT MUST BE KEPT UPDATED! 5 | =================================== 6 | 7 | _______________________ 8 | Last updated 2025-03-12 9 | 10 | 11 | 1) To build a clean pzero environment, start updating the conda base environment: 12 | 13 | conda activate base 14 | conda update -n base -c conda-forge conda 15 | conda update --all 16 | 17 | (sometimes repeating this more than once helps to have a completely up-to-date base environment) 18 | 19 | 3) Then remove any old pzero environment, e.g.: 20 | 21 | conda remove --name pzero --all 22 | 23 | 4) Install packages (from within ./PZero/envs directory): 24 | 25 | conda env create -n pzero -f ./pzero-env.yml 26 | 27 | or 28 | 29 | conda env create -n pzero -f ./pzero-env-from-history.yml 30 | 31 | The former includes all packages with specific releases, while the latter includes only requirements listed in pzero-env-from-history.yml. 32 | 33 | The logic in the second case is to have the most recent versions as possible, or at least well proven ones (>=), with exceptions (strictly =) motivated in the issues. 34 | 35 | Note that version requirements are easily recorded/updated by editing the pzero-env-from-history.yml file and then regenerating the environment as above. 36 | 37 | For some strange reason, openpyxl (used to import Excel files) is required by pandas but not automatically added with its dependencies, so we have to add it explicitly to the environment. 38 | 39 | Additional modules that where explicitly required in the past, but apparently not now, are pillow>=9.5.0, pythreejs>=2.4.2, and vedo. 40 | 41 | This has been tested on Windows 11 and macOS 15 so far. 42 | 43 | On macOS it is possible that conda fails to detect the right version of the OS. In this case it is necessary to force the __osx variable with: 44 | 45 | conda env config vars set CONDA_OVERRIDE_OSX="11" 46 | 47 | Then the value of __osx can be checked with: 48 | 49 | conda info 50 | 51 | 5) Finally record the environment in .yml files (from within ./PZero/envs directory): 52 | 53 | conda env export --from-history > ./pzero-env-from-history.yml 54 | 55 | and 56 | 57 | conda env export > ./pzero-env.yml 58 | 59 | This allows a quick installation with (from within ./PZero/envs directory): 60 | 61 | conda env create -n pzero -f ./pzero-env.yml 62 | 63 | 64 | 65 | * In case a new empty environment must be created, use: 66 | 67 | conda create -n pzero 68 | 69 | * In case a channels must be activated (this must be done for each new environment): 70 | 71 | conda config --env --add channels conda-forge 72 | conda config --env --add channels loop3d 73 | conda config --env --add channels anaconda -------------------------------------------------------------------------------- /pzero/imports/obj2vtk.py: -------------------------------------------------------------------------------- 1 | """obj2vtk.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from vtk import vtkOBJWriter 5 | from pzero.entities_factory import TriSurf 6 | from pzero.helpers.helper_dialogs import options_dialog 7 | 8 | 9 | def vtk2obj(self=None, out_dir_name=None): 10 | """Exports all triangulated surfaces to a collection of OBJ surfaces. 11 | IN THE FUTURE extendo to other entity classes such as DEM, polyline, etc.""" 12 | # At the moment only entities in the geological collection can be exported. 13 | if self.shown_table != "tabGeology": 14 | return 15 | append_name = options_dialog( 16 | title="Append name", 17 | message="Append entity name to output file name?", 18 | yes_role="Yes", 19 | no_role="No", 20 | reject_role=None, 21 | ) 22 | if append_name == 0: 23 | append_name = True 24 | else: 25 | append_name = False 26 | # Create STL writer. 27 | obj_writer = vtkOBJWriter() 28 | # Loop for each entity. 29 | for uid in self.selected_uids: 30 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 31 | if append_name: 32 | out_file_name = ( 33 | str(out_dir_name) 34 | + "/" 35 | + uid 36 | + "_" 37 | + self.geol_coll.df.loc[ 38 | self.geol_coll.df["uid"] == uid, "name" 39 | ].values[0] 40 | + ".obj" 41 | ) 42 | else: 43 | out_file_name = str(out_dir_name) + "/" + uid + ".obj" 44 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 45 | obj_writer.SetFileName(out_file_name) 46 | obj_writer.SetInputData(vtk_entity) 47 | obj_writer.Write() 48 | # for uid in self.selected_uids: 49 | # if isinstance(self.boundary_coll.get_uid_vtk_obj(uid), TriSurf): 50 | # if append_name: 51 | # out_file_name = ( 52 | # str(out_dir_name) 53 | # + "/" 54 | # + uid 55 | # + "_" 56 | # + self.boundary_coll.df.loc[ 57 | # self.boundary_coll.df["uid"] == uid, "name" 58 | # ].values[0] 59 | # + ".obj" 60 | # ) 61 | # else: 62 | # out_file_name = str(out_dir_name) + "/" + uid + ".obj" 63 | # vtk_entity = self.boundary_coll.get_uid_vtk_obj(uid) 64 | # obj_writer.SetFileName(out_file_name) 65 | # obj_writer.SetInputData(vtk_entity) 66 | # obj_writer.Write() 67 | # print("All TSurfs saved.") 68 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_geological_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.geological_collection import GeologicalCollection 2 | from pzero.legend_manager import Legend 3 | 4 | from pandas import DataFrame as pd_DataFrame 5 | from PySide6.QtWidgets import QMainWindow 6 | 7 | 8 | # Class used as a substitute of pyqt-signals/emit 9 | class FakeSignal: 10 | def emit(self, uid): 11 | return 12 | 13 | 14 | # Class used as a substitute of Legend 15 | class FakeLegend: 16 | def update_widget(self, parent): 17 | return 18 | 19 | 20 | # Class used for test the main window (project_window) as a parent 21 | class FakeWindow(QMainWindow): 22 | def __init__(self): 23 | super(FakeWindow, self).__init__() 24 | 25 | geol_coll.legend_df = pd_DataFrame(columns=list(Legend.geol_legend_dict.keys())) 26 | legend = FakeLegend() 27 | prop_legend = FakeLegend() 28 | geol_coll.signals.added = FakeSignal() 29 | geol_coll.signals.removed = FakeSignal() 30 | 31 | 32 | # Class for testing geological_collection.py 33 | class TestGeologicalCollection: 34 | geo_coll_istance = GeologicalCollection(FakeWindow) 35 | 36 | geological_entity_dict1 = { 37 | "uid": "0", 38 | "name": "geoname", 39 | "topology": "topol", 40 | "role": "undef", 41 | "feature": "undef", 42 | "scenario": "sc1", 43 | "properties_names": [], 44 | "properties_components": [], 45 | "x_section": "", 46 | "vtk_obj": None, 47 | } 48 | 49 | geological_entity_dict2 = { 50 | "uid": "2", 51 | "name": "geoname2", 52 | "topology": "topol2", 53 | "role": "undef", 54 | "feature": "undef", 55 | "scenario": "sc2", 56 | "properties_names": [], 57 | "properties_components": [], 58 | "x_section": "", 59 | "vtk_obj": None, 60 | } 61 | 62 | # Test the add_entity_from_dict() method from geological_collection.py 63 | def test_add_entity_from_dict(self): 64 | # Add two entities 65 | self.geo_coll_istance.add_entity_from_dict( 66 | entity_dict=self.geological_entity_dict1 67 | ) 68 | self.geo_coll_istance.add_entity_from_dict( 69 | entity_dict=self.geological_entity_dict2 70 | ) 71 | 72 | # This print should be the same as the entity_dict 73 | # print(geo_coll_istance.df) 74 | 75 | assert ( 76 | self.geo_coll_istance.get_number_of_entities() == 2 77 | and ( 78 | self.geological_entity_dict1["uid"] in self.geo_coll_istance.get_uids() 79 | ) 80 | and ( 81 | self.geological_entity_dict2["uid"] in self.geo_coll_istance.get_uids() 82 | ) 83 | ) 84 | -------------------------------------------------------------------------------- /icons/ExportAsGLTF.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GLTF 69 | -------------------------------------------------------------------------------- /icons/ExportAsHTML.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | HTML 69 | -------------------------------------------------------------------------------- /icons/ExportAsOBJ.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OBJ 69 | -------------------------------------------------------------------------------- /icons/SortLineNodes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 75 | -------------------------------------------------------------------------------- /pzero/imports/dem2vtk.py: -------------------------------------------------------------------------------- 1 | """dem2vtk.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | import os 5 | 6 | from uuid import uuid4 7 | 8 | from copy import deepcopy 9 | 10 | from rioxarray import open_rasterio as rx_open_rasterio 11 | 12 | from numpy import meshgrid as np_meshgrid 13 | 14 | from pyvista import StructuredGrid as pv_StructuredGrid 15 | 16 | from pzero.collections.dom_collection import DomCollection 17 | from pzero.collections.fluid_collection import FluidCollection 18 | from pzero.entities_factory import DEM 19 | 20 | 21 | def dem2vtk(self=None, in_file_name=None, collection=None): 22 | """Import and add a DEM structured grid to the dom_coll of the project. 23 | is the calling ProjectWindow() instance.""" 24 | # Read raster file format (geotiff) with xarray and rasterio and create DEM structured grid. 25 | # Helpful: http://xarray.pydata.org/en/stable/auto_gallery/plot_rasterio.html 26 | # https://github.com/pyvista/pyvista-support/issues/205, thanks to Bane Sullivan 27 | data = rx_open_rasterio(in_file_name) 28 | values = data.values[0] 29 | # nans = values == data.nodatavals 30 | # if np_any(nans): 31 | # values[nans] = np_nan 32 | xx, yy = np_meshgrid(data["x"], data["y"]) 33 | zz = values.reshape(xx.shape) 34 | # Convert to DEM() instance. 35 | curr_obj = DEM() 36 | temp_obj = pv_StructuredGrid(xx, yy, zz) 37 | temp_obj["elevation"] = zz.ravel(order="F") 38 | curr_obj.ShallowCopy(temp_obj) 39 | curr_obj.Modified() 40 | # Create dictionary. 41 | if collection == "DEMs and DOMs": 42 | curr_obj_attributes = deepcopy(DomCollection().entity_dict) 43 | curr_obj_attributes["uid"] = str(uuid4()) 44 | curr_obj_attributes["name"] = os.path.basename(in_file_name) 45 | curr_obj_attributes["topology"] = "DEM" 46 | curr_obj_attributes["textures"] = [] 47 | curr_obj_attributes["properties_names"] = ["elevation"] 48 | curr_obj_attributes["properties_components"] = [1] 49 | curr_obj_attributes["vtk_obj"] = curr_obj 50 | # Add to entity collection. 51 | self.dom_coll.add_entity_from_dict(entity_dict=curr_obj_attributes) 52 | elif collection == "Fluid contacts": 53 | curr_obj_attributes = deepcopy(FluidCollection.entity_dict) 54 | curr_obj_attributes["uid"] = str(uuid4()) 55 | curr_obj_attributes["name"] = os.path.basename(in_file_name) 56 | curr_obj_attributes["role"] = "raster" 57 | curr_obj_attributes["textures"] = [] 58 | curr_obj_attributes["properties_names"] = ["elevation"] 59 | curr_obj_attributes["properties_components"] = [1] 60 | curr_obj_attributes["vtk_obj"] = curr_obj 61 | # Add to entity collection. 62 | self.fluid_coll.add_entity_from_dict(entity_dict=curr_obj_attributes) 63 | # Cleaning. 64 | del curr_obj 65 | del curr_obj_attributes 66 | -------------------------------------------------------------------------------- /icons/EditLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 69 | -------------------------------------------------------------------------------- /icons/ExportAsVTKjs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | VTKjs 71 | -------------------------------------------------------------------------------- /docs/pzero.imports.rst: -------------------------------------------------------------------------------- 1 | pzero.imports package 2 | ===================== 3 | 4 | Submodules 5 | ---------- 6 | 7 | pzero.imports.dem2vtk module 8 | ---------------------------- 9 | 10 | .. automodule:: pzero.imports.dem2vtk 11 | :members: 12 | :undoc-members: 13 | :show-inheritance: 14 | 15 | pzero.imports.dxf2vtk module 16 | ---------------------------- 17 | 18 | .. automodule:: pzero.imports.dxf2vtk 19 | :members: 20 | :undoc-members: 21 | :show-inheritance: 22 | 23 | pzero.imports.gocad2vtk module 24 | ------------------------------ 25 | 26 | .. automodule:: pzero.imports.gocad2vtk 27 | :members: 28 | :undoc-members: 29 | :show-inheritance: 30 | 31 | pzero.imports.image2vtk module 32 | ------------------------------ 33 | 34 | .. automodule:: pzero.imports.image2vtk 35 | :members: 36 | :undoc-members: 37 | :show-inheritance: 38 | 39 | pzero.imports.lxml2vtk module 40 | ----------------------------- 41 | 42 | .. automodule:: pzero.imports.lxml2vtk 43 | :members: 44 | :undoc-members: 45 | :show-inheritance: 46 | 47 | pzero.imports.obj2vtk module 48 | ---------------------------- 49 | 50 | .. automodule:: pzero.imports.obj2vtk 51 | :members: 52 | :undoc-members: 53 | :show-inheritance: 54 | 55 | pzero.imports.pc2vtk module 56 | --------------------------- 57 | 58 | .. automodule:: pzero.imports.pc2vtk 59 | :members: 60 | :undoc-members: 61 | :show-inheritance: 62 | 63 | pzero.imports.ply2vtk module 64 | ---------------------------- 65 | 66 | .. automodule:: pzero.imports.ply2vtk 67 | :members: 68 | :undoc-members: 69 | :show-inheritance: 70 | 71 | pzero.imports.pyvista2vtk module 72 | -------------------------------- 73 | 74 | .. automodule:: pzero.imports.pyvista2vtk 75 | :members: 76 | :undoc-members: 77 | :show-inheritance: 78 | 79 | pzero.imports.segy2vtk module 80 | ----------------------------- 81 | 82 | .. automodule:: pzero.imports.segy2vtk 83 | :members: 84 | :undoc-members: 85 | :show-inheritance: 86 | 87 | pzero.imports.shp2vtk module 88 | ---------------------------- 89 | 90 | .. automodule:: pzero.imports.shp2vtk 91 | :members: 92 | :undoc-members: 93 | :show-inheritance: 94 | 95 | pzero.imports.stl2vtk module 96 | ---------------------------- 97 | 98 | .. automodule:: pzero.imports.stl2vtk 99 | :members: 100 | :undoc-members: 101 | :show-inheritance: 102 | 103 | pzero.imports.vedo2vtk module 104 | ----------------------------- 105 | 106 | .. automodule:: pzero.imports.vedo2vtk 107 | :members: 108 | :undoc-members: 109 | :show-inheritance: 110 | 111 | pzero.imports.well2vtk module 112 | ----------------------------- 113 | 114 | .. automodule:: pzero.imports.well2vtk 115 | :members: 116 | :undoc-members: 117 | :show-inheritance: 118 | 119 | Module contents 120 | --------------- 121 | 122 | .. automodule:: pzero.imports 123 | :members: 124 | :undoc-members: 125 | :show-inheritance: 126 | -------------------------------------------------------------------------------- /icons/SaveHomeView.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 68 | -------------------------------------------------------------------------------- /icons/ClearSelection.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 64 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_xsection_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.xsection_collection import XSectionCollection 2 | from PySide6.QtWidgets import QMainWindow 3 | 4 | 5 | # Class used as a substitute of pyqt-signals/emit 6 | class FakeSignal: 7 | def emit(self, uid): 8 | return 9 | 10 | 11 | # Class used as a substitute of Legend 12 | class FakeLegend: 13 | def update_widget(self, parent): 14 | return 15 | 16 | 17 | # Class used for test the main window (project_window) as a parent 18 | class FakeWindow(QMainWindow): 19 | def __init__(self): 20 | super(FakeWindow, self).__init__() 21 | 22 | xsect_coll.signals.added = FakeSignal() 23 | xsect_coll.signals.removed = FakeSignal() 24 | 25 | 26 | class TestXSectionCollection: 27 | entity_dict = { 28 | "uid": "0", 29 | "name": "geoname", 30 | "topology": "topol", 31 | "role": "undef", 32 | "feature": "undef", 33 | "scenario": "sc1", 34 | "properties_names": [], 35 | "properties_components": [], 36 | "x_section": "", 37 | "vtk_obj": None, 38 | } 39 | 40 | x_section_coll_istance = XSectionCollection(FakeWindow) 41 | 42 | def test_add_entity_from_dict(self): 43 | # add an entity 44 | self.x_section_coll_istance.add_entity_from_dict(self.entity_dict) 45 | 46 | # check if the entities number is equal to the add_entity calls 47 | # and if the uid inserted is in the uids of the collection 48 | assert ( 49 | self.x_section_coll_istance.get_number_of_entities() == 1 50 | and self.entity_dict["uid"] in self.x_section_coll_istance.get_uids() 51 | ) 52 | 53 | def test_remove_entity(self): 54 | # add an entity 55 | self.x_section_coll_istance.add_entity_from_dict(self.entity_dict) 56 | 57 | # remove an entity 58 | self.x_section_coll_istance.remove_entity(self.entity_dict["uid"]) 59 | 60 | # check if the entities number is equal to the add_entity calls minus the remove_entity calls 61 | # and if the uid inserted and then removed is not in the uids of the collection 62 | assert ( 63 | self.x_section_coll_istance.get_number_of_entities() == 0 64 | and self.entity_dict["uid"] not in self.x_section_coll_istance.get_uids() 65 | ) 66 | 67 | def test_set_parameters_in_table(self): 68 | # add an entity 69 | self.x_section_coll_istance.add_entity_from_dict(self.entity_dict) 70 | 71 | self.x_section_coll_istance.set_parameters_in_table( 72 | uid="2", 73 | name="name-test", 74 | base_point=[0, 1, 10], 75 | end_point=[0, 1, 2], 76 | normal=[0, 1, 2], 77 | azimuth=None, 78 | length=None, 79 | top=None, 80 | bottom=None, 81 | ) 82 | 83 | assert ( 84 | self.x_section_coll_istance.get_number_of_entities() == 1 85 | and self.entity_dict["uid"] in self.x_section_coll_istance.get_uids() 86 | ) 87 | -------------------------------------------------------------------------------- /.github/old_workflow/class_refactoring_pyside6-testing.yml: -------------------------------------------------------------------------------- 1 | name: Unit Testing 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'class_refactoring_pyside6' 7 | 8 | 9 | jobs: 10 | # Linux 11 | linux-testing: 12 | name: Linux Unit Testing 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | shell: bash -el {0} 17 | permissions: 18 | checks: write 19 | pull-requests: write 20 | 21 | 22 | steps: 23 | - name: Checkout Repo 24 | uses: actions/checkout@v3 25 | 26 | - name: Create Environment from .yml 27 | uses: conda-incubator/setup-miniconda@v2 28 | with: 29 | #mamba-version: "*" 30 | channels: conda-forge,defaults 31 | channel-priority: true 32 | activate-environment: pzero-testing 33 | environment-file: envs/pzero-env.yml 34 | # python-version: 3.8.16 35 | python-version: 3.12 36 | auto-activate-base: false 37 | 38 | - name: Check Conda Dependencies 39 | run: | 40 | conda info 41 | conda list 42 | 43 | - name: Test with pytest 44 | run: | 45 | conda install pytest-html 46 | pytest --html=ubu-test-report.html tests/ 47 | 48 | - name: Upload pytest test results 49 | uses: actions/upload-artifact@v3 50 | with: 51 | name: pytest-results-ubuntu 52 | path: ubu-test-report.html 53 | # Use always() to always run this step to publish test results when there are test failures 54 | if: ${{ always() }} 55 | 56 | 57 | # Windows 58 | windows-testing: 59 | name: Windows Unit Testing 60 | runs-on: windows-latest 61 | defaults: 62 | run: 63 | shell: bash -el {0} 64 | permissions: 65 | checks: write 66 | pull-requests: write 67 | 68 | steps: 69 | - name: Checkout Repo 70 | uses: actions/checkout@v3 71 | 72 | - name: Create Environment from .yml 73 | uses: conda-incubator/setup-miniconda@v2 74 | with: 75 | #mamba-version: "*" 76 | channels: conda-forge,defaults 77 | channel-priority: true 78 | activate-environment: pzero-test 79 | environment-file: envs/pzero-env.yml 80 | # python-version: 3.8.16 81 | python-version: 3.12 82 | auto-activate-base: false 83 | 84 | - name: Check Conda Dependencies 85 | run: | 86 | conda info 87 | conda list 88 | 89 | - name: Test with pytest 90 | run: | 91 | conda install pytest-html 92 | pytest --html=win-test-report.html tests/ 93 | 94 | - name: Upload pytest test results 95 | uses: actions/upload-artifact@v3 96 | with: 97 | name: pytest-results-win 98 | path: win-test-report.html 99 | # Use always() to always run this step to publish test results when there are test failures 100 | if: ${{ always() }} 101 | -------------------------------------------------------------------------------- /icons/ZoomToActive.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 78 | -------------------------------------------------------------------------------- /icons/dip.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 35 | 37 | 41 | 47 | 54 | 61 | 67 | 71 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /icons/RotateLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 83 | -------------------------------------------------------------------------------- /icons/SnapLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 70 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_well_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.well_collection import WellCollection 2 | from pzero.entities_factory import Well 3 | from pzero.entities_factory import PolyLine 4 | from pzero.legend_manager import Legend 5 | 6 | from pandas import DataFrame as pd_DataFrame 7 | from PySide6.QtWidgets import QMainWindow 8 | 9 | 10 | # Class used as a substitute of pyqt-signals/emit 11 | class FakeSignal: 12 | def emit(self, uid): 13 | return 14 | 15 | 16 | # Class used as a substitute of Legend 17 | class FakeLegend: 18 | def update_widget(self, parent): 19 | return 20 | 21 | 22 | # Class used for test the main window (project_window) as a parent 23 | class FakeWindow(QMainWindow): 24 | def __init__(self): 25 | super(FakeWindow, self).__init__() 26 | 27 | legend = FakeLegend() 28 | prop_legend = FakeLegend() 29 | well_legend_df = pd_DataFrame(columns=list(Legend.well_legend_dict.keys())) 30 | well_coll.signals.added = FakeSignal() 31 | well_coll.signals.removed = FakeSignal() 32 | 33 | 34 | class TestWellConnection: 35 | vtk_obj = PolyLine() 36 | vtk_obj2 = Well().trace 37 | well_istance = WellCollection(FakeWindow) 38 | 39 | well_entity_dict_1 = { 40 | "uid": "14", 41 | "Loc ID": "3", 42 | "properties_names": [], 43 | "properties_components": [], 44 | "properties_types": [], 45 | "markers": [], 46 | "vtk_obj": vtk_obj, 47 | } 48 | 49 | def test_add_entity_from_dict(self): 50 | # add an entity 51 | self.well_istance.add_entity_from_dict(self.well_entity_dict_1) 52 | 53 | # check if the entities number is equal to the add_entity calls 54 | # and if the uid inserted is in the uids of the collection 55 | assert ( 56 | self.well_istance.get_number_of_entities() == 1 57 | and self.well_entity_dict_1["uid"] in self.well_istance.get_uids() 58 | ) 59 | 60 | def test_remove_entity(self): 61 | # add an entity 62 | self.well_istance.add_entity_from_dict(self.well_entity_dict_1) 63 | 64 | # remove an entity 65 | self.well_istance.remove_entity(self.well_entity_dict_1["uid"]) 66 | 67 | # check if the entities number is equal to the add_entity calls minus the remove_entity calls 68 | # and if the uid inserted and then removed is not in the uids of the collection 69 | assert ( 70 | self.well_istance.get_number_of_entities() == 0 71 | and self.well_entity_dict_1["uid"] not in self.well_istance.get_uids() 72 | ) 73 | 74 | def test_replace_vtk(self): 75 | # add an entity 76 | self.well_istance.add_entity_from_dict(self.well_entity_dict_1) 77 | 78 | # replace the vtk obj of the entity added 79 | self.well_istance.replace_vtk( 80 | uid=self.well_entity_dict_1["uid"], vtk_object=self.vtk_obj2 81 | ) 82 | 83 | # check if the entities number is equal to the add_entity calls 84 | # and if the vtk obj inserted is in the uids of the collection 85 | assert ( 86 | self.well_istance.get_number_of_entities() == 1 87 | and self.well_istance.get_uid_vtk_obj(self.well_entity_dict_1["uid"]) 88 | == self.vtk_obj2 89 | ) 90 | -------------------------------------------------------------------------------- /old_tests/test_collections/test_dom_collection.py: -------------------------------------------------------------------------------- 1 | from pzero.collections.dom_collection import DomCollection 2 | from pzero.legend_manager import Legend 3 | from pzero.entities_factory import DEM 4 | 5 | from pandas import DataFrame as pd_DataFrame 6 | from PySide6.QtWidgets import QMainWindow 7 | 8 | 9 | # Class used as a substitute of pyqt-signals/emit 10 | class FakeSignal: 11 | def emit(self, uid): 12 | return 13 | 14 | 15 | # Class used as a substitute of Legend 16 | class FakeLegend: 17 | def update_widget(self, parent): 18 | return 19 | 20 | 21 | # Class used for test the main window (project_window) as a parent 22 | class FakeWindow(QMainWindow): 23 | 24 | def __init__(self): 25 | super(FakeWindow, self).__init__() 26 | 27 | backgrnd_coll.legend_df = pd_DataFrame( 28 | columns=list(Legend.backgrounds_legend_dict.keys()) 29 | ) 30 | legend = FakeLegend() 31 | prop_legend = FakeLegend() 32 | dom_coll.signals.added = FakeSignal() 33 | dom_coll.signals.removed = FakeSignal() 34 | 35 | 36 | # Class for testing the dom_collection 37 | class TestDomCollection: 38 | test_vtk_obj = DEM() 39 | test_vtk_obj2 = DEM() 40 | entity_dict = { 41 | "uid": "0", 42 | "name": "geoname", 43 | "topology": "topol", 44 | "role": "undef", 45 | "feature": "undef", 46 | "scenario": "sc1", 47 | "properties_names": [], 48 | "properties_components": [], 49 | "x_section": "", 50 | "vtk_obj": test_vtk_obj, 51 | } 52 | 53 | dom_istance = DomCollection(FakeWindow) 54 | 55 | def test_add_entity_from_dict(self): 56 | # add an entity 57 | self.dom_istance.add_entity_from_dict(self.entity_dict) 58 | 59 | # check if the entities number is equal to the add_entity calls 60 | # and if the uid inserted is in the uids of the collection 61 | assert ( 62 | self.dom_istance.get_number_of_entities() == 1 63 | and self.entity_dict["uid"] in self.dom_istance.get_uids() 64 | ) 65 | 66 | def test_remove_entity(self): 67 | # add an entity 68 | self.dom_istance.add_entity_from_dict(self.entity_dict) 69 | 70 | # remove an entity 71 | self.dom_istance.remove_entity(self.entity_dict["uid"]) 72 | 73 | # check if the entities number is equal to the add_entity calls minus the remove_entity calls 74 | # and if the uid inserted and then removed is not in the uids of the collection 75 | assert ( 76 | self.dom_istance.get_number_of_entities() == 0 77 | and self.entity_dict["uid"] not in self.dom_istance.get_uids() 78 | ) 79 | 80 | def test_replace_vtk(self): 81 | # add an entity 82 | self.dom_istance.add_entity_from_dict(self.entity_dict) 83 | 84 | # replace the vtk obj of the entity added 85 | self.dom_istance.replace_vtk( 86 | uid=self.entity_dict["uid"], vtk_object=self.test_vtk_obj2 87 | ) 88 | 89 | # check if the entities number is equal to the add_entity calls 90 | # and if the vtk obj inserted is in the uids of the collection 91 | assert ( 92 | self.dom_istance.get_number_of_entities() == 1 93 | and self.dom_istance.get_uid_vtk_obj(self.entity_dict["uid"]) 94 | == self.test_vtk_obj2 95 | ) 96 | -------------------------------------------------------------------------------- /docs/pzero.collections.rst: -------------------------------------------------------------------------------- 1 | pzero.collections package 2 | ========================= 3 | One of the most important groups of files comprises the "collections." Each collection encapsulates all entities of a specific type of geological or non-geological objects, such as images that indirectly assist in describing a geological model. These collections are always Pandas dataframes. Inside each "type_of_collection.py" file, we have a series of similar methods that serve to manage the entities, including: 4 | 5 | -add_entity(): Takes an entity as input and inserts it into the entity list of that collection. 6 | 7 | -remove_entity(): Removes a specific entity from a collection using its UID. 8 | 9 | -clone_entity(): Duplicates and adds the same input entity to a collection. 10 | 11 | -replace_vtk(): Given a UID, replaces the vtk instance of that entity with another input instance. 12 | 13 | Many getter and setter methods for retrieving and setting UIDs, properties, metadata, etc. 14 | Another group of files is used within "project_windows.py," comprising around fifteen small files that contain all the useful functions for converting, importing, and exporting various formats to be used in the View. 15 | 16 | Submodules 17 | ---------- 18 | 19 | pzero.collections.background\_collection module 20 | ----------------------------------------------- 21 | 22 | .. automodule:: pzero.collections.background_collection 23 | :members: 24 | :undoc-members: 25 | :show-inheritance: 26 | 27 | pzero.collections.boundary\_collection module 28 | --------------------------------------------- 29 | 30 | .. automodule:: pzero.collections.boundary_collection 31 | :members: 32 | :undoc-members: 33 | :show-inheritance: 34 | 35 | pzero.collections.dom\_collection module 36 | ---------------------------------------- 37 | 38 | .. automodule:: pzero.collections.dom_collection 39 | :members: 40 | :undoc-members: 41 | :show-inheritance: 42 | 43 | pzero.collections.fluid\_collection module 44 | ------------------------------------------ 45 | 46 | .. automodule:: pzero.collections.fluid_collection 47 | :members: 48 | :undoc-members: 49 | :show-inheritance: 50 | 51 | pzero.collections.geological\_collection module 52 | ----------------------------------------------- 53 | 54 | .. automodule:: pzero.collections.geological_collection 55 | :members: 56 | :undoc-members: 57 | :show-inheritance: 58 | 59 | pzero.collections.image\_collection module 60 | ------------------------------------------ 61 | 62 | .. automodule:: pzero.collections.image_collection 63 | :members: 64 | :undoc-members: 65 | :show-inheritance: 66 | 67 | pzero.collections.mesh3d\_collection module 68 | ------------------------------------------- 69 | 70 | .. automodule:: pzero.collections.mesh3d_collection 71 | :members: 72 | :undoc-members: 73 | :show-inheritance: 74 | 75 | pzero.collections.well\_collection module 76 | ----------------------------------------- 77 | 78 | .. automodule:: pzero.collections.well_collection 79 | :members: 80 | :undoc-members: 81 | :show-inheritance: 82 | 83 | pzero.collections.xsection\_collection module 84 | --------------------------------------------- 85 | 86 | .. automodule:: pzero.collections.xsection_collection 87 | :members: 88 | :undoc-members: 89 | :show-inheritance: 90 | 91 | Module contents 92 | --------------- 93 | 94 | .. automodule:: pzero.collections 95 | :members: 96 | :undoc-members: 97 | :show-inheritance: 98 | -------------------------------------------------------------------------------- /icons/ResampleDistance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 79 | -------------------------------------------------------------------------------- /.github/old_workflow/release.yml: -------------------------------------------------------------------------------- 1 | name: Deploy and Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - v*.*.* 7 | 8 | jobs: 9 | # Specify the reusable workflow 10 | testing: 11 | uses: ./.github/old_workflow/release-testing.yml 12 | 13 | deploy: 14 | strategy: 15 | matrix: 16 | # OS we can use to run jobs 17 | os: [windows-latest, ubuntu-latest, macos-latest] 18 | 19 | defaults: 20 | run: 21 | shell: bash -el {0} 22 | permissions: 23 | checks: write 24 | pull-requests: write 25 | 26 | runs-on: ${{ matrix.os }} 27 | # Requiring one or more reusable old_workflow in needs 28 | needs: [testing] 29 | 30 | # Steps for deploying a release 31 | steps: 32 | - name: Checkout Repo 33 | uses: actions/checkout@v3 34 | 35 | - name: Install Conda Env 36 | # Use the following if condition to generate os-specific envs 37 | # if: ${{ matrix.os == 'macos-latest' }} 38 | # if: ${{ matrix.os != 'macos-latest' }} 39 | uses: conda-incubator/setup-miniconda@v2 40 | with: 41 | mamba-version: "*" 42 | channels: conda-forge,defaults 43 | channel-priority: true 44 | activate-environment: pzero 45 | environment-file: envs/pzero-env-from-history.yml 46 | python-version: 3.11 47 | auto-activate-base: false 48 | 49 | - name: Check Conda Dependencies 50 | run: | 51 | conda info 52 | conda list 53 | 54 | - name: Make executable - Windows 55 | if: ${{ matrix.os == 'windows-latest' }} 56 | run: | 57 | conda activate pzero 58 | pyinstaller pzero-windows.spec 59 | ls 60 | 61 | - name: Make executable - Linux 62 | if: ${{ matrix.os == 'ubuntu-latest' }} 63 | run: | 64 | conda activate pzero 65 | pyinstaller pzero-linux.spec 66 | ls 67 | 68 | - name: Make executable - MacOS 69 | if: ${{ matrix.os == 'macos-latest' }} 70 | run: | 71 | conda activate pzero 72 | pyinstaller pzero-macos.spec 73 | ls 74 | 75 | - name: check path 76 | run: | 77 | cd installers 78 | ls 79 | 80 | - name: Upload Windows Build 81 | if: ${{ matrix.os == 'windows-latest' }} 82 | uses: actions/upload-artifact@v3 83 | with: 84 | name: ${{ matrix.os }} build 85 | path: installers/PZero_Windows/pzero.exe 86 | 87 | 88 | - name: Upload Ubuntu Build 89 | if: ${{ matrix.os == 'ubuntu-latest' }} 90 | uses: actions/upload-artifact@v3 91 | with: 92 | name: ${{ matrix.os }} build 93 | path: installers/PZero_Linux/pzero 94 | 95 | 96 | - name: Upload MacOs Build 97 | if: ${{ matrix.os == 'macos-latest' }} 98 | uses: actions/upload-artifact@v3 99 | with: 100 | name: ${{ matrix.os }} build 101 | path: installers/PZero_Darwin/pzero -------------------------------------------------------------------------------- /icons/TakeScreenshot.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 93 | -------------------------------------------------------------------------------- /icons/ResampleNumber.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | n 84 | -------------------------------------------------------------------------------- /style/imgs/uparrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 61 | 73 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 91 | 96 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /style/imgs/downarrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 61 | 73 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 91 | 96 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /style/imgs/leftarrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 61 | 73 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 91 | 96 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /icons/SimplifyLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 61 | -------------------------------------------------------------------------------- /pzero/imports/stl2vtk.py: -------------------------------------------------------------------------------- 1 | """stl2py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from vtk import vtkSTLWriter 5 | 6 | from pzero.entities_factory import TriSurf 7 | 8 | 9 | def vtk2stl(self=None, out_dir_name=None): 10 | """Exports all triangulated surfaces to a collection of STL surfaces.""" 11 | # Create STL writer. 12 | stl_writer = vtkSTLWriter() 13 | stl_writer.SetFileTypeToASCII() 14 | # Loop for each entity. 15 | for uid in self.geol_coll.df["uid"]: 16 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 17 | header = ( 18 | uid 19 | + "_" 20 | + self.geol_coll.df.loc[self.geol_coll.df["uid"] == uid, "name"].values[ 21 | 0 22 | ] 23 | ) 24 | out_file_name = ( 25 | str(out_dir_name) 26 | + "/" 27 | + uid 28 | + "_" 29 | + self.geol_coll.df.loc[self.geol_coll.df["uid"] == uid, "name"].values[ 30 | 0 31 | ] 32 | + ".stl" 33 | ) 34 | stl_writer.SetFileName(out_file_name) 35 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 36 | stl_writer.SetInputData(vtk_entity) 37 | stl_writer.SetHeader(header) 38 | stl_writer.Write() 39 | for uid in self.boundary_coll.df["uid"]: 40 | if isinstance(self.boundary_coll.get_uid_vtk_obj(uid), TriSurf): 41 | header = ( 42 | uid 43 | + "_" 44 | + self.boundary_coll.df.loc[ 45 | self.boundary_coll.df["uid"] == uid, "name" 46 | ].values[0] 47 | ) 48 | out_file_name = ( 49 | str(out_dir_name) 50 | + "/" 51 | + uid 52 | + "_" 53 | + self.boundary_coll.df.loc[ 54 | self.boundary_coll.df["uid"] == uid, "name" 55 | ].values[0] 56 | + ".stl" 57 | ) 58 | stl_writer.SetFileName(out_file_name) 59 | vtk_entity = self.boundary_coll.get_uid_vtk_obj(uid) 60 | stl_writer.SetInputData(vtk_entity) 61 | stl_writer.SetHeader(header) 62 | stl_writer.Write() 63 | print("All TSurfs saved.") 64 | 65 | 66 | def vtk2stl_dilation(self=None, out_dir_name=None, tol=1.0): 67 | """Apply dilation then exports all triangulated surfaces to a collection of STL surfaces.""" 68 | # Create STL writer. 69 | stl_writer = vtkSTLWriter() 70 | stl_writer.SetFileTypeToASCII() 71 | # Loop for each entity. 72 | for uid in self.geol_coll.df["uid"]: 73 | if isinstance(self.geol_coll.get_uid_vtk_obj(uid), TriSurf): 74 | header = ( 75 | uid 76 | + "_" 77 | + self.geol_coll.df.loc[self.geol_coll.df["uid"] == uid, "name"].values[ 78 | 0 79 | ] 80 | ) 81 | out_file_name = ( 82 | str(out_dir_name) 83 | + "/" 84 | + uid 85 | + "_" 86 | + self.geol_coll.df.loc[self.geol_coll.df["uid"] == uid, "name"].values[ 87 | 0 88 | ] 89 | + ".stl" 90 | ) 91 | stl_writer.SetFileName(out_file_name) 92 | vtk_entity = self.geol_coll.get_uid_vtk_obj(uid) 93 | vtk_entity_dilated = vtk_entity.boundary_dilation(tol=tol) 94 | try: 95 | stl_writer.SetInputData(vtk_entity_dilated) 96 | stl_writer.SetHeader(header) 97 | stl_writer.Write() 98 | except: 99 | pass 100 | print("All TSurfs saved.") 101 | -------------------------------------------------------------------------------- /style/imgs/rightarrow2.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 61 | 73 | 74 | 76 | 77 | 79 | image/svg+xml 80 | 82 | 83 | 84 | 85 | 86 | 91 | 96 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /old_tests/test_helpers/test_helper_dialogs.py: -------------------------------------------------------------------------------- 1 | from pzero.helpers.helper_dialogs import progress_dialog 2 | import pytest 3 | 4 | """ 5 | def test_option_dialogs(self, qtbot): 6 | title = "test_dialog" 7 | message = "this is a test message" 8 | res = options_dialog(title=title, message=message, yes_role="yes", no_role="no") 9 | 10 | qtbot.mouseClick() 11 | 12 | # if pressed no res == 1, otherwise res == 0 13 | assert res == 1 14 | """ 15 | 16 | 17 | # Testing the class progress_dialog() 18 | class TestProgressDialog: 19 | 20 | # testing if the initial dialog values are correct 21 | @pytest.fixture 22 | def test_init_dialog(self, qtbot): 23 | max_value = 1 24 | title = "Title_test" 25 | label = "Saving test" 26 | 27 | progress_dialog_instance = progress_dialog( 28 | max_value=max_value, 29 | title_txt=title, 30 | label_txt=label, 31 | cancel_txt=None, 32 | parent=self, 33 | ) 34 | 35 | assert ( 36 | progress_dialog_instance.value() == -1 37 | and progress_dialog_instance.maximum() == max_value 38 | and progress_dialog_instance.windowTitle() == title 39 | and progress_dialog_instance.labelText() == label 40 | ) 41 | 42 | # Testing the add_one function 43 | @pytest.fixture 44 | def test_add_one(self, qtbot): 45 | max_value = 5000 46 | title = "Title_test" 47 | label = "Saving test" 48 | 49 | progress_dialog_instance = progress_dialog( 50 | max_value=max_value, 51 | title_txt=title, 52 | label_txt=label, 53 | cancel_txt=None, 54 | parent=self, 55 | ) 56 | 57 | for i in range(max_value): 58 | progress_dialog_instance.add_one() 59 | 60 | # we use max_value -1 because the value in the dialog starts with -1 61 | assert progress_dialog_instance.value() == max_value - 1 62 | 63 | # Testing was_canceled in the progress dialog 64 | @pytest.fixture 65 | def test_was_canceled(self, qtbot): 66 | max_value = 1 67 | title = "Title_test" 68 | label = "Saving test" 69 | cancel_button_text = "test delete me" 70 | 71 | progress_dialog_instance = progress_dialog( 72 | max_value=max_value, 73 | title_txt=title, 74 | label_txt=label, 75 | cancel_txt=cancel_button_text, 76 | parent=self, 77 | ) 78 | 79 | assert progress_dialog_instance.wasCanceled() is False 80 | 81 | # Testing with calling the cancel button in the progress dialog 82 | @pytest.fixture 83 | def test_was_canceled_true(self, qtbot): 84 | max_value = 1 85 | title = "Title_test" 86 | label = "Saving test" 87 | cancel_button_text = "test delete me" 88 | 89 | progress_dialog_instance = progress_dialog( 90 | max_value=max_value, 91 | title_txt=title, 92 | label_txt=label, 93 | cancel_txt=cancel_button_text, 94 | parent=self, 95 | ) 96 | progress_dialog_instance.cancel() 97 | 98 | assert progress_dialog_instance.wasCanceled() is True 99 | 100 | # Testing change in the dialog 101 | @pytest.fixture 102 | def test_change_dialog_label(self, qtbot): 103 | max_value = 5000 104 | title = "Title_test" 105 | label = "Saving test" 106 | change_label = "new_test_label" 107 | 108 | progress_dialog_instance = progress_dialog( 109 | max_value=max_value, 110 | title_txt=title, 111 | label_txt=label, 112 | cancel_txt=None, 113 | parent=self, 114 | ) 115 | progress_dialog_instance.setLabelText(change_label) 116 | 117 | assert progress_dialog_instance.labelText() == change_label 118 | -------------------------------------------------------------------------------- /icons/CopyKink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 76 | -------------------------------------------------------------------------------- /pzero/collections/dom_collection.py: -------------------------------------------------------------------------------- 1 | """dom_collection.py 2 | PZero© Andrea Bistacchi""" 3 | 4 | from .DIM_collection import DIMCollection 5 | 6 | 7 | class DomCollection(DIMCollection): 8 | """Collection for all DOM entities and their metadata.""" 9 | 10 | def __init__(self, parent=None, *args, **kwargs): 11 | super(DomCollection, self).__init__(parent, *args, **kwargs) 12 | # Initialize properties required by the abstract superclass. 13 | self.entity_dict = { 14 | "uid": "", 15 | "name": "undef", 16 | "scenario": "undef", 17 | "parent_uid": "", # this is the uid of the cross section for "XsVertexSet", "XsPolyLine", and "XsImage", empty for all others 18 | "textures": [], 19 | "topology": "undef", 20 | "vtk_obj": None, 21 | "properties_names": [], 22 | "properties_components": [], 23 | } 24 | 25 | self.entity_dict_types = { 26 | "uid": str, 27 | "name": str, 28 | "scenario": str, 29 | "parent_uid": str, 30 | "textures": str, 31 | "topology": str, 32 | "vtk_obj": object, 33 | "properties_names": list, 34 | "properties_components": list, 35 | } 36 | 37 | self.valid_topologies = ["DEM", "TSDom", "PCDom"] 38 | 39 | self.collection_name = "dom_coll" 40 | 41 | self.default_colormap = "terrain" 42 | 43 | self.initialize_df() 44 | 45 | # =================================== Obligatory methods =========================================== 46 | 47 | def get_uid_legend(self, uid: str = None) -> dict: 48 | """Get legend for a particular uid.""" 49 | legend_dict = self.parent.others_legend_df.loc[ 50 | self.parent.others_legend_df["other_collection"] == "DOM" 51 | ].to_dict("records") 52 | return legend_dict[0] 53 | 54 | # =================================== Additional methods =========================================== 55 | 56 | def get_uid_textures(self, uid=None): 57 | """Get value(s) stored in dataframe (as pointer) from uid.""" 58 | return self.df.loc[self.df["uid"] == uid, "textures"].values[0] 59 | 60 | def set_uid_textures(self, uid=None, textures=None): 61 | """Set value(s) stored in dataframe (as pointer) from uid..""" 62 | self.df.loc[self.df["uid"] == uid, "textures"] = textures 63 | 64 | def add_map_texture_to_dom(self, dom_uid=None, map_image_uid=None): 65 | """Add a map texture to a DOM.""" 66 | row = self.df[self.df["uid"] == dom_uid].index.values[0] 67 | if map_image_uid not in self.df.at[row, "textures"]: 68 | self.get_uid_vtk_obj(dom_uid).add_texture( 69 | map_image=self.parent.image_coll.get_uid_vtk_obj(map_image_uid), 70 | map_image_uid=map_image_uid, 71 | ) 72 | self.df.at[row, "textures"].append(map_image_uid) 73 | self.parent.signals.metadata_modified.emit([dom_uid], self) 74 | 75 | def remove_map_texture_from_dom(self, dom_uid=None, map_image_uid=None): 76 | """Remove a map texture from a DOM.""" 77 | row = self.df[self.df["uid"] == dom_uid].index.values[0] 78 | if map_image_uid in self.df.at[row, "textures"]: 79 | self.get_uid_vtk_obj(dom_uid).remove_texture(map_image_uid=map_image_uid) 80 | self.df.at[row, "textures"].remove(map_image_uid) 81 | self.parent.signals.data_keys_removed.emit([dom_uid], self) 82 | # print("self.parent.signals.data_keys_removed.emit([dom_uid], self)") 83 | # self.parent.signals.metadata_modified.emit([dom_uid], self) 84 | # print("self.parent.signals.metadata_modified.emit([dom_uid], self)") 85 | 86 | def set_active_texture_on_dom(self, dom_uid=None, map_image_uid=None): 87 | """Set active texture on a DOM.""" 88 | self.get_uid_vtk_obj(dom_uid).set_active_texture(map_image_uid=map_image_uid) 89 | -------------------------------------------------------------------------------- /pzero/ui/navigator_window_ui.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | ################################################################################ 4 | ## Form generated from reading UI file 'navigator_windowtkAWbV.ui' 5 | ## 6 | ## Created by: Qt User Interface Compiler version 6.7.3 7 | ## 8 | ## WARNING! All changes made in this file will be lost when recompiling UI file! 9 | ################################################################################ 10 | 11 | from PySide6.QtCore import ( 12 | QCoreApplication, 13 | QDate, 14 | QDateTime, 15 | QLocale, 16 | QMetaObject, 17 | QObject, 18 | QPoint, 19 | QRect, 20 | QSize, 21 | QTime, 22 | QUrl, 23 | Qt, 24 | ) 25 | from PySide6.QtGui import ( 26 | QBrush, 27 | QColor, 28 | QConicalGradient, 29 | QCursor, 30 | QFont, 31 | QFontDatabase, 32 | QGradient, 33 | QIcon, 34 | QImage, 35 | QKeySequence, 36 | QLinearGradient, 37 | QPainter, 38 | QPalette, 39 | QPixmap, 40 | QRadialGradient, 41 | QTransform, 42 | ) 43 | from PySide6.QtWidgets import ( 44 | QApplication, 45 | QGridLayout, 46 | QLabel, 47 | QMainWindow, 48 | QPushButton, 49 | QSizePolicy, 50 | QVBoxLayout, 51 | QWidget, 52 | ) 53 | 54 | 55 | class Ui_NavWindow(object): 56 | def setupUi(self, NavWindow): 57 | if not NavWindow.objectName(): 58 | NavWindow.setObjectName("NavWindow") 59 | NavWindow.resize(540, 95) 60 | NavWindow.setMinimumSize(QSize(540, 90)) 61 | NavWindow.setMaximumSize(QSize(562, 95)) 62 | self.centralwidget = QWidget(NavWindow) 63 | self.centralwidget.setObjectName("centralwidget") 64 | self.verticalLayout = QVBoxLayout(self.centralwidget) 65 | self.verticalLayout.setObjectName("verticalLayout") 66 | self.NavGrid = QWidget(self.centralwidget) 67 | self.NavGrid.setObjectName("NavGrid") 68 | sizePolicy = QSizePolicy( 69 | QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Preferred 70 | ) 71 | sizePolicy.setHorizontalStretch(0) 72 | sizePolicy.setVerticalStretch(0) 73 | sizePolicy.setHeightForWidth(self.NavGrid.sizePolicy().hasHeightForWidth()) 74 | self.NavGrid.setSizePolicy(sizePolicy) 75 | self.NavGrid.setMinimumSize(QSize(540, 45)) 76 | self.NavGrid.setMaximumSize(QSize(815, 65)) 77 | self.gridLayout = QGridLayout(self.NavGrid) 78 | self.gridLayout.setObjectName("gridLayout") 79 | self.ForwardButton = QPushButton(self.NavGrid) 80 | self.ForwardButton.setObjectName("ForwardButton") 81 | 82 | self.gridLayout.addWidget(self.ForwardButton, 0, 2, 1, 1) 83 | 84 | self.BackButton = QPushButton(self.NavGrid) 85 | self.BackButton.setObjectName("BackButton") 86 | 87 | self.gridLayout.addWidget(self.BackButton, 0, 0, 1, 1) 88 | 89 | self.SectionLabel = QLabel(self.NavGrid) 90 | self.SectionLabel.setObjectName("SectionLabel") 91 | self.SectionLabel.setLayoutDirection(Qt.LayoutDirection.LeftToRight) 92 | self.SectionLabel.setAlignment(Qt.AlignmentFlag.AlignCenter) 93 | 94 | self.gridLayout.addWidget(self.SectionLabel, 0, 1, 1, 1) 95 | 96 | self.verticalLayout.addWidget(self.NavGrid) 97 | 98 | NavWindow.setCentralWidget(self.centralwidget) 99 | 100 | self.retranslateUi(NavWindow) 101 | 102 | QMetaObject.connectSlotsByName(NavWindow) 103 | 104 | # setupUi 105 | 106 | def retranslateUi(self, NavWindow): 107 | NavWindow.setWindowTitle( 108 | QCoreApplication.translate("NavWindow", "Navigator", None) 109 | ) 110 | self.ForwardButton.setText(QCoreApplication.translate("NavWindow", ">>", None)) 111 | self.BackButton.setText(QCoreApplication.translate("NavWindow", "<<", None)) 112 | self.SectionLabel.setText("") 113 | 114 | # retranslateUi 115 | -------------------------------------------------------------------------------- /icons/SectionFromAzimuth.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 78 | -------------------------------------------------------------------------------- /pzero/README.md: -------------------------------------------------------------------------------- 1 | # PZero Core Modules 2 | 3 | This folder contains the core Python modules for the PZero project. These files implement the main logic, data structures, and algorithms used throughout the application. 4 | 5 | ## File Overview 6 | 7 | - `project_window.py` 8 | Main window class for the PZero application, integrating the UI and core logic. Handles project creation, opening, saving, import/export of various geological data formats, entity management, and connections to all main collections and legends. Provides the central hub for user interaction and workflow orchestration. 9 | 10 | - `entities_factory.py` 11 | Defines the main geometric and geological entity classes used throughout the project, including polylines, triangulated surfaces, point clouds, voxets, images, wells, and related data structures. Provides Pythonic wrappers around VTK and PyVista objects, exposing data as NumPy arrays and offering methods for deep copying, property management, and geometric operations. Supports both geological and non-geological objects, and integrates with orientation analysis utilities. 12 | 13 | - `legend_manager.py` 14 | Manages the legend system for geological, fluid, background, well, and other entity collections. Provides utilities for updating the legend widget, handling color, line thickness, point size, opacity, and sequence changes, and synchronizing legend data with the UI and project dataframes. Integrates with PySide6 and pandas for interactive legend editing and visualization. 15 | 16 | - `properties_manager.py` 17 | Manages property colormaps and legends for geological, DOM, mesh3d, and related collections. Provides utilities for updating property colormap tables, synchronizing with project data, and handling user interactions for property visualization. Integrates with PySide6, matplotlib, colorcet, cmocean, and PyVista for colormap management and display. 18 | 19 | - `two_d_lines.py` 20 | Functions and tools for creating, editing, and manipulating 2D lines, including digitizing, editing, splitting, merging, snapping, and resampling lines. Integrates with the application's map and cross-section views. 21 | 22 | - `three_d_surfaces.py` 23 | Tools and algorithms for creating, editing, and processing 3D surfaces and meshes. Includes Delaunay triangulation, Poisson surface reconstruction, implicit modeling with LoopStructural, surface smoothing, mesh decimation, subdivision, intersection, projection, and retopology. Integrates with VTK, PyVista, and LoopStructural libraries. 24 | 25 | - `point_clouds.py` 26 | Functions and tools for processing, segmenting, and analyzing point cloud data. Includes utilities for normal calculation, region extraction, segmentation by dip and dip direction, decimation, cutting, calibration, and conversion to geological features. Integrates with VTK, PyVista, matplotlib, seaborn, and NumPy for advanced point cloud operations and visualization. 27 | 28 | - `orientation_analysis.py` 29 | Functions for orientation analysis, including conversions between geological orientation measurements (strike, dip, dip direction, plunge, trend), calculation of normal and lineation vectors, and utilities for setting normals on geological entities and point clouds. Integrates with NumPy and project-specific entity classes. 30 | 31 | ## Sub-modules 32 | 33 | - `collections/` 34 | Manages grouped data structures and collections of geological entities, such as layers, surfaces, wells, and other domain-specific objects. 35 | 36 | - `helpers/` 37 | Utility modules for dialogs, widgets, and general helper functions used across the application. 38 | 39 | - `imports/` 40 | Modules and utilities for importing, parsing, and converting external geological data formats into the application's internal structures. 41 | 42 | - `processing/` 43 | Implements core data processing algorithms, computational routines, and utilities for geological modeling, mesh operations, and advanced analysis. 44 | 45 | - `ui/` 46 | Contains user interface components, custom widgets, dialogs, and Qt Designer `.ui` files used to build the application's graphical interface. 47 | 48 | - `views/` 49 | Contains the main view classes for the application's GUI, including map and cross-section windows. 50 | -------------------------------------------------------------------------------- /pzero/imports/README.md: -------------------------------------------------------------------------------- 1 | # Imports 2 | 3 | This folder contains modules for importing, exporting, and converting various data formats (such as well data, mesh data, images, triangulated surfaces, GIS shapefiles, seismic SEG-Y volumes, PyVista-supported formats, PLY surfaces, point clouds, OBJ surfaces, LandXML surfaces, glTF/GLB models, GOCAD ASCII files, DXF files, DEM rasters, Cesium 3D Tiles, and abstract base classes) into VTK objects and internal representations for the PZero project. 4 | 5 | ## File Overview 6 | 7 | - `well2vtk.py` 8 | Converts well data from Excel and other formats into VTK objects and PZero entities. 9 | **Main function:** 10 | - `well2vtk(self, path=None)` 11 | 12 | - `mesh2vtk.py` 13 | Imports mesh data (e.g., from GOCAD or other sources) and converts them into VTK mesh objects. 14 | 15 | - `image2vtk.py` 16 | Imports image data and converts them into VTK image objects. 17 | 18 | - `stl2vtk.py` 19 | Exports triangulated surfaces (TSurfs) to STL files, with optional boundary dilation. 20 | **Main functions:** 21 | - `vtk2stl(self, out_dir_name)` 22 | - `vtk2stl_dilation(self, out_dir_name, tol=1.0)` 23 | 24 | - `shp2vtk.py` 25 | Imports points and polylines from ESRI Shapefiles (SHP) and other GIS formats. 26 | **Main function:** 27 | - `shp2vtk(self, in_file_name, collection)` 28 | 29 | - `segy2vtk.py` 30 | Imports seismic SEG-Y volumes and converts them into VTK structured grids. 31 | **Main functions:** 32 | - `segy2vtk(self, in_file_name)` 33 | - `read_segy_file(in_file_name)` 34 | 35 | - `pyvista2vtk.py` 36 | Imports various file formats supported by PyVista and adds them as VTK polydata entities. 37 | **Main function:** 38 | - `pyvista2vtk(self)` 39 | 40 | - `ply2vtk.py` 41 | Exports all triangulated surfaces (TSurfs) to PLY files with color information. 42 | **Main function:** 43 | - `vtk2ply(self, out_dir_name)` 44 | 45 | - `pc2vtk.py` 46 | Imports point cloud data from PLY, LAS/LAZ, or CSV files and converts them into VTK point cloud objects. 47 | **Main function:** 48 | - `pc2vtk(in_file_name, col_names, row_range, header_row, usecols, delimiter, self=None)` 49 | 50 | - `obj2vtk.py` 51 | Exports all triangulated surfaces (TSurfs) to OBJ files. 52 | **Main function:** 53 | - `vtk2obj(self, out_dir_name)` 54 | 55 | - `lxml2vtk.py` 56 | Exports triangulated surfaces (TSurfs) to LandXML files and provides a placeholder for LandXML import. 57 | **Main functions:** 58 | - `vtk2lxml(self, out_dir_name=None)` 59 | - `lxml2vtk(self, input_path=None)` 60 | 61 | - `gltf2vtk.py` 62 | Exports all triangulated surfaces (TSurfs) to a GLTF binary file (.glb) using VTK’s GLTF writer. 63 | **Main function:** 64 | - `vtk2gltf(self, out_dir_name=None)` 65 | 66 | - `gocad2vtk.py` 67 | Imports and exports GOCAD ASCII files (VSet, PLine, TSurf) as VTK entities for geology, cross-sections, and boundaries. 68 | **Main functions:** 69 | - `gocad2vtk(self, in_file_name, uid_from_name=None)` 70 | - `gocad2vtk_section(self, in_file_name, ...)` 71 | - `gocad2vtk_boundary(self, in_file_name, uid_from_name=None)` 72 | - `vtk2gocad(self, out_file_name)` 73 | 74 | - `dxf2vtk.py` 75 | Exports all triangulated surfaces, boundaries, and wells to DXF files as 3DFACE objects and 3D polylines, including CSV exports of coordinates. 76 | **Main function:** 77 | - `vtk2dxf(self, out_dir_name=None)` 78 | 79 | - `dem2vtk.py` 80 | Imports Digital Elevation Model (DEM) raster files (e.g., GeoTIFF) and adds them as VTK structured grids to the project. 81 | **Main function:** 82 | - `dem2vtk(self, in_file_name, collection)` 83 | 84 | - `cesium2vtk.py` 85 | Exports all triangulated surfaces to a Cesium 3D Tiles collection (GLTF binary surfaces, .glb) using VTK’s Cesium3DTilesWriter. 86 | **Main function:** 87 | - `vtk2cesium(self, out_dir_name=None)` 88 | 89 | - `AbstractImporter.py` 90 | In theory should provide abstract base classes for import/export operations, ensuring a consistent interface for all importers and exporters, but this is not yet implemented. 91 | **Main class:** 92 | - `BaseIO`: Abstract base class with `import_from_file()` and `output_to_file()` methods to be implemented by subclasses. 93 | -------------------------------------------------------------------------------- /icons/CleanIntersections.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 51 | --------------------------------------------------------------------------------