mirror of https://gitlab.com/pamhyr/pamhyr2
Geometry: Remove old OnPickEvent file.
parent
317cbf904d
commit
731567369e
|
|
@ -22,7 +22,6 @@ from math import dist, sqrt
|
|||
|
||||
from tools import timer, trace
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from View.Tools.Plot.OnPickEvent import OnpickEvent
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QCoreApplication, QItemSelectionModel
|
||||
|
|
|
|||
|
|
@ -1,574 +0,0 @@
|
|||
# mpl_canvas_onpick_event.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
import logging
|
||||
|
||||
from time import time
|
||||
|
||||
import numpy as np
|
||||
from PyQt5 import QtWidgets
|
||||
from PyQt5.QtCore import QItemSelectionModel, Qt
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from shapely.geometry.polygon import Polygon as ShapelyPolygon
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class OnpickEvent(object):
|
||||
def __init__(self, ax, x, y, x_carto, y_carto, tableview=None):
|
||||
"""
|
||||
Args:
|
||||
ax: objet Axes.
|
||||
x: abscisse x1 du graphe (f(x1) = y1) à tracer.
|
||||
y: ordonnée y1 du graphe (f(x1) = y1) à tracer.
|
||||
x_carto: (vecteur) abscisse des points (X,Y,Z) du profil.
|
||||
y_carto: (vecteur) abscisse des points (X,Y,Z) du profil
|
||||
tableview: tableau (de type QtableView) 'associé' au grahique.
|
||||
"""
|
||||
self.ax = ax
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.x_carto = x_carto
|
||||
self.y_carto = y_carto
|
||||
self.tableView = tableview
|
||||
self.counter_onclick = 0
|
||||
# self.select_point_plot()
|
||||
self.count = 0
|
||||
self.annotation_onclick = self.ax.annotate(
|
||||
"",
|
||||
xytext=(np.mean(self.x), np.mean(self.y)),
|
||||
xy=(np.mean(self.x), np.mean(self.y)),
|
||||
horizontalalignment='center',
|
||||
fontsize=8,
|
||||
# fontstyle='italic',
|
||||
fontweight='bold',
|
||||
alpha=0.7
|
||||
)
|
||||
self.annotation_onclick.set_visible(False)
|
||||
self.pos_x = 0
|
||||
self.zomm_xmin_xmax = self.ax.get_xlim()
|
||||
self.plot_selec()
|
||||
# self.select_point_plot()
|
||||
self._largeur_miroir, = self.ax.plot(
|
||||
self.x[1], self.y[1],
|
||||
color='blue', lw=1.2, ls=":"
|
||||
)
|
||||
|
||||
self.pt = []
|
||||
self.tableView.selectionModel()\
|
||||
.selectionChanged\
|
||||
.connect(self.update_select_point_point)
|
||||
|
||||
def select_row_pt_clicked(self, ind: int = 0):
|
||||
"""
|
||||
Args:
|
||||
ind: Indice de la ligne où se trouve le point le plus proche
|
||||
'visé'.
|
||||
|
||||
Returns: Sélectionne la ligne (du tableau) correspondant au point le
|
||||
plus proche 'visé' à la suite de l'événement onpick.
|
||||
"""
|
||||
if self.tableView is not None:
|
||||
selectionModel = self.tableView.selectionModel()
|
||||
index = self.tableView.model().index(ind, 0)
|
||||
selectionModel.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
self.tableView.scrollTo(index)
|
||||
|
||||
def select_qtableview_row(self, event):
|
||||
if self.tableView is not None:
|
||||
self.tableView.setFocus()
|
||||
ind = self.indice_points_onpick(event)
|
||||
dataidx_ecran = self.index_pt_plus_proche_ecran(event)
|
||||
self.select_row_pt_clicked(ind[dataidx_ecran])
|
||||
|
||||
def select_point_plot(self):
|
||||
"""
|
||||
Returns: sélectionne le(s) point(s) du graphique correspondant à
|
||||
la/aux ligne(s) sélectionnée(s) dans le tableau.
|
||||
"""
|
||||
if self.tableView is not None:
|
||||
rows = list(set(
|
||||
[index.row() for index in self.tableView.selectedIndexes()]
|
||||
))
|
||||
for row in rows:
|
||||
pass
|
||||
|
||||
def update_select_point_point(self):
|
||||
if self.tableView is not None:
|
||||
rows = list(set(
|
||||
[index.row() for index in self.tableView.selectedIndexes()]
|
||||
))
|
||||
|
||||
if len(rows) > 1:
|
||||
for row in rows:
|
||||
self.pt1 = self.ax.plot(self.x[row], self.y[row],
|
||||
'+', c='Blue', markersize=7)
|
||||
self.pt.append(self.pt1)
|
||||
self.update_select_point_point_bis(
|
||||
self.x[row], self.y[row])
|
||||
elif len(rows) == 1:
|
||||
for row in rows:
|
||||
try:
|
||||
[pl[0].set_data([], [])
|
||||
for pl in self.pt if len(self.pt) > 1]
|
||||
except Exception:
|
||||
logger.info("update_select_point_point: Update issue")
|
||||
|
||||
try:
|
||||
self.update_select_point_point_bis(self.x[row],
|
||||
self.y[row])
|
||||
except Exception:
|
||||
logger.info(
|
||||
"update_select_point_point_bis: Update issue, " +
|
||||
"possible index missing"
|
||||
)
|
||||
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
|
||||
def plot_selec(self):
|
||||
self.point_selec, = self.ax.plot(self.x[0], self.y[0],
|
||||
'+', c='Blue', markersize=7)
|
||||
self.point_selec.set_visible(False)
|
||||
|
||||
def update_select_point_point_bis(self, x_ind, y_ind):
|
||||
self.point_selec.set_data(x_ind, y_ind)
|
||||
self.point_selec.set_visible(True)
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
|
||||
def plot_selection_point(self, x, y):
|
||||
"""
|
||||
Args:
|
||||
x: abscissa
|
||||
y: ordinate
|
||||
|
||||
Returns: sélectionne le point du graphique correspond à la ligne
|
||||
sélectionnée dans le tableau.
|
||||
"""
|
||||
if self.tableView is not None:
|
||||
self.select_point, = self.ax.plot(
|
||||
x, y,
|
||||
'+', c='Blue',
|
||||
markersize=7
|
||||
)
|
||||
else:
|
||||
self.select_point, = self.ax.plot([], [])
|
||||
|
||||
def geometrie_sans_rebord(self):
|
||||
rebord = True
|
||||
z_sans_rebord = [i for i in self.y]
|
||||
x_sans_rebord = [i for i in self.x]
|
||||
|
||||
while rebord:
|
||||
if z_sans_rebord[1] >= z_sans_rebord[0]:
|
||||
z_sans_rebord.pop(0)
|
||||
x_sans_rebord.pop(0)
|
||||
else:
|
||||
rebord = False
|
||||
|
||||
rebord = True
|
||||
while rebord:
|
||||
if z_sans_rebord[-1] <= z_sans_rebord[-2]:
|
||||
z_sans_rebord.pop()
|
||||
x_sans_rebord.pop()
|
||||
else:
|
||||
rebord = False
|
||||
|
||||
z_berge_basse = min(z_sans_rebord[0], z_sans_rebord[-1])
|
||||
|
||||
return z_berge_basse, z_sans_rebord, x_sans_rebord
|
||||
|
||||
@property
|
||||
def z_berge_basse(self):
|
||||
return self.geometrie_sans_rebord()[0]
|
||||
|
||||
@property
|
||||
def z_sans_rebord(self):
|
||||
return self.geometrie_sans_rebord()[1]
|
||||
|
||||
@property
|
||||
def x_sans_rebord(self):
|
||||
return self.geometrie_sans_rebord()[2]
|
||||
|
||||
@property
|
||||
def z_fond(self):
|
||||
return np.array(self.z_sans_rebord)
|
||||
|
||||
@property
|
||||
def z_point_bas(self):
|
||||
"""
|
||||
Returns: la cote (Zmin) du point le plus bas.
|
||||
"""
|
||||
return min(self.y)
|
||||
|
||||
@property
|
||||
def delta_x(self):
|
||||
""""
|
||||
Returns: la longueur entre les limites de la vue sur l'axe des x,
|
||||
c'est-à-dire |x_max_visible - x_min_visible|.
|
||||
"""
|
||||
xgauche, xdroite = self.ax.get_xlim()
|
||||
delta_x = abs(xdroite - xgauche)
|
||||
return delta_x
|
||||
|
||||
@property
|
||||
def delta_y(self):
|
||||
"""
|
||||
Returns: la longueur entre les limites de la vue sur l'axe des y,
|
||||
c'est à dire |y_max_visible - y_min_visible|.
|
||||
"""
|
||||
ybas, yhaut = self.ax.get_ylim()
|
||||
delta_y = abs(yhaut - ybas)
|
||||
return delta_y
|
||||
|
||||
@staticmethod
|
||||
def indice_points_onpick(event):
|
||||
"""
|
||||
Args: event
|
||||
Returns: le(s) indexe(s) du/des point(s) (plus précisement les
|
||||
coordonnées de points) capturé(s) par l'événement onpick
|
||||
(voir picker)
|
||||
"""
|
||||
return event.ind
|
||||
|
||||
def points_onpick(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: une array contenant les coordonées des points qui se
|
||||
trouvent dans la zone définie par l'événement onpick
|
||||
(voir picker)
|
||||
"""
|
||||
thisline = event.artist
|
||||
xdata = thisline.get_xdata()
|
||||
ydata = thisline.get_ydata()
|
||||
points_onpick = np.array(
|
||||
[(xdata[i], ydata[i]) for i in self.indice_points_onpick(event)]
|
||||
)
|
||||
return points_onpick
|
||||
|
||||
def distance_normee(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: la liste des distances normées (en m) entre les points
|
||||
situés dans la région définie par l'événement onpick
|
||||
(voir picker).
|
||||
"""
|
||||
ind = event.ind
|
||||
thisline = event.artist
|
||||
xdata = thisline.get_xdata()
|
||||
ydata = thisline.get_ydata()
|
||||
points_onpick = np.array([(xdata[i], ydata[i]) for i in ind])
|
||||
distances_normees = [
|
||||
(((x - event.mouseevent.xdata) / self.delta_x) ** 2 +
|
||||
((y - event.mouseevent.ydata) / self.delta_y) ** 2) ** (1 / 2)
|
||||
for (x, y) in points_onpick
|
||||
]
|
||||
return distances_normees
|
||||
|
||||
def position_souris(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: la position de la souris
|
||||
"""
|
||||
self.pos_souris = [(event.mouseevent.xdata, event.mouseevent.ydata)]
|
||||
return self.pos_souris
|
||||
|
||||
def distance_ecran(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: la liste des distances 'visuelles' entre les points situés
|
||||
dans la région définie par l'événement onpick (voir picker).
|
||||
"""
|
||||
bbox = self.ax.get_window_extent()\
|
||||
.transformed(self.ax.figure.dpi_scale_trans.inverted())
|
||||
ratio_w_sur_h = bbox.width / bbox.height
|
||||
distances_ecran = [
|
||||
(
|
||||
(
|
||||
(x - event.mouseevent.xdata) /
|
||||
(self.delta_x * ratio_w_sur_h)
|
||||
) ** 2 +
|
||||
(
|
||||
(y - event.mouseevent.ydata) /
|
||||
self.delta_y
|
||||
) ** 2
|
||||
) ** (1 / 2)
|
||||
for (x, y) in self.points_onpick(event)
|
||||
]
|
||||
|
||||
return distances_ecran
|
||||
|
||||
def distances(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: la liste des distances entre la position de la souris et
|
||||
tous les points se trouvant dans la zone définie par
|
||||
l'événement onpick (voir picker)
|
||||
"""
|
||||
distances = np.linalg.norm(
|
||||
self.points_onpick(event) - self.position_souris(event),
|
||||
axis=1
|
||||
)
|
||||
|
||||
return distances
|
||||
|
||||
def index_pt_plus_proche_ecran(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: indice du point le plus proche visuellement de la position
|
||||
du click.
|
||||
"""
|
||||
dataidx_ecran = np.argmin(self.distance_ecran(event))
|
||||
return dataidx_ecran
|
||||
|
||||
def point_plus_proche_ecran(self, event):
|
||||
point_onpick = self.points_onpick(event)
|
||||
datapos_ecran = point_onpick[
|
||||
self.index_pt_plus_proche_ecran(event)
|
||||
]
|
||||
|
||||
return self.points_onpick(event)[
|
||||
self.index_pt_plus_proche_ecran(event)
|
||||
]
|
||||
|
||||
def index_pt_plus_proche(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: indice du point le plus proche de la position du click.
|
||||
"""
|
||||
dataidx = np.argmin(self.distances(event))
|
||||
|
||||
return dataidx
|
||||
|
||||
def point_plus_proche(self, event):
|
||||
"""
|
||||
Args:
|
||||
event:
|
||||
Returns: point le plus proche de la position du click
|
||||
"""
|
||||
point_onpick = self.points_onpick(event)
|
||||
datapos = point_onpick[self.index_pt_plus_proche(event)]
|
||||
|
||||
return datapos
|
||||
|
||||
def annotate_onpick(self, x, y):
|
||||
"""
|
||||
Args:
|
||||
x: abscisse du point à annoter.
|
||||
y: ordonnée du point à annoter.
|
||||
Returns: annote le point xy avec du texte text = xytext.
|
||||
"""
|
||||
return self.ax.annotate(
|
||||
"X", xytext=(x, y),
|
||||
xy=(x, y), fontsize=9,
|
||||
bbox=dict(
|
||||
boxstyle='round,pad=0.8', fc='yellow', alpha=0.75
|
||||
),
|
||||
arrowprops=dict(
|
||||
arrowstyle='->',
|
||||
connectionstyle='arc3,rad=0.',
|
||||
color='blue'
|
||||
)
|
||||
)
|
||||
|
||||
def on_ylims_change(self, event_ax):
|
||||
return event_ax.get_ylim()
|
||||
|
||||
def annotate_onclick(self, event):
|
||||
if self.z_point_bas <= event.ydata:
|
||||
self.count += 1
|
||||
|
||||
if event.ydata <= self.z_berge_basse:
|
||||
A, p, L = self.calcul_ligne_eau(event.ydata)
|
||||
else:
|
||||
event.ydata = self.z_berge_basse
|
||||
A, p, L = self.calcul_ligne_eau(event.ydata)
|
||||
|
||||
etiq = f"Z = {event.ydata:.3f} m, A = {A:.3f} "\
|
||||
f"m\u00B2, p = {p:.3f} m, L = {L:.3f} m"
|
||||
self.annotation_onclick.set_text(etiq)
|
||||
x_min, x_max = self.ax.get_xlim()
|
||||
self.pos_x_annotation = x_min + ((x_max - x_min) / 2)
|
||||
|
||||
percent = 0
|
||||
y_ecran_lim = ((max(self.ax.set_ylim()) -
|
||||
min(self.ax.set_ylim())) / 2)
|
||||
|
||||
if abs(y_ecran_lim) > 4:
|
||||
percent = 0.05
|
||||
elif 4 < abs(y_ecran_lim) < 1.5:
|
||||
percent = 0.01
|
||||
elif 0.5 < abs(y_ecran_lim) < 1.5:
|
||||
percent = 0.05
|
||||
elif 0.25 < abs(y_ecran_lim) < 0.5:
|
||||
percent = 0.08
|
||||
elif 0 < abs(y_ecran_lim) < 0.25:
|
||||
percent = 0.25
|
||||
else:
|
||||
percent = 0.1
|
||||
|
||||
cte = 0.
|
||||
if abs(event.ydata) < 100:
|
||||
cte = 0.05
|
||||
else:
|
||||
cte = event.y * 0.1 / 100
|
||||
|
||||
self.y_pos_text_param_hydrau = event.ydata + cte
|
||||
self.annotation_onclick.set_position(
|
||||
(self.pos_x_annotation,
|
||||
self.y_pos_text_param_hydrau)
|
||||
)
|
||||
|
||||
self.ax.callbacks.connect('ylim_changed', self.on_ylims_change)
|
||||
|
||||
self.annotation_onclick.set_color("DarkBlue")
|
||||
self.annotation_onclick.set_visible(True)
|
||||
self.annotation_onclick.set_horizontalalignment('center')
|
||||
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
|
||||
return self.annotation_onclick
|
||||
|
||||
def largeur_au_miroir(self, event):
|
||||
if event.ydata <= self.z_berge_basse:
|
||||
self._largeur_miroir.set_data(
|
||||
[min(self.x), max(self.x)],
|
||||
[event.ydata, event.ydata]
|
||||
)
|
||||
else:
|
||||
self._largeur_miroir.set_data(
|
||||
[min(self.x), max(self.x)],
|
||||
[self.z_berge_basse, self.z_berge_basse]
|
||||
)
|
||||
|
||||
return self._largeur_miroir
|
||||
|
||||
def onpick(self, event):
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
|
||||
if modifiers == Qt.ControlModifier:
|
||||
if event.mouseevent.inaxes == self.ax:
|
||||
self.select_qtableview_row(event)
|
||||
x_proche, y_proche = self.point_plus_proche_ecran(event)
|
||||
self.update_select_point_point_bis(x_proche, y_proche)
|
||||
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
|
||||
def onclick(self, event):
|
||||
modifiers = QtWidgets.QApplication.keyboardModifiers()
|
||||
if modifiers == Qt.ShiftModifier:
|
||||
if event.inaxes == self.ax:
|
||||
if self.z_point_bas < event.ydata:
|
||||
try:
|
||||
self.poly_col_bis.remove()
|
||||
self.largeur_au_miroir(event)
|
||||
except Exception:
|
||||
self.largeur_au_miroir(event)
|
||||
|
||||
self.annotate_onclick(event)
|
||||
self.ax.figure.canvas.draw_idle()
|
||||
|
||||
def remplir_zone_mouillee(self, x, y1, y2):
|
||||
"""
|
||||
Args:
|
||||
x: Les coordonnées x des nœuds définissant la courbe.
|
||||
y1: points définisant le polygone à déssiner.
|
||||
y2: points définisant le polygone à déssiner.
|
||||
Returns: dessine et colorie la région définie par le polygone.
|
||||
"""
|
||||
return self.ax.fill_between(
|
||||
x, y1=y1, y2=y2, where=y1 > y2, interpolate=True,
|
||||
facecolor='skyblue', alpha=0.7
|
||||
)
|
||||
|
||||
def calcul_ligne_eau(self, val: float) -> (float, float, float):
|
||||
"""
|
||||
Args:
|
||||
val: Valeur de la cote Z à laquelle on veut caluler A , p et L.
|
||||
Returns: la valeur de la section mouillée A, du périmètre mouillé p
|
||||
et de la largeur au miroir L.
|
||||
"""
|
||||
largeur_miroir = 0.
|
||||
section_mouillee_totale = 0.
|
||||
perimetre_mouille_total = 0.
|
||||
|
||||
if self.z_point_bas < val <= self.z_berge_basse:
|
||||
z_eau = np.array([val] * (len(self.z_sans_rebord)))
|
||||
self.poly_col_bis = self.remplir_zone_mouillee(self.x_sans_rebord,
|
||||
z_eau,
|
||||
self.z_sans_rebord)
|
||||
liste_chemins = self.poly_col_bis.get_paths()
|
||||
couleurs = ['crimson', 'pink'] * len(liste_chemins)
|
||||
aire_calculee_shapely = None
|
||||
perimetre_mouille_total_shapely = None
|
||||
perimetre_shapely = 0.
|
||||
perim_calc = 0.
|
||||
|
||||
for polyg, coul in zip(
|
||||
liste_chemins,
|
||||
couleurs[0:len(liste_chemins)]
|
||||
):
|
||||
points_polygone = polyg.vertices
|
||||
xs = points_polygone[:, 0]
|
||||
ys = points_polygone[:, 1]
|
||||
|
||||
liste_points_miroir = [
|
||||
x for (x, y) in zip(xs, ys) if np.isclose(y, val)
|
||||
]
|
||||
largeur_miroir_polygone = liste_points_miroir[-2] - \
|
||||
liste_points_miroir[0]
|
||||
largeur_miroir += largeur_miroir_polygone
|
||||
|
||||
polygone_shapely = ShapelyPolygon(points_polygone)
|
||||
aire_calculee_shapely = polygone_shapely.area
|
||||
perimetre_shapely = polygone_shapely.length
|
||||
perimetre_mouille_total_shapely = (
|
||||
polygone_shapely.length - largeur_miroir
|
||||
)
|
||||
liste_points_fond = [
|
||||
(x, y) for (x, y) in zip(xs, ys) if not np.isclose(y, val)
|
||||
]
|
||||
x_pt_prec, y_pt_prec = max(liste_points_miroir), val
|
||||
|
||||
perimetre = 0
|
||||
aire = 0
|
||||
|
||||
for un_point in liste_points_fond + [
|
||||
(min(liste_points_miroir), val)
|
||||
]:
|
||||
x_pt_suivant, y_pt_suivant = un_point
|
||||
perimetre += ((x_pt_prec - x_pt_suivant) ** 2 +
|
||||
(y_pt_prec - y_pt_suivant) ** 2) ** (1 / 2)
|
||||
aire += (((val - y_pt_prec) + (val - y_pt_suivant)) *
|
||||
abs(x_pt_suivant - x_pt_prec) / 2)
|
||||
x_pt_prec, y_pt_prec = x_pt_suivant, y_pt_suivant
|
||||
perim_calc = perimetre
|
||||
|
||||
perimetre_mouille_total = perimetre_shapely - largeur_miroir
|
||||
section_mouillee_totale = aire_calculee_shapely
|
||||
|
||||
return section_mouillee_totale, perimetre_mouille_total, largeur_miroir
|
||||
Loading…
Reference in New Issue