"""
Author: HECE - University of Liege, Pierre Archambeau
Date: 2024
Copyright (c) 2024 University of Liege. All rights reserved.
This script and its content are protected by copyright law. Unauthorized
copying or distribution of this file, via any medium, is strictly prohibited.
"""
from __future__ import annotations
import logging
import numpy as np
import numpy.ma as ma
from typing import Literal, TYPE_CHECKING
from enum import Enum
try:
from OpenGL.GL import *
except ImportError:
pass
try:
import wx
except ImportError:
pass
from ..PyTranslate import _
from ._header_wolf import header_wolf
from ..PyVertexvectors import vector, zone
if TYPE_CHECKING:
from ._base import WolfArrayModel as WolfArray
from ._mb import WolfArrayMB
[docs]
class StorageMode(Enum):
""" Storage mode for selections in WolfArray """
[docs]
class SelectionData():
"""
User-selected data in a WolfArray
Contains two storage elements :
- myselection
- selections( dict): Stored selection(s) to be used, for example, in a spatial interpolation operation.
These selections are only lost in the event of a general reset.
The selected nodes are stored using their "world" spatial coordinates so that they can be easily transferred to other objects.
The "myselection" can be stored in two modes :
- LIST: 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) for all nodes and excluded nodes
- ARRAY: np.ndarray of selected nodes (boolean array) with shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
The boolean array is True if the node is selected, False if not selected.
The selection can be converted from one mode to another automatically based on the number of nodes in the array.
If the number of nodes exceeds a threshold (default is 100,000), the selection is stored as an ARRAY.
Otherwise, it is stored as a LIST.
The selection can be forced to a specific storage mode using the `force_storage_mode` method.
"""
# 'all' or list of (x, y) coordinates or a tuple of ('all', np.ndarray) for all nodes and excluded nodes
[docs]
_myselection:list[tuple[float, float]] | str | tuple[str, np.ndarray]
[docs]
selections: dict[str:dict['select':list[tuple[float, float]], 'idgllist':int, 'color':list[float]]]
def __init__(self, parent:"WolfArray", threshold_array_mode:int = 100_000) -> None:
""" Initialize the SelectionData object.
:param parent: The parent WolfArray object to which this selection data belongs.
:param threshold_array_mode: The threshold number of nodes to switch from LIST to ARRAY storage mode.
"""
self.parent = parent
[docs]
self.wx_exists = wx.GetApp() is not None
self._myselection = []
self.selections = {}
[docs]
self._boolarray: np.ndarray | None = None # boolean array for selection - True if selected, False if not selected
[docs]
self._storage_mode = StorageMode.LIST # 0: 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) for all nodes and excluded nodes, 1 for np.ndarray of selected nodes
[docs]
self.update_plot_selection = False # force to update OpenGL list if True
[docs]
self.hideselection = False
[docs]
self.numlist_select = 0 # OpenGL list index
[docs]
self.threshold_array_mode = threshold_array_mode # Default threshold for switching storage mode
[docs]
def _auto_storage_mode(self):
""" Choose the storage mode based on the number of nodes in the array """
if self.nb > self.threshold_array_mode:
_storage_mode = StorageMode.ARRAY
else:
_storage_mode = StorageMode.LIST
if self._storage_mode != _storage_mode:
self._convert_to_storage_mode(_storage_mode)
[docs]
def _convert_to_storage_mode(self, new_mode:StorageMode):
""" Convert the selection to the new storage mode.
:param new_mode: The new storage mode to convert to (StorageMode.LIST or StorageMode.ARRAY).
"""
logging.info('Switching storage mode to {}'.format(new_mode))
if self._storage_mode == StorageMode.LIST:
# Convert from 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) to np.ndarray
_bool_array = self._myselection_as_array
if self._myselection == ALL_SELECTED:
_bool_array[:,:] = True
elif isinstance(self._myselection, list):
if len(self._myselection) == 0:
_bool_array[:,:] = False
else:
_bool_array[:,:] = False
xy = np.asarray(self._myselection)
ij = self.parent.xy2ij_np(xy)
_bool_array[ij[:, 0], ij[:, 1]] = True
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
_bool_array[:,:] = True
_excluded_nodes = self._myselection[1]
if _excluded_nodes.size > 0:
_excluded_nodes = self.parent.xy2ij_np(_excluded_nodes)
_bool_array[_excluded_nodes[:, 0], _excluded_nodes[:, 1]] = False
self._myselection = []
elif self._storage_mode == StorageMode.ARRAY:
if self.is_all_selected():
self._myselection = ALL_SELECTED
else:
nb = self.nb
if nb == 0:
self._myselection = []
elif nb / max(self.parent.nbnotnull, 1) > 0.5:
ij_not_selected = np.argwhere(~self._myselection_as_array)
xy_not_selected = self.parent.ij2xy_np(ij_not_selected)
self._myselection = (ALL_SELECTED, xy_not_selected)
else:
ij_selected = np.argwhere(self._myselection_as_array)
xy_selected = self.parent.ij2xy_np(ij_selected)
self._myselection = list(map(tuple, xy_selected))
self._storage_mode = new_mode
[docs]
def force_storage_mode(self, new_mode:StorageMode):
""" Force the storage mode to the new mode.
:param new_mode: The new storage mode to force (StorageMode.LIST or StorageMode.ARRAY).
"""
if new_mode not in StorageMode:
logging.error('Invalid storage mode - must be LIST or ARRAY')
return
if self._storage_mode != new_mode:
self._convert_to_storage_mode(new_mode)
@property
[docs]
def myselection(self) -> list[tuple[float, float]] | str:
""" Current selection of nodes.
Returns:
- 'all' if all nodes are selected
- list of (x, y) coordinates if specific nodes are selected
"""
if self._storage_mode == StorageMode.LIST:
if self._myselection == ALL_SELECTED:
return ALL_SELECTED
elif isinstance(self._myselection, list):
return self._myselection
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
excluded_nodes = self._myselection[1]
if excluded_nodes.shape[0] == 0:
return ALL_SELECTED
else:
if self.parent.usemask:
# Return all nodes except the excluded ones
loc_mask = ~ self.parent.array.mask.copy()
else:
loc_mask = np.ones((self.parent.nbx, self.parent.nby), dtype=bool)
ij_excluded = self.parent.xy2ij_np(excluded_nodes)
loc_mask[ij_excluded[:, 0], ij_excluded[:, 1]] = False
ij = np.argwhere(loc_mask)
xy = self.parent.ij2xy_np(ij)
return list(map(tuple, xy)) # Convert to list of tuples
elif self._storage_mode == StorageMode.ARRAY:
if self.is_all_selected():
return ALL_SELECTED
else:
nbsel = np.count_nonzero(self._myselection_as_array)
if nbsel == 0:
return []
else:
# Convert boolean array to list of (x, y) coordinates
ij_selected = np.argwhere(self._myselection_as_array)
xy_selected = self.parent.ij2xy_np(ij_selected)
return list(map(tuple, xy_selected))
else:
logging.error('Invalid storage mode - must be LIST or ARRAY')
return []
@property
[docs]
def _myselection_as_array(self) -> np.ndarray:
""" Current selection of nodes as a numpy array.
Returns:
- np.ndarray of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
- True if the node is selected, False if not selected.
"""
if self._storage_mode == StorageMode.LIST:
if self._boolarray is not None:
return self._boolarray
self._boolarray = np.zeros((self.parent.nbx, self.parent.nby), dtype=bool)
if self._myselection == ALL_SELECTED:
if self.parent.usemask:
self._boolarray[:,:] = ~self.parent.array.mask[:,:]
else:
self._boolarray[:,:] = True
elif isinstance(self._myselection, list):
# Convert list of (x, y) coordinates to boolean array
if len(self._myselection) > 0:
xy= np.asarray(self._myselection)
ij = self.parent.xy2ij_np(xy)
self._boolarray[ij[:, 0], ij[:, 1]] = True
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
if self.parent.usemask:
self._boolarray[:,:] = ~self.parent.array.mask[:,:]
else:
self._boolarray[:,:] = True
# Exclude nodes from the second element of the tuple
excluded_nodes = self.myselection[1]
if excluded_nodes.size > 0:
excluded_ij = self.parent.xy2ij_np(excluded_nodes)
self._boolarray[excluded_ij[:, 0], excluded_ij[:, 1]] = False
elif self._storage_mode == StorageMode.ARRAY:
if self._boolarray is None:
self._boolarray = np.zeros((self.parent.nbx, self.parent.nby), dtype=bool)
return self._boolarray
@_myselection_as_array.setter
def _myselection_as_array(self, value: np.ndarray):
""" Set the current selection of nodes as a numpy array.
:param value: numpy array of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
True if the node is selected, False if not selected.
"""
if not isinstance(value, np.ndarray):
logging.error('Invalid value for myselection_as_array - must be a numpy array')
if value.shape != (self.parent.nbx, self.parent.nby):
logging.error('Invalid shape for myselection_as_array - must be ({}, {})'.format
(self.parent.nbx, self.parent.nby))
if self._boolarray is None:
self._boolarray = value.copy()
else:
self._boolarray[:,:] = value[:,:]
@property
[docs]
def myselection_npargwhere(self) -> np.ndarray:
""" Current selection of nodes as a numpy array using np.argwhere """
return np.argwhere(self._myselection_as_array)
@myselection.setter
def myselection(self, value: list[tuple[float, float]] | str | tuple[str, np.ndarray]):
""" Set the current selection of nodes.
:param value: 'all' to select all nodes, a list of (x, y) coordinates to select specific nodes,
or a tuple of ('all', np.ndarray) for all nodes and excluded nodes.
"""
if self._storage_mode == StorageMode.ARRAY:
# Convert to the new storage mode if necessary
if isinstance(value, list):
if len(value) == 0:
self._myselection_as_array[:,:] = False
else:
ij = self.parent.xy2ij_np(np.array(value))
self._myselection_as_array[:,:] = False
self._myselection_as_array[ij[:, 0], ij[:, 1]] = True
elif isinstance(value, str):
if value == ALL_SELECTED:
if self.parent.usemask:
self._myselection_as_array[:,:] = ~self.parent.array.mask[:,:]
else:
self._myselection_as_array[:,:] = True
else:
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates')
elif isinstance(value, tuple) and len(value) == 2 and value[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
if self.parent.usemask:
self._myselection_as_array[:,:] = ~self.parent.array.mask[:,:]
else:
self._myselection_as_array[:,:] = True
excluded_nodes = value[1]
if excluded_nodes.size > 0:
ij_excluded = self.parent.xy2ij_np(excluded_nodes)
self._myselection_as_array[ij_excluded[:, 0], ij_excluded[:, 1]] = False
else:
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates or a tuple of ("all", np.ndarray)')
elif self._storage_mode == StorageMode.LIST:
if isinstance(value, list):
if len(value) == 0:
self._myselection = []
else:
self._myselection = value
elif isinstance(value, str):
if value == ALL_SELECTED:
self._myselection = ALL_SELECTED
else:
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates')
elif isinstance(value, tuple) and len(value) == 2 and value[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
self._myselection = value
else:
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates or a tuple of ("all", np.ndarray)')
self.update_nb_nodes_selection()
[docs]
def is_all_selected(self) -> bool:
""" Check if all nodes are selected.
:return: True if all nodes are selected, False otherwise
"""
if self._storage_mode == StorageMode.LIST:
if self.myselection == ALL_SELECTED:
return True
elif isinstance(self.myselection, list):
if self.parent.usemask:
return len(self.myselection) == self.parent.nbnotnull
else :
return len(self.myselection) == self.parent.nbx * self.parent.nby
elif isinstance(self.myselection, tuple) and len(self.myselection) == 2 and self.myselection[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
if self.myselection[1].size == 0:
return True
return False
elif self._storage_mode == StorageMode.ARRAY:
if self.nb > 0:
# Check if all nodes are selected in the boolean array
if self.parent.usemask:
return np.all(self._myselection_as_array)
else:
return np.all(self._myselection_as_array == ~self.parent.array.mask)
else:
return False
[docs]
def set_selection_from_list_xy(self, xylist: list[tuple[float, float]]):
""" Set the current selection from a list of (x, y) coordinates.
Alias for myselection setter to set a list of (x, y) coordinates.
This will convert the list to the appropriate storage mode if necessary.
"""
self.myselection = xylist
@property
[docs]
def dx(self) -> float:
""" Resolution in x """
if self.parent is None:
return 0.
else:
return self.parent.dx
@property
[docs]
def dy(self) -> float:
""" Resolution in y """
if self.parent is None:
return 0.
else:
return self.parent.dy
@property
[docs]
def nb(self) -> int:
""" Number of selected nodes. """
if self._storage_mode == StorageMode.LIST:
if self._myselection == ALL_SELECTED:
if self.parent.usemask:
return self.parent.nbnotnull
else:
return self.parent.nbx * self.parent.nby
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
if self.parent.usemask:
return self.parent.nbnotnull - self._myselection[1].shape[0]
else:
return self.parent.nbx * self.parent.nby - self._myselection[1].shape[0]
else:
return len(self._myselection)
elif self._storage_mode == StorageMode.ARRAY:
return np.count_nonzero(self._myselection_as_array)
[docs]
def unmask_selection(self, resetplot:bool=True):
""" Unmask selection """
if self.nb == 0:
return
self.parent.array.mask[self.myselection_npargwhere] = False
if resetplot:
self.parent.reset_plot()
[docs]
def reset(self):
""" Reset the selection """
self.myselection = []
self._boolarray = None # Reset the boolean array
self._storage_mode = StorageMode.LIST # Reset the storage mode to LIST
[docs]
def reset_all(self):
""" Reset the selection """
self.reset()
self.selections = {}
[docs]
def get_string(self, which:str = None, all_memories:bool= False) -> str:
""" Get string of the current selection or of a stored one.
:param which: id/key of the selection to get the string for. If None, get the current selection.
:param all_memories: If True, include all stored selections in the output string.
:return: String representation of the selection.
"""
if which is None:
curlist = self.myselection
txt = 'X\tY\n'
else:
if str(which) in self.selections:
all_memories = False
curlist = self.selections[str(which)]['select']
txt = 'Selection {}\n'.format(which)
txt += 'Color : {}\n'.format(self.selections[str(which)]['color'])
txt += 'X\tY\n'
else:
logging.error(_('Selection {} does not exist').format(which))
return ''
if len(curlist) == 0:
return ''
if curlist == ALL_SELECTED:
txt += 'all\n'
return txt
for cur in curlist:
txt += str(cur[0]) + '\t' + str(cur[1]) + '\n'
txt += 'i\tj\t1-based indices\n'
for cur in curlist:
i,j = self.parent.get_ij_from_xy(cur[0], cur[1], aswolf=True)
txt += str(i) + '\t' + str(j) + '\n'
if all_memories:
for key, cur in self.selections.items():
txt += self.get_string(key)
return txt
[docs]
def get_script(self, which:int = None) -> str:
""" Get script of the current selection or of a stored one.
:param which: id/key of the selection to get the script for. If None, get the current selection.
:return: Script representation of the selection.
"""
if self.myselection == ALL_SELECTED:
logging.error(_('Cannot create script for "all" selection'))
return ''
txt = '# script adapted to a WolfGPU script\n'
txt += '# - (i,j) are 1-based for add_boundary_condition -- Do not forget to adapt BC type, value and direction or use BC Manager\n'
txt += '# - (i,j) are 0-based for infiltration zones\n\n'
if which is None:
curlist = self.myselection
idx = 0
else:
if str(which) in self.selections:
txt += '# Selection {}\n'.format(which)
curlist = self.selections[str(which)]['select']
idx = which
else:
logging.error(_('Selection {} does not exist').format(which))
return ''
if len(curlist) == 0:
return ''
txt += '# For boundary conditions :\n'
for cur in curlist:
i,j = self.parent.get_ij_from_xy(cur[0], cur[1], aswolf=True)
txt += "simul.add_boundary_condition(i={}, j={}, bc_type=BoundaryConditionsTypes.FROUDE_NORMAL, bc_value=.3, border=Direction.LEFT)".format(i, j) + '\n'
txt += '\n\n# For infiltration zones :\n'
for cur in curlist:
i,j = self.parent.get_ij_from_xy(cur[0], cur[1], aswolf=True)
txt += "infiltration_zones.array[{},{}]={}".format(i-1, j-1, idx) + '\n'
txt += '\n\n"""If needed, selection as string :\n'
txt += self.get_string(which)
txt += '"""\n'
return txt
[docs]
def copy_to_clipboard(self, which:int = None, typestr:Literal['string', 'script'] = 'string'):
""" Copy current selection to clipboard.
-- ONLY WORKS WITH wxPython --
:param which: id/key of the selection to copy. If None, copy the current selection.
:param typestr: Type of data to copy to clipboard - 'string' for string representation, 'script' for script representation.
"""
if self.wx_exists:
if wx.TheClipboard.Open():
wx.TheClipboard.Clear()
if typestr == 'string':
wx.TheClipboard.SetData(wx.TextDataObject(self.get_string(which)))
else:
wx.TheClipboard.SetData(wx.TextDataObject(self.get_script(which)))
wx.TheClipboard.Close()
else:
logging.warning(_('Cannot open the clipboard'))
else:
logging.warning(_('Clipboard is not available in this environment'))
[docs]
def reselect_from_memory(self, idx:list[str] = None):
"""
Reselect a stored selection
:param idx: id/key of the selection to reselect. If None, show a dialog to choose from available selections.
"""
if idx is None:
if not self.wx_exists:
logging.error(_('Cannot reselect from memory - no wxPython available'))
logging.error(_('Please use the method reselect_from_memory with a list of keys'))
return
keys = list(self.selections.keys())
keys = [cur for cur in keys if len(self.selections[cur]['select']) > 0]
with wx.MultiChoiceDialog(None, "Choose the memory to reselect", "Choices", keys+['All']) as dlg:
ret = dlg.ShowModal()
if ret == wx.ID_CANCEL:
return
idx = dlg.GetSelections()
if len(idx) == 0:
return
if len(idx) == 1 and idx[0] == len(keys):
idx = keys
elif len(idx) in idx:
idx = keys
else:
idx = [keys[i] for i in idx]
for curidx in idx:
if curidx in self.selections:
self.add_nodes_to_selection(self.selections[curidx]['select'])
# self.myselection += self.selections[curidx]['select']
else:
logging.error(_('Selection {} does not exist').format(idx))
# self.update_nb_nodes_selection()
self.parent.reset_plot()
[docs]
def move_selectionto(self, idx:str, color:list[float], resetplot:bool=True):
"""
Transfer current selection to dictionary
:param idx: id/key of the selection
:param color: color of the selection - list of 4 integers between 0 and 255
:param resetplot: if True, reset the plot after moving the selection
"""
assert len(color) == 4, "color must be a list of 4 integers between 0 and 255"
# force idx to be a string
idtxt = str(idx)
self.selections[idtxt] = {}
curdict = self.selections[idtxt]
curdict['select'] = self.myselection
curdict['idgllist'] = 0 # will be created later - index of OpenGL list
curdict['color'] = color
self.myselection = [] # reset current selection
# self.update_nb_nodes_selection()
if resetplot:
self.parent.reset_plot()
[docs]
def plot_selection(self):
""" Plot current selection and stored selections """
# Make a copy of the current value of the flag because it will be modified in the function _plot_selection
# So, if we want to update the plot, we need to apply the flag on each selection (current ans stored)
update_select = self.update_plot_selection
if len(self.selections) > 0:
# plot stored selections
for cur in self.selections.values():
if cur['select'] != ALL_SELECTED:
self.update_plot_selection = update_select
col = cur['color']
cur['idgllist'] = self._plot_selection(cur['select'],
(float(col[0]) / 255., float(col[1]) / 255.,
float(col[2]) / 255.),
cur['idgllist'])
if self._myselection != ALL_SELECTED and not isinstance(self._myselection, tuple):
# plot current selection in RED if not 'all'
if self.nb > 0:
self.update_plot_selection = update_select
self.numlist_select = self._plot_selection(self.myselection,
(1., 0., 0.),
self.numlist_select)
[docs]
def _plot_selection(self, curlist:list[float], color:list[float], loclist:int=0):
"""
Plot a selection
:param curlist: list of selected nodes -- list of tuples (x,y)
:param color: color of the selection - list of 3 floats between 0 and 1
:param loclist: index of OpenGL list
"""
#FIXME : Is it a good idea to use SHADER rather than list ?
if self.update_plot_selection:
dx = self.dx
dy = self.dy
if loclist != 0:
glDeleteLists(loclist, 1)
loclist = glGenLists(1)
glNewList(loclist, GL_COMPILE)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
glBegin(GL_QUADS)
for cursel in curlist:
x1 = cursel[0] - dx / 2.
x2 = cursel[0] + dx / 2.
y1 = cursel[1] - dy / 2.
y2 = cursel[1] + dy / 2.
glColor3f(color[0], color[1], color[2])
glVertex2f(x1, y1)
glVertex2f(x2, y1)
glVertex2f(x2, y2)
glVertex2f(x1, y2)
glEnd()
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
for cursel in curlist:
glBegin(GL_LINE_STRIP)
x1 = cursel[0] - dx / 2.
x2 = cursel[0] + dx / 2.
y1 = cursel[1] - dy / 2.
y2 = cursel[1] + dy / 2.
glColor3f(0., 1., 0.)
glVertex2f(x1, y1)
glVertex2f(x2, y1)
glVertex2f(x2, y2)
glVertex2f(x1, y2)
glVertex2f(x1, y1)
glEnd()
glEndList()
glCallList(loclist)
self.update_plot_selection = False
else:
if loclist != 0:
glCallList(loclist)
return loclist
[docs]
def add_node_to_selection(self, x:float, y:float, verif:bool=True):
"""
Add one coordinate to the selection
:param x: x coordinate - float
:param y: y coordinate - float
:param verif: if True, the coordinates are checked to avoid duplicates
"""
# on repasse par les i,j car les coordonnées transférées peuvent venir d'un click souris
# le but est de ne conserver que les coordonnées des CG de mailles
i, j = self.parent.get_ij_from_xy(x, y)
if self.parent.check_bounds_ij(i, j):
# if i>=0 and j>=0 and i<self.parent.nbx and j<self.parent.nby:
self._add_node_to_selectionij(i, j, verif)
return 0 # useful for MB
else:
return -1 # useful for MB
self.update_nb_nodes_selection()
[docs]
def add_nodes_to_selection(self, xy:list[float] | np.ndarray, verif:bool=True):
"""
Add multiple coordinates to the selection.
:param xy: list of coordinates or numpy array of shape (nb_nodes, 2) where nb_nodes is the number of nodes
or a numpy array of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
If a numpy array of shape (nbx, nby) is provided, it is assumed to be a boolean array where True indicates a selected node.
:param verif: if True, the coordinates are checked to avoid duplicates
"""
# on repasse par les i,j car les coordonnées transférées peuvent venir d'un click souris
# le but est de ne conserver que les coordonnées des CG de mailles
if isinstance(xy, np.ndarray):
self._add_nodes_to_selection_np(xy, verif)
else:
ij = [self.parent.get_ij_from_xy(x, y) for x, y in xy]
self._add_nodes_to_selectionij(ij, verif)
[docs]
def _add_node_to_selectionij(self, i:int, j:int, verif=True):
"""
Add one ij coordinate to the selection.
:param i: i coordinate
:param j: j coordinate
:param verif: if True, the coordinates are checked to avoid duplicates
"""
if self._storage_mode == StorageMode.ARRAY:
if verif:
self._boolarray[i, j] = not self._boolarray[i, j]
else:
self._boolarray[i, j] = True
elif self._storage_mode == StorageMode.LIST:
x1, y1 = self.parent.get_xy_from_ij(i, j)
if self._myselection == ALL_SELECTED:
# If all nodes are selected, we need to exclude the current node
self._myselection = (ALL_SELECTED, np.array([[x1, y1]])) # Exclude the current node
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
# check is the current node is in the excluded nodes
excluded_nodes = self._myselection[1].tolist()
if (x1, y1) in excluded_nodes:
# If the current node is in the excluded nodes, we remove it from the excluded nodes
ret = excluded_nodes.index((x1, y1))
excluded_nodes.pop(ret)
if len(excluded_nodes) == 0:
self._myselection = ALL_SELECTED # All nodes are selected again
else:
self._myselection = (ALL_SELECTED, np.array(excluded_nodes))
else:
# The node is included in the selection, we add it to the excluded nodes
excluded_nodes.append((x1, y1))
self._myselection = (ALL_SELECTED, np.array(excluded_nodes))
else:
if verif:
try:
ret = self._myselection.index((x1, y1))
except:
ret = -1
if ret >= 0:
self._myselection.pop(ret)
return 0
else:
self._myselection.append((x1, y1))
return 0
else:
self._myselection.append((x1, y1))
return 0
[docs]
def _add_nodes_to_selectionij(self, ij:list[tuple[float, float]], verif:bool=True):
"""
Add multiple ij coordinates to the selection
:param ij: list of ij coordinates
:param verif: if True, the coordinates are checked to avoid duplicates
"""
if len(ij)==0:
logging.info(_('Nothing to do in add_nodes_to_selectionij !'))
return
nbini = self.nb
ij = np.asarray(ij)
if self._storage_mode == StorageMode.ARRAY:
if verif:
self._boolarray[ij[:, 0], ij[:, 1]] = np.logical_xor(self._boolarray[ij[:, 0], ij[:, 1]], True)
else:
self._boolarray[ij[:, 0], ij[:, 1]] = True
elif self._storage_mode == StorageMode.LIST:
xy = self.parent.ij2xy_np(ij)
if self._myselection == ALL_SELECTED:
# If all nodes are selected, we need to exclude the current nodes
self._myselection = (ALL_SELECTED, xy) # Exclude the current nodes
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
# check if the current nodes are in the excluded nodes
excluded_nodes = self._myselection[1]
# extend the excluded nodes with the new ones
excluded_nodes = np.vstack((excluded_nodes, xy))
# Find the duplicates in the excluded nodes
selunique, counts = np.unique(excluded_nodes, return_counts=True, axis=0)
# les éléments énumérés plus d'une fois doivent être enlevés
locsort = sorted(zip(counts.tolist(), selunique.tolist()), reverse=True)
counts = [x[0] for x in locsort]
sel = [tuple(x[1]) for x in locsort]
# on recherche le premier 1
if 1 in counts:
idx = counts.index(1)
# on ne conserve que la portion de liste utile
self._myselection = (ALL_SELECTED, np.array(sel[idx:]))
else:
self._myselection = ALL_SELECTED # All nodes are selected again
else:
# Add the new nodes to the selection
if nbini != 0:
self._myselection += xy.tolist()
if verif:
# trouve les éléments uniques dans la liste de tuples (--> axis=0) et retourne également le comptage
selunique, counts = np.unique(self._myselection, return_counts=True, axis=0)
# les éléments énumérés plus d'une fois doivent être enlevés
# on trie par ordre décroissant
locsort = sorted(zip(counts.tolist(), selunique.tolist()), reverse=True)
counts = [x[0] for x in locsort]
sel = [tuple(x[1]) for x in locsort]
# on recherche le premier 1
if 1 in counts:
idx = counts.index(1)
# on ne conserve que la portion de liste utile
self._myselection = sel[idx:]
else:
self._myselection = []
else:
self._myselection = np.unique(self.myselection, axis=0).tolist()
else:
self._myselection = xy.tolist()
self.update_nb_nodes_selection()
[docs]
def _add_nodes_to_selection_np(self, ij:np.ndarray, verif:bool=True):
""" Add multiple coordinates to the selection from a numpy array
:param ij: numpy array of coordinates - same shape as the parent array (nbx, nby) or (nb_nodes, 2)
:param verif: if True, the coordinates are checked to avoid duplicates
"""
assert ij.shape == (self.parent.nbx, self.parent.nby) or ij.shape[1]==2, _('Invalid shape for ij - must be ({}, {})'.format(self.parent.nbx, self.parent.nby))
dtype = ij.dtype
if dtype not in [np.bool, np.int32, np.int64]:
assert ij.shape[1] == 2, _('Invalid shape for ij - must be ({}, {}) or (nb_nodes, 2)'.format(self.parent.nbx, self.parent.nby))
ij = self.parent.xy2ij_np(ij)
if self._storage_mode == StorageMode.ARRAY:
if verif:
if ij.shape[1] == 2:
# using xy from np.where
self._myselection_as_array[ij[:,0], ij[:,1]] = np.logical_xor(self._myselection_as_array[ij[:, 0], ij[:, 1]], True)
else:
self._myselection_as_array[:,:] = np.logical_xor(self._myselection_as_array, ij)
else:
if ij.shape[1] == 2:
# using xy from np.where
self._myselection_as_array[ij[:,0], ij[:,1]] = True
else:
self._myselection_as_array[ij] = True
elif self._storage_mode == StorageMode.LIST:
if ij.shape[1] == 2:
# using xy from np.where
self._add_nodes_to_selectionij(ij, verif)
else:
# Convert the numpy array to a list of tuples
ij = np.argwhere(ij)
self._add_nodes_to_selectionij(ij, verif)
[docs]
def select_insidepoly(self, myvect: vector):
""" Select nodes inside a polygon.
:param myvect: vector defining the polygon
"""
nbini = self.nb
self.hideselection = self.parent.usemask
inside = self.parent.get_ij_inside_polygon(myvect, usemask=self.hideselection)
if inside.shape[0] == 0:
logging.info(_('No nodes inside the polygon'))
return
else:
if inside.shape[0] > self.threshold_array_mode:
self.force_storage_mode(StorageMode.ARRAY)
self._add_nodes_to_selectionij(inside, verif= nbini != 0)
self.hideselection=False
[docs]
def select_underpoly(self, myvect: vector):
""" Select nodes along a polyline
:param myvect: vector defining the polyline
"""
nbini = self.nb
myvect.find_minmax()
mypoints = self.parent.get_ij_under_polyline(myvect)
if self.parent.usemask:
# If the parent uses a mask, we need to check if the points are not masked
mypoints = mypoints[~self.parent.array.mask[mypoints[:, 0], mypoints[:, 1]]]
if len(mypoints) == 0:
logging.info(_('No nodes under the polyline'))
return
self.add_nodes_to_selection(mypoints, verif = nbini != 0)
[docs]
def dilate_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
""" Extend the selection.
:param nb_iterations: Number of iterations to dilate the selection
:param use_mask: If True, use the mask of the parent array to limit the dilation
:param structure: Structuring element for dilation, default is a cross shape (nodes directly adjacent in the x or y direction)
"""
if self.is_all_selected():
logging.info(_('Cannot extend selection when all nodes are selected'))
return
if self.nb == 0:
logging.info(_('No nodes selected'))
return
if nb_iterations < 1:
logging.info(_('Number of iterations must be greater than 0'))
return
if self.parent.array is None:
logging.info(_('No array to select from'))
return
from scipy import ndimage
self._myselection_as_array[:,:] = ndimage.binary_dilation(self._myselection_as_array,
iterations=nb_iterations,
mask=~self.parent.array.mask if use_mask else None,
structure=structure)
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
self.update_nb_nodes_selection()
[docs]
def erode_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
""" Reduce the selection.
:param nb_iterations: Number of iterations to erode the selection
:param use_mask: If True, use the mask of the parent array to limit the erosion
:param structure: Structuring element for erosion, default is a cross shape (nodes directly adjacent in the x or y direction)
"""
if self.is_all_selected():
logging.info(_('Cannot reduce selection when all nodes are selected'))
return
if self.nb == 0:
logging.info(_('No nodes selected'))
return
if nb_iterations < 1:
logging.info(_('Number of iterations must be greater than 0'))
return
if self.parent.array is None:
logging.info(_('No array to select from'))
return
from scipy import ndimage
self._myselection_as_array[:,:] = ndimage.binary_erosion(self._myselection_as_array,
iterations=nb_iterations,
mask=~self.parent.array.mask if use_mask else None,
structure=structure)
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
self.update_nb_nodes_selection()
[docs]
def dilate_contour_selection(self, nbiter:int= 1, use_mask:bool = True, structure:np.ndarray = np.ones((3,3))):
""" Dilate the contour of the selection.
:param nbiter: Number of iterations to dilate the selection
:param use_mask: If True, use the mask of the parent array to limit the dilation
:param structure: Structuring element for dilation, default is a 3x3 square
"""
if self.nb > 0:
oldsel = self._myselection_as_array.copy()
self.dilate_selection(nbiter, use_mask, structure)
# We keep only the new nodes that were not in the old selection
self._myselection_as_array[:,:] = np.logical_and(self._myselection_as_array, ~oldsel)
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
self.update_nb_nodes_selection()
else:
logging.info('No selection to expand/dilate')
[docs]
def erode_contour_selection(self):
""" Erode the contour of the selection.
:param nbiter: Number of iterations to erode the selection
:param use_mask: If True, use the mask of the parent array to limit the erosion
:param structure: Structuring element for erosion, default is a 3x3 square
"""
if self.nb > 0:
oldselect = self._myselection_as_array.copy()
self.erode_selection(1)
self._myselection_as_array[:,:] = np.logical_and(self._myselection_as_array, oldselect)
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
self.update_nb_nodes_selection()
else:
logging.info('No selection to contract/erode')
[docs]
def save_selection(self, filename:str=None):
""" Save the selection to a file.
:param filename: Name of the file to save the selection to. If None, a dialog will be shown to choose the file.
"""
if filename is None:
with wx.FileDialog(None, 'Save selection', wildcard='Text files (*.txt)|*.txt',
style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as dlg:
if dlg.ShowModal() == wx.ID_CANCEL:
return
filename = dlg.GetPath()
with open(filename, 'w') as f:
f.write(self.get_string(all_memories=True))
[docs]
def load_selection(self, filename:str=None):
""" Load a selection from a file.
:param filename: Name of the file to load the selection from. If None, a dialog will be shown to choose the file.
"""
if filename is None:
with wx.FileDialog(None, 'Load selection', wildcard='Text files (*.txt)|*.txt',
style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as dlg:
if dlg.ShowModal() == wx.ID_CANCEL:
return
filename = dlg.GetPath()
with open(filename, 'r') as f:
lines = f.readlines()
xy = []
k=0
for line in lines:
if line[0] == 'X':
k+=1
continue
if line[0] == 'i':
k+=1
break
x, y = line.split()
xy.append((float(x), float(y)))
k+=1
self.myselection = xy
self.update_nb_nodes_selection()
k += len(xy)
while k < len(lines):
lines = lines[k:]
k=0
for line in lines:
if 'Selection' in line:
idx = line.split()[1]
self.selections[idx] = {}
xy = []
k+=1
elif 'Color' in line:
color = [int(x.replace('[','').replace(']','').replace(',','')) for x in line.split()[2:]]
self.selections[idx]['Color'] = color
k+=1
elif line[0] == 'X':
k+=1
continue
elif line[0] == 'i':
k+=len(xy)+1
self.selections[idx]['select']=xy
break
else:
x, y = line.split()
xy.append((float(x), float(y)))
k+=1
self.parent.reset_plot()
[docs]
def update_nb_nodes_selection(self, autostoragemode:bool=True) -> tuple[int, float, float, float, float]:
""" Update the number of selected nodes.
:param autostoragemode: If True, automatically switch to ARRAY mode if the number of selected nodes exceeds the threshold.
:return: Tuple containing the number of selected nodes, and the bounds of the selection (xmin, xmax, ymin, ymax).
"""
if autostoragemode:
self._auto_storage_mode()
if self._storage_mode == StorageMode.ARRAY:
nb = np.count_nonzero(self._myselection_as_array)
elif self._storage_mode == StorageMode.LIST:
if self.is_all_selected():
if self.parent.usemask:
nb = self.parent.nbnotnull
else:
nb = self.parent.nbx * self.parent.nby
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
if self.parent.usemask:
nb = self.parent.nbnotnull
else:
nb = self.parent.nbx * self.parent.nby
nb -= len(self._myselection[1])
else:
nb = len(self._myselection)
self.update_plot_selection = True
if self.wx_exists:
if nb > 10000:
if not self.hideselection:
self.update_plot_selection = False # on met par défaut àFalse car OpenGL va demander une MAJ de l'affichage le temps que l'utilisateur réponde
dlg = wx.MessageDialog(None,
'Large selection !!' + str(nb) + '\n Do you want plot the selected cells?',
style=wx.YES_NO)
ret = dlg.ShowModal()
if ret == wx.ID_YES:
self.update_plot_selection = True
else:
self.update_plot_selection = False
self.hideselection = True
dlg.Destroy()
else:
self.update_plot_selection = True
if nb>0:
if self.is_all_selected():
[xmin, xmax], [ymin, ymax] = self.parent.get_bounds()
else:
if self._storage_mode == StorageMode.ARRAY:
# Get the bounds of the selection from the boolean array
sel = np.argwhere(self._myselection_as_array)
xmin = np.min(sel[:, 0])
ymin = np.min(sel[:, 1])
xmax = np.max(sel[:, 0])
ymax = np.max(sel[:, 1])
xmin, ymin = self.parent.get_xy_from_ij(xmin, ymin)
xmax, ymax = self.parent.get_xy_from_ij(xmax, ymax)
elif self._storage_mode == StorageMode.LIST:
xmin = np.min(np.asarray(self.myselection)[:, 0])
ymin = np.min(np.asarray(self.myselection)[:, 1])
xmax = np.max(np.asarray(self.myselection)[:, 0])
ymax = np.max(np.asarray(self.myselection)[:, 1])
else:
xmin = -99999.
ymin = -99999.
xmax = -99999.
ymax = -99999.
if self.parent.myops is not None:
self.parent.myops.nbselect.SetLabelText(str(nb))
self.parent.myops.nbselect2.SetLabelText(str(nb))
if nb>0:
self.parent.myops.minx.SetLabelText('{:.3f}'.format(xmin))
self.parent.myops.miny.SetLabelText('{:.3f}'.format(ymin))
self.parent.myops.maxx.SetLabelText('{:.3f}'.format(xmax))
self.parent.myops.maxy.SetLabelText('{:.3f}'.format(ymax))
return nb, xmin, xmax, ymin, ymax
[docs]
def condition_select(self, cond:str | int, condval, condval2 = 0, usemask:bool = False):
""" Select nodes based on a condition.
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN', 6: '>=<=', 7: '><', 8: '<>')
:param condval: value to compare with
:param condval2: second value to compare with (for ranges)
:param usemask: whether to use the mask or not
"""
array = self.parent.array
nbini = self.nb
if array.dtype == np.float32:
condval = np.float32(condval)
condval2 = np.float32(condval2)
elif array.dtype == np.float64:
condval = np.float64(condval)
condval2 = np.float64(condval2)
elif array.dtype == np.int32:
condval = np.int32(condval)
condval2 = np.int32(condval2)
elif array.dtype == np.int64:
condval = np.int64(condval)
condval2 = np.int64(condval2)
elif array.dtype == np.int16:
condval = np.int16(condval)
condval2 = np.int16(condval2)
elif array.dtype == np.int8:
condval = np.int8(condval)
condval2 = np.int8(condval2)
elif array.dtype == np.uint32:
condval = np.uint32(condval)
condval2 = np.uint32(condval2)
elif array.dtype == np.uint64:
condval = np.uint64(condval)
condval2 = np.uint64(condval2)
elif array.dtype == np.uint16:
condval = np.uint16(condval)
condval2 = np.uint16(condval2)
elif array.dtype == np.uint8:
condval = np.uint8(condval)
condval2 = np.uint8(condval2)
else:
logging.error(_('Unknown dtype in treat_select !'))
return
if usemask :
mask=np.logical_not(array.mask)
if nbini == 0:
try:
if cond == 0 or cond=='<':
# <
ij = np.argwhere((array < condval) & mask)
elif cond == 1 or cond=='<=':
# <=
ij = np.argwhere((array <= condval) & mask)
elif cond == 2 or cond=='==':
# ==
ij = np.argwhere((array == condval) & mask)
elif cond == 3 or cond=='>=':
# >=
ij = np.argwhere((array >= condval) & mask)
elif cond == 4 or cond=='>':
# >
ij = np.argwhere((array > condval) & mask)
elif cond == 5 or cond=='NaN':
# NaN
ij = np.argwhere((np.isnan(array)) & mask)
elif cond == 6 or cond=='>=<=':
# interval with equality
ij = np.argwhere(((array>=condval) & (array<=condval2)) & mask)
elif cond == 7 or cond=='><':
# interval without equality
ij = np.argwhere(((array>condval) & (array<condval2)) & mask)
elif cond == 8 or cond=='<>':
# interval without equality
ij = np.argwhere(((array<condval) | (array>condval2)) & mask)
self._add_nodes_to_selectionij(ij, nbini != 0)
except:
logging.error(_('Error in condition_select -- nbini == 0 ! -- Please report this bug, specifying the context'))
return
else:
try:
if self.nb > self.threshold_array_mode:
# If the selection is large, we use the boolean array
ijall = np.argwhere(self._myselection_as_array)
else:
# Otherwise, we use the selection list
sel = np.asarray(self.myselection)
ijall = self.parent.xy2ij_np(sel)
if cond == 0 or cond=='<':
# <
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] < condval) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 1 or cond=='<=':
# <=
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] <= condval) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 2 or cond=='==':
# ==
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] == condval) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 3 or cond=='>=':
# >=
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] >= condval) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 4 or cond=='>':
# >
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] > condval) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 5 or cond=='NaN':
# NaN
ij = np.argwhere((np.isnan(array[ijall[:, 0], ijall[:, 1]])) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 6 or cond=='>=<=':
# interval with equality
ij = np.argwhere(((array[ijall[:, 0], ijall[:, 1]]>=condval) & (array[ijall[:, 0], ijall[:, 1]]<=condval2)) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 7 or cond=='><':
# interval without equality
ij = np.argwhere(((array[ijall[:, 0], ijall[:, 1]]>condval) & (array[ijall[:, 0], ijall[:, 1]]<condval2)) & (mask[ijall[:, 0], ijall[:, 1]]))
elif cond == 8 or cond=='<>':
# interval without equality
ij = np.argwhere(((array[ijall[:, 0], ijall[:, 1]]<condval) | (array[ijall[:, 0], ijall[:, 1]]>condval2)) & (mask[ijall[:, 0], ijall[:, 1]]))
ij = ij.flatten()
self._add_nodes_to_selectionij(ijall[ij], nbini != 0)
except:
logging.error(_('Error in condition_select ! -- Please report this bug, specifying the context'))
return
else:
if nbini == 0:
try:
if cond == 0 or cond=='<':
# <
ij = np.argwhere(array < condval)
elif cond == 1 or cond=='<=':
# <=
ij = np.argwhere(array <= condval)
elif cond == 2 or cond=='==':
# ==
ij = np.argwhere(array == condval)
elif cond == 3 or cond=='>=':
# >=
ij = np.argwhere(array >= condval)
elif cond == 4 or cond=='>':
# >
ij = np.argwhere(array > condval)
elif cond == 5 or cond=='NaN':
# NaN
ij = np.argwhere(np.isnan(array))
elif cond == 6 or cond=='>=<=':
# interval with equality
ij = np.argwhere((array>=condval) & (array<=condval2))
elif cond == 7 or cond=='><':
# interval without equality
ij = np.argwhere((array>condval) & (array<condval2))
elif cond == 8 or cond=='<>':
# interval without equality
ij = np.argwhere((array<condval) | (array>condval2))
elif cond == -1 or cond=='Mask':
# Mask
ij = np.argwhere(array.mask)
elif cond == -2 or cond=='NotMask':
# Mask
ij = np.argwhere(np.logical_not(array.mask))
self._add_nodes_to_selectionij(ij, nbini != 0)
except:
logging.error(_('Error in condition_select -- nbini == 0 ! -- Please report this bug, specifying the context'))
return
else:
try:
if self.nb > self.threshold_array_mode:
ijall = np.argwhere(self._myselection_as_array)
else:
sel = np.asarray(self.myselection)
ijall = self.parent.xy2ij_np(sel)
if cond == 0 or cond=='<':
# <
ij = np.argwhere(array[ijall[:, 0], ijall[:, 1]] < condval)
elif cond == 1 or cond=='<=':
# <=
ij = np.argwhere(array[ijall[:, 0], ijall[:, 1]] <= condval)
elif cond == 2 or cond=='==':
# ==
ij = np.argwhere(array[ijall[:, 0], ijall[:, 1]] == condval)
elif cond == 3 or cond=='>=':
# >=
ij = np.argwhere(array[ijall[:, 0], ijall[:, 1]] >= condval)
elif cond == 4 or cond=='>':
# >
ij = np.argwhere(array[ijall[:, 0], ijall[:, 1]] > condval)
elif cond == 5 or cond=='NaN':
# NaN
ij = np.argwhere(np.isnan(array[ijall[:, 0], ijall[:, 1]]))
elif cond == 6 or cond=='>=<=':
# interval with equality
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]]>=condval) & (array[ijall[:, 0], ijall[:, 1]]<=condval2))
elif cond == 7 or cond=='><':
# interval without equality
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]]>condval) & (array[ijall[:, 0], ijall[:, 1]]<condval2))
elif cond == 8 or cond=='<>':
# interval without equality
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]]<condval) | (array[ijall[:, 0], ijall[:, 1]]>condval2) )
elif cond == -1 or cond=='Mask':
# Mask
ij = np.argwhere(array.mask[ijall[:, 0], ijall[:, 1]])
elif cond == -2 or cond=='NotMask':
# Mask
ij = np.argwhere(np.logical_not(array.mask[ijall[:, 0], ijall[:, 1]]))
ij = ij.flatten()
self._add_nodes_to_selectionij(ijall[ij], nbini != 0)
except:
logging.error(_('Error in condition_select ! -- Please report this bug, specifying the context'))
return
# self.update_nb_nodes_selection()
[docs]
def treat_select(self, op:int, cond:int, opval, condval):
""" Apply an operation on the selected nodes based on a condition.
:param op: operation to apply (0: '+', 1: '-', 2: '*', 3: '/', 4: 'replace')
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN')
:param opval: value to apply the operation with
:param condval: value to compare with
"""
# operationChoices = [ u"+", u"-", u"*", u"/", u"replace'" ]
# conditionChoices = [ u"<", u"<=", u"=", u">=", u">",u"isNaN" ]
def test(val, cond, condval):
if cond == 0:
return val < condval
elif cond == 1:
return val <= condval
elif cond == 2:
return val == condval
elif cond == 3:
return val >= condval
elif cond == 4:
return val > condval
elif cond == 5:
return np.isnan(val)
array = self.parent.array
if array.dtype == np.float32:
opval = np.float32(opval)
condval = np.float32(condval)
elif array.dtype == np.float64:
opval = np.float64(opval)
condval = np.float64(condval)
elif array.dtype == np.int32:
opval = np.int32(opval)
condval = np.int32(condval)
elif array.dtype == np.int64:
opval = np.int64(opval)
condval = np.int64(condval)
elif array.dtype == np.int16:
opval = np.int16(opval)
condval = np.int16(condval)
elif array.dtype == np.int8:
opval = np.int8(opval)
condval = np.int8(condval)
elif array.dtype == np.uint32:
opval = np.uint32(opval)
condval = np.uint32(condval)
elif array.dtype == np.uint64:
opval = np.uint64(opval)
condval = np.uint64(condval)
elif array.dtype == np.uint16:
opval = np.uint16(opval)
condval = np.uint16(condval)
elif array.dtype == np.uint8:
opval = np.uint8(opval)
condval = np.uint8(condval)
else:
logging.error(_('Unknown dtype in treat_select !'))
return
if self.myselection == ALL_SELECTED:
if op == 0:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] += opval
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] += opval
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] += opval
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] += opval
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] += opval
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif op == 1:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] -= opval
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] -= opval
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] -= opval
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] -= opval
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] -= opval
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif op == 2:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] *= opval
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] *= opval
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] *= opval
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] *= opval
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] *= opval
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif op == 3 and opval != 0.:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] /= opval
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] /= opval
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] /= opval
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] /= opval
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] /= opval
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = 0
elif op == 4:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array[ind[:, 0], ind[:, 1]] = opval
else:
if len(self.myselection) == 0:
logging.info(_('Nothing to do in treat_select ! -- PLease select some nodes'))
return
ij = [self.parent.get_ij_from_xy(cur[0], cur[1]) for cur in self.myselection]
if op == 0:
for i, j in ij:
if test(array.data[i, j], cond, condval):
array.data[i, j] += opval
elif op == 1:
for i, j in ij:
if test(array.data[i, j], cond, condval):
array.data[i, j] -= opval
elif op == 2:
for i, j in ij:
if test(array.data[i, j], cond, condval):
array.data[i, j] *= opval
elif op == 3 and opval != 0.:
for i, j in ij:
if test(array.data[i, j], cond, condval):
array.data[i, j] /= opval
elif op == 4:
for i, j in ij:
if test(array.data[i, j], cond, condval):
array.data[i, j] = opval
self.parent.mask_data(self.parent.nullvalue)
self.refresh_parantarray()
[docs]
def refresh_parantarray(self):
""" Refresh the parent array after a selection """
self.parent.reset_plot()
[docs]
def mask_condition(self, op:int, cond:int, opval, condval):
""" Mask nodes based on a condition.
:param op: operation to apply (0: '+', 1: '-', 2: '*', 3: '/', 4: 'replace')
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN')
:param opval: value to apply the operation with
:param condval: value to compare with
"""
# operationChoices = [ u"+", u"-", u"*", u"/", u"replace'" ]
# conditionChoices = [ u"<", u"<=", u"=", u">=", u">",u"isNaN" ]
def test(val, cond, condval):
if cond == 0:
return val < condval
elif cond == 1:
return val <= condval
elif cond == 2:
return val == condval
elif cond == 3:
return val >= condval
elif cond == 4:
return val > condval
elif cond == 5:
return np.isnan(val)
array = self.parent.array
if array.dtype == np.float32:
opval = np.float32(opval)
condval = np.float32(condval)
elif array.dtype == np.float64:
opval = np.float64(opval)
condval = np.float64(condval)
elif array.dtype == np.int32:
opval = np.int32(opval)
condval = np.int32(condval)
elif array.dtype == np.int64:
opval = np.int64(opval)
condval = np.int64(condval)
elif array.dtype == np.int16:
opval = np.int16(opval)
condval = np.int16(condval)
elif array.dtype == np.int8:
opval = np.int8(opval)
condval = np.int8(condval)
elif array.dtype == np.uint32:
opval = np.uint32(opval)
condval = np.uint32(condval)
elif array.dtype == np.uint64:
opval = np.uint64(opval)
condval = np.uint64(condval)
elif array.dtype == np.uint16:
opval = np.uint16(opval)
condval = np.uint16(condval)
elif array.dtype == np.uint8:
opval = np.uint8(opval)
condval = np.uint8(condval)
else:
logging.error(_('Unknown dtype in treat_select !'))
return
if self.myselection == ALL_SELECTED:
if cond == 0:
# <
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
elif cond == 1:
# <=
ind = np.argwhere(np.logical_and(array <= condval, np.logical_not(array.mask)))
elif cond == 2:
# ==
ind = np.argwhere(np.logical_and(array == condval, np.logical_not(array.mask)))
elif cond == 3:
# >=
ind = np.argwhere(np.logical_and(array >= condval, np.logical_not(array.mask)))
elif cond == 4:
# >
ind = np.argwhere(np.logical_and(array > condval, np.logical_not(array.mask)))
elif cond == 5:
# NaN
ind = np.argwhere(np.logical_and(np.isnan(array), np.logical_not(array.mask)))
array.mask[ind[:, 0], ind[:, 1]] = True
else:
npself = np.asarray(self.myselection)
ij = self.parent.xy2ij_np(npself)
array.mask[ij[:, 0], ij[:, 1]] = test(array.data[ij[:, 0], ij[:, 1]], cond, condval)
# ij = [self.parent.get_ij_from_xy(cur[0], cur[1]) for cur in self.myselection]
# for i, j in ij:
# if test(array.data[i, j], cond, condval):
# array.mask[i, j] = True
self.parent.nbnotnull = array.count()
self.parent.updatepalette()
self.parent.delete_lists()
[docs]
def get_values_sel(self) -> np.ndarray:
""" Get the values of the selected nodes.
:return: numpy array of values of the selected nodes, or -99999 if all nodes are selected.
"""
if self.myselection == ALL_SELECTED:
return -99999
else:
sel = np.asarray(self.myselection)
if len(sel) == 1:
ijall = self.parent.xy2ij_np(sel)
z = self.parent.array[ijall[0], ijall[1]]
else:
ijall = self.parent.xy2ij_np(sel)
z = self.parent.array[ijall[:, 0], ijall[:, 1]].flatten()
return z
[docs]
def get_newarray(self) -> "WolfArray":
""" Create a new array from the selection.
:return: a new WolfArray object containing the selected nodes, or None if the selection is empty. Null values are set to -99999.
If all nodes are selected, returns a WolfArray molded from the parent.
"""
from ._base import WolfArrayModel # lazy import to avoid circular dependency
if self.nb == 0:
return None
if self.myselection == ALL_SELECTED:
return WolfArrayModel(mold=self.parent)
newarray = WolfArrayModel()
lochead = self._get_header()
if lochead is None:
logging.error(_('Error in get_newarray !'))
return
newarray.init_from_header(self._get_header())
sel = np.asarray(self.myselection)
if len(sel) == 1:
# ijall = np.asarray(self.parent.get_ij_from_xy(sel[0, 0], sel[0, 1])).transpose()
ijall = self.parent.xy2ij_np(sel)
z = self.parent.array[ijall[0], ijall[1]]
else:
ijall = np.asarray(self.parent.get_ij_from_xy(sel[:, 0], sel[:, 1])).transpose()
ijall = self.parent.xy2ij_np(sel)
z = self.parent.array[ijall[:, 0], ijall[:, 1]].flatten()
newarray.array[:, :] = -99999.
newarray.nullvalue = -99999.
newarray.set_values_sel(sel, z)
return newarray
[docs]
def select_all(self):
""" Select all nodes.
It will set the selection to ALL_SELECTED, which is a special value indicating that all nodes are selected.
"""
self.myselection = ALL_SELECTED
[docs]
def interp2Dpolygons(self, working_zone:zone,
method:Literal["nearest", "linear", "cubic"] = None,
resetplot:bool = True,
keep:Literal['all', 'below', 'above'] = 'all'):
"""
Interpolation sous tous les polygones d'une zone
cf parent.interp2Dpolygon
Useful for WX GUI, where the method is chosen by the user.
From script, you can call this method directly from the parent array
with the desired method and keep parameters.
"""
if method is None:
choices = ["nearest", "linear", "cubic"]
dlg = wx.SingleChoiceDialog(None, "Pick an interpolate method", "Choices", choices)
ret = dlg.ShowModal()
if ret == wx.ID_CANCEL:
dlg.Destroy()
return
method = dlg.GetStringSelection()
dlg.Destroy()
self.parent.interpolate_on_polygons(working_zone, method, keep)
if resetplot:
self.parent.reset_plot()
[docs]
def interp2Dpolygon(self, working_vector:vector,
method:Literal["nearest", "linear", "cubic"] = None,
resetplot:bool = True,
keep:Literal['all', 'below', 'above'] = 'all'):
"""
Interpolation sous un polygone
cf parent.interp2Dpolygon
Useful for WX GUI, where the method is chosen by the user.
From script, you can call this method directly from the parent array
with the desired method and keep parameters.
"""
if method is None:
choices = ["nearest", "linear", "cubic"]
dlg = wx.SingleChoiceDialog(None, "Pick an interpolate method", "Choices", choices)
ret = dlg.ShowModal()
if ret == wx.ID_CANCEL:
dlg.Destroy()
return
method = dlg.GetStringSelection()
dlg.Destroy()
self.parent.interpolate_on_polygon(working_vector, method, keep)
if resetplot:
self.parent.reset_plot()
[docs]
def interp2Dpolylines(self, working_zone:zone, resetplot:bool = True):
"""
Interpolation sous toutes les polylignes de la zone
cf parent.interp2Dpolyline
Useful for WX GUI, where the method is chosen by the user.
From script, you can call this method directly from the parent array
with the desired method and keep parameters.
"""
self.parent.interpolate_on_polylines(working_zone)
if resetplot:
self.parent.reset_plot()
[docs]
def interp2Dpolyline(self, working_vector:vector, resetplot:bool = True):
"""
Interpolation sous la polyligne active
cf parent.interp2Dpolyline
Useful for WX GUI, where the method is chosen by the user.
From script, you can call this method directly from the parent array
with the desired method and keep parameters.
"""
self.parent.interpolate_on_polyline(working_vector)
if resetplot:
self.parent.reset_plot()
[docs]
def copy(self, source:"SelectionData"):
""" Copy the selection data from another SelectionData object.
:param source: another SelectionData object to copy from.
"""
if source._storage_mode == StorageMode.ARRAY:
self._storage_mode = StorageMode.ARRAY
self._myselection_as_array = source._myselection_as_array.copy()
elif source._storage_mode == StorageMode.LIST:
self._storage_mode = StorageMode.LIST
self._myselection = source._myselection.copy()
[docs]
def volumesurface(self, show=True):
"""
Evaluation of stage-storage-surface relation from "volume_estimation" routine of the WolfArray.
If the selection is empty, it will use the whole array.
If the selection is ALL_SELECTED, it will use the whole array.
If the selection is not empty, it will use the selected nodes only -- see "get_newarray" routine.
If the parent array is linked to a MapViewer, it will apply the same operation to all linked active arrays.
:param show: if True, the figure will be shown.
"""
if self.parent.get_mapviewer() is not None:
mapviewer = self.parent.get_mapviewer()
if mapviewer.linked:
# If linked arrays, we copy the selection data to all linked arrays
arrays = [cur.active_array for cur in mapviewer.linkedList]
for cur in arrays:
if cur is not self.parent:
cur.SelectionData.copy(self.parent.SelectionData)
if self.nb == 0 or self.myselection == ALL_SELECTED:
for cur in arrays:
fig, axs = cur.volume_estimation()
if show:
fig.show()
else:
for cur in arrays:
myarray = cur.SelectionData.get_newarray()
fig, axs = myarray.volume_estimation()
if show:
fig.show()
else:
if len(self.parent.SelectionData.myselection) == 0 or self.parent.SelectionData.myselection == ALL_SELECTED:
myarray = self.parent
else:
myarray = self.parent.SelectionData.get_newarray()
myarray.SelectionData.selections = self.parent.SelectionData.selections.copy()
fig, axs = myarray.volume_estimation()
if show:
fig.show()
else:
if self.nb == 0 or self.myselection == ALL_SELECTED:
myarray = self.parent
else:
myarray = self.get_newarray()
myarray.SelectionData.selections = self.selections.copy()
fig, axs = myarray.volume_estimation()
if show:
fig.show()
[docs]
class SelectionDataMB(SelectionData):
""" Extension of SelectionData to manage multiple blocks.
All routines are not implemented yet, as they depend on the specificities of the blocks.
For the rest, it is mainly a simple wrapper around the SelectionData of each block.
"""
def __init__(self, parent:"WolfArrayMB"):
SelectionData.__init__(self, parent)
[docs]
self.parent:"WolfArrayMB" = parent
@property
[docs]
def nb(self):
return np.sum([cur.SelectionData.nb for cur in self.parent.active_blocks])
[docs]
def unmask_selection(self):
for curblock in self.parent.active_blocks:
curblock.SelectionData.unmask_selection(resetplot=False)
self.parent.reset_plot()
[docs]
def reset(self):
for curblock in self.parent.active_blocks:
curblock.SelectionData.reset()
[docs]
def select_all(self):
for curblock in self.parent.active_blocks:
curblock.SelectionData.select_all()
[docs]
def reset_all(self):
""" Reset the selection """
for curblock in self.parent.active_blocks:
curblock.SelectionData.reset_all()
[docs]
def get_string(self, which:str = None) -> str:
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def save_selection(self, filename:str=None, which:str = None):
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def load_selection(self, filename:str=None, which:str = None):
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def get_script(self, which:int = None) -> str:
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def get_newarray(self):
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def add_node_to_selection(self, x:float, y:float, verif:bool=True):
""" Add a node to the selection """
for curblock in self.parent.active_blocks:
ret = curblock.SelectionData.add_node_to_selection(x, y, verif)
[docs]
def add_nodes_to_selection(self, xy:list[float] | np.ndarray, verif:bool=True):
""" Add nodes to the selection """
for curblock in self.parent.active_blocks:
curblock.SelectionData.add_nodes_to_selection(xy, verif)
[docs]
def select_insidepoly(self, myvect: vector):
for curblock in self.parent.active_blocks:
curblock.SelectionData.select_insidepoly(myvect)
[docs]
def select_underpoly(self, myvect: vector):
for curblock in self.parent.active_blocks:
curblock.SelectionData.select_underpoly(myvect)
[docs]
def dilate_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
""" Extend the selection """
for curblock in self.parent.active_blocks:
curblock.SelectionData.dilate_selection(nb_iterations, use_mask, structure)
[docs]
def erode_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
""" Reduce the selection """
for curblock in self.parent.active_blocks:
curblock.SelectionData.erode_selection(nb_iterations, use_mask, structure)
[docs]
def update_nb_nodes_selection(self):
""" Update the number of nodes selected """
# Get infos from all blocks
ret = []
for curblock in self.parent.active_blocks:
ret.append(curblock.SelectionData.update_nb_nodes_selection())
# sum all the nodes
nb = np.sum([cur[0] for cur in ret])
if nb > 0 :
xmin = np.min([cur[1] for cur in ret if cur[1] != -99999.])
ymin = np.min([cur[3] for cur in ret if cur[3] != -99999.])
xmax = np.max([cur[2] for cur in ret if cur[2] != -99999.])
ymax = np.max([cur[4] for cur in ret if cur[4] != -99999.])
if self.parent.myops is not None:
self.parent.myops.nbselect.SetLabelText(str(nb))
self.parent.myops.nbselect2.SetLabelText(str(nb))
if nb>0:
self.parent.myops.minx.SetLabelText('{:.3f}'.format(xmin))
self.parent.myops.miny.SetLabelText('{:.3f}'.format(ymin))
self.parent.myops.maxx.SetLabelText('{:.3f}'.format(xmax))
self.parent.myops.maxy.SetLabelText('{:.3f}'.format(ymax))
else:
self.parent.myops.minx.SetLabelText('')
self.parent.myops.miny.SetLabelText('')
self.parent.myops.maxx.SetLabelText('')
self.parent.myops.maxy.SetLabelText('')
[docs]
def condition_select(self, cond, condval, condval2=0, usemask=False):
for curblock in self.parent.active_blocks:
curblock.SelectionData.condition_select(cond, condval, condval2, usemask)
[docs]
def treat_select(self, op, cond, opval, condval):
for curblock in self.parent.active_blocks:
curblock.SelectionData.treat_select(op, cond, opval, condval)
[docs]
def mask_condition(self, op, cond, opval, condval):
for curblock in self.parent.active_blocks:
curblock.SelectionData.mask_condition(op, cond, opval, condval)
[docs]
def plot_selection(self):
for curblock in self.parent.active_blocks:
curblock.SelectionData.plot_selection()
[docs]
def move_selectionto(self, idx:str, color:list[float]):
for curblock in self.parent.active_blocks:
curblock.SelectionData.move_selectionto(idx, color, resetplot=False)
self.parent.reset_plot()
[docs]
def copy_to_clipboard(self, which:int = None, typestr:Literal['string', 'script'] = 'string'):
logging.error(_('Not yet implemented for Multi-Blocks'))
[docs]
def interp2Dpolygons(self, working_zone:zone,
method:Literal["nearest", "linear", "cubic"] = None,
resetplot:bool = True,
keep:Literal['all', 'below', 'above'] = 'all'):
"""
Interpolation sous tous les polygones d'une zone
cf parent.interp2Dpolygon
"""
if method is None:
choices = ["nearest", "linear", "cubic"]
dlg = wx.SingleChoiceDialog(None, "Pick an interpolate method", "Choices", choices)
ret = dlg.ShowModal()
if ret == wx.ID_CANCEL:
dlg.Destroy()
return
method = dlg.GetStringSelection()
dlg.Destroy()
self.parent.interpolate_on_polygons(working_zone, method, keep)
if resetplot:
self.parent.reset_plot()
[docs]
def interp2Dpolygon(self, working_vector:vector,
method:Literal["nearest", "linear", "cubic"] = None,
resetplot:bool = True,
keep:Literal['all', 'below', 'above'] = 'all'):
"""
Interpolation sous un polygone
cf parent.interp2Dpolygon
"""
if method is None:
choices = ["nearest", "linear", "cubic"]
dlg = wx.SingleChoiceDialog(None, "Pick an interpolate method", "Choices", choices)
ret = dlg.ShowModal()
if ret == wx.ID_CANCEL:
dlg.Destroy()
return
method = dlg.GetStringSelection()
dlg.Destroy()
self.parent.interpolate_on_polygon(working_vector, method, keep)
if resetplot:
self.parent.reset_plot()
[docs]
def interp2Dpolylines(self, working_zone:zone, resetplot:bool = True):
"""
Interpolation sous toutes les polylignes de la zone
cf parent.interp2Dpolyline
"""
self.parent.interpolate_on_polylines(working_zone)
self.parent.reset_plot()
[docs]
def interp2Dpolyline(self, working_vector:vector, resetplot:bool = True):
"""
Interpolation sous la polyligne active
cf parent.interp2Dpolyline
"""
self.parent.interpolate_on_polyline(working_vector)
self.parent.reset_plot()
[docs]
def volumesurface(self, show=True):
"""
Evaluation of stage-storage-surface relation
"""
logging.error(_('Not yet implemented for Multi-Blocks'))