Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
696 changes: 696 additions & 0 deletions loopstructural/gui/interpolation_dialog.py

Large diffs are not rendered by default.

493 changes: 493 additions & 0 deletions loopstructural/gui/interpolation_dialog.ui

Large diffs are not rendered by default.

158 changes: 158 additions & 0 deletions loopstructural/main/interpolator_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
"""Manager for storing and retrieving LoopStructural interpolators."""

from typing import Dict, List
import dill as pickle


class InterpolatorManager:
"""Manages a collection of LoopStructural interpolators with unique keys.

This class provides storage, retrieval, and persistence for interpolator
objects, allowing users to build multiple interpolators and reference them
by name for later evaluation.
"""

def __init__(self):
"""Initialize the interpolator manager with an empty storage."""
self._interpolators: Dict[str, any] = {}

def add_interpolator(self, name: str, interpolator) -> None:
"""Add or update an interpolator with the given name.

Parameters
----------
name : str
Unique identifier for the interpolator
interpolator : any
The interpolator object to store

Raises
------
ValueError
If name is empty or None
"""
if not name or not name.strip():
raise ValueError("Interpolator name cannot be empty")

self._interpolators[name.strip()] = interpolator

def get_interpolator(self, name: str):
"""Retrieve an interpolator by name.

Parameters
----------
name : str
Name of the interpolator to retrieve

Returns
-------
any
The interpolator object, or None if not found
"""
return self._interpolators.get(name.strip())

def remove_interpolator(self, name: str) -> bool:
"""Remove an interpolator by name.

Parameters
----------
name : str
Name of the interpolator to remove

Returns
-------
bool
True if removed, False if not found
"""
if name.strip() in self._interpolators:
del self._interpolators[name.strip()]
return True
return False

def list_interpolators(self) -> List[str]:
"""Get a list of all interpolator names.

Returns
-------
List[str]
List of interpolator names
"""
return list(self._interpolators.keys())

def has_interpolator(self, name: str) -> bool:
"""Check if an interpolator exists with the given name.

Parameters
----------
name : str
Name to check

Returns
-------
bool
True if exists, False otherwise
"""
return name.strip() in self._interpolators

def clear(self) -> None:
"""Remove all interpolators."""
self._interpolators.clear()

def save_interpolator(self, name: str, filepath: str) -> None:
"""Save a specific interpolator to a pickle file.

Parameters
----------
name : str
Name of the interpolator to save
filepath : str
Path where to save the pickle file

Raises
------
ValueError
If interpolator with given name doesn't exist
IOError
If file cannot be written
"""
interpolator = self.get_interpolator(name)
if interpolator is None:
raise ValueError(f"Interpolator '{name}' not found")

with open(filepath, 'wb') as f:
pickle.dump(interpolator, f)

def load_interpolator(self, name: str, filepath: str) -> None:
"""Load an interpolator from a pickle file and store it with the given name.

Parameters
----------
name : str
Name to assign to the loaded interpolator
filepath : str
Path to the pickle file

Raises
------
ValueError
If name is empty
IOError
If file cannot be read
"""
if not name or not name.strip():
raise ValueError("Interpolator name cannot be empty")

with open(filepath, 'rb') as f:
interpolator = pickle.load(f)

self.add_interpolator(name, interpolator)

def count(self) -> int:
"""Get the number of stored interpolators.

Returns
-------
int
Number of interpolators
"""
return len(self._interpolators)
22 changes: 22 additions & 0 deletions loopstructural/plugin_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,10 @@
)
from loopstructural.gui.dlg_settings import PlgOptionsFactory
from loopstructural.gui.loop_widget import LoopWidget
from loopstructural.gui.interpolation_dialog import InterpolationDialog
from loopstructural.main.data_manager import ModellingDataManager
from loopstructural.main.model_manager import GeologicalModelManager
from loopstructural.main.interpolator_manager import InterpolatorManager
from loopstructural.processing.provider import LoopstructuralProvider
from loopstructural.toolbelt import PlgLogger, PlgOptionsManager

Expand Down Expand Up @@ -76,6 +78,7 @@ def __init__(self, iface: QgisInterface):
mapCanvas=self.iface.mapCanvas(), logger=self.log, project=QgsProject.instance()
)
self.model_manager = GeologicalModelManager()
self.interpolator_manager = InterpolatorManager()
self.data_manager.set_model_manager(self.model_manager)

def injectLogHandler(self):
Expand Down Expand Up @@ -137,9 +140,16 @@ def initGui(self):
self.tr("LoopStructural Visualisation"),
self.iface.mainWindow(),
)
self.action_interpolation = QAction(
QIcon(os.path.dirname(__file__) + "/icon.png"),
self.tr("LoopStructural Interpolation"),
self.iface.mainWindow(),
)

self.toolbar.addAction(self.action_modelling)
self.toolbar.addAction(self.action_interpolation)
# -- Menu
self.iface.addPluginToMenu(__title__, self.action_interpolation)
self.iface.addPluginToMenu(__title__, self.action_settings)
self.iface.addPluginToMenu(__title__, self.action_help)

Expand Down Expand Up @@ -217,6 +227,7 @@ def initGui(self):
self.action_visualisation.triggered.connect(
self.visualisation_dockwidget.toggleViewAction().trigger
)
self.action_interpolation.triggered.connect(self._show_interpolation_dialog)
# Store reference to main dock as None for unload compatibility
self.loop_dockwidget = None
else:
Expand Down Expand Up @@ -251,6 +262,7 @@ def initGui(self):

# -- Connect actions
self.action_modelling.triggered.connect(self.loop_dockwidget.toggleViewAction().trigger)
self.action_interpolation.triggered.connect(self._show_interpolation_dialog)

# Store references to separate docks as None for unload compatibility
self.modelling_dockwidget = None
Expand All @@ -271,6 +283,15 @@ def tr(self, message: str) -> str:
"""
return QCoreApplication.translate(self.__class__.__name__, message)

def _show_interpolation_dialog(self):
"""Show the interpolation dialog."""
dialog = InterpolationDialog(
parent=self.iface.mainWindow(),
interpolator_manager=self.interpolator_manager,
logger=self.log
)
dialog.exec_()

def unload(self):
"""Clean up when plugin is disabled or uninstalled."""
# -- Clean up dock widgets
Expand All @@ -287,6 +308,7 @@ def unload(self):
# -- Clean up menu
self.iface.removePluginMenu(__title__, self.action_help)
self.iface.removePluginMenu(__title__, self.action_settings)
self.iface.removePluginMenu(__title__, self.action_interpolation)
# self.iface.removeMenu(self.menu)
# -- Clean up preferences panel in QGIS settings
self.iface.unregisterOptionsWidgetFactory(self.options_factory)
Expand Down
Loading