wolfhece 2.2.27__py3-none-any.whl → 2.2.29__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wolfhece/PyConfig.py +27 -3
- wolfhece/PyDraw.py +202 -22
- wolfhece/PyPalette.py +18 -0
- wolfhece/PyVertexvectors.py +156 -25
- wolfhece/PyWMS.py +6 -3
- wolfhece/__init__.py +27 -0
- wolfhece/acceptability/acceptability.py +25 -20
- wolfhece/acceptability/acceptability_gui.py +150 -92
- wolfhece/acceptability/func.py +169 -82
- wolfhece/apps/version.py +1 -1
- wolfhece/irm_qdf.py +71 -7
- wolfhece/lb7208_ntv2/__init__.py +0 -0
- wolfhece/lb7208_ntv2/be_ign_README.txt +36 -0
- wolfhece/lb7208_ntv2/be_ign_bd72lb72_etrs89lb08.tif +0 -0
- wolfhece/lb7208_ntv2/be_ign_hBG18.tif +0 -0
- wolfhece/mesh2d/gpu_2d.py +11 -2
- wolfhece/report/common.py +496 -0
- wolfhece/report/compare_arrays.py +1054 -0
- wolfhece/report/simplesimgpu.py +26 -101
- wolfhece/report/tools.py +121 -16
- wolfhece/scenario/config_manager.py +265 -8
- wolfhece/ui/wolf_multiselection_collapsiblepane.py +153 -1
- wolfhece/wolf_array.py +116 -64
- wolfhece/wolf_texture.py +4 -0
- {wolfhece-2.2.27.dist-info → wolfhece-2.2.29.dist-info}/METADATA +1 -1
- {wolfhece-2.2.27.dist-info → wolfhece-2.2.29.dist-info}/RECORD +29 -23
- {wolfhece-2.2.27.dist-info → wolfhece-2.2.29.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.27.dist-info → wolfhece-2.2.29.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.27.dist-info → wolfhece-2.2.29.dist-info}/top_level.txt +0 -0
wolfhece/report/simplesimgpu.py
CHANGED
@@ -11,6 +11,7 @@ from tempfile import NamedTemporaryFile
|
|
11
11
|
from datetime import datetime as dt
|
12
12
|
|
13
13
|
import matplotlib.pyplot as plt
|
14
|
+
import matplotlib as mpl
|
14
15
|
import seaborn as sns
|
15
16
|
|
16
17
|
import pymupdf as pdf
|
@@ -19,111 +20,18 @@ from wolfgpu.simple_simulation import InfiltrationChronology, SimulationDuration
|
|
19
20
|
from wolfgpu.simple_simulation import boundary_condition_2D, BoundaryConditionsTypes
|
20
21
|
|
21
22
|
from .pdf import PDFViewer
|
23
|
+
from .common import cm2pts, A4_rect, rect_cm, cm2inches, list_to_html, list_to_html_aligned, get_rect_from_text
|
22
24
|
from ..wolf_array import WolfArray, header_wolf
|
23
25
|
from ..PyTranslate import _
|
24
26
|
from .. import __version__ as wolfhece_version
|
25
27
|
from wolfgpu.version import __version__ as wolfgpu_version
|
26
28
|
|
27
|
-
def cm2pts(cm):
|
28
|
-
""" Convert centimeters to points for PyMuPDF.
|
29
|
-
|
30
|
-
One point equals 1/72 inches.
|
31
|
-
"""
|
32
|
-
return cm * 28.346456692913385 # 1 cm = 28.346456692913385 points = 72/2.54
|
33
|
-
|
34
|
-
def A4_rect():
|
35
|
-
""" Return the A4 rectangle in PyMuPDF units.
|
36
|
-
|
37
|
-
(0, 0) is the top-left corner in PyMuPDF coordinates.
|
38
|
-
"""
|
39
|
-
return pdf.Rect(0, 0, cm2pts(21), cm2pts(29.7)) # A4 size in points (PDF units)
|
40
|
-
|
41
|
-
def rect_cm(x, y, width, height):
|
42
|
-
""" Create a rectangle in PyMuPDF units from centimeters.
|
43
|
-
|
44
|
-
(0, 0) is the top-left corner in PyMuPDF coordinates.
|
45
|
-
"""
|
46
|
-
return pdf.Rect(cm2pts(x), cm2pts(y), cm2pts(x) + cm2pts(width), cm2pts(y) + cm2pts(height))
|
47
|
-
|
48
|
-
def get_rect_from_text(text, width, fontsize=10, padding=5):
|
49
|
-
""" Get a rectangle that fits the text in PyMuPDF units.
|
50
|
-
|
51
|
-
:param text: The text to fit in the rectangle.
|
52
|
-
:param width: The width of the rectangle in centimeters.
|
53
|
-
:param fontsize: The font size in points.
|
54
|
-
:param padding: Padding around the text in points.
|
55
|
-
:return: A PyMuPDF rectangle that fits the text.
|
56
|
-
"""
|
57
|
-
# Create a temporary PDF document to measure the text size
|
58
|
-
with NamedTemporaryFile(delete=True, suffix='.pdf') as temp_pdf:
|
59
|
-
doc = pdf.Document()
|
60
|
-
page = doc.new_page(A4_rect())
|
61
|
-
text_rect = page.insert_text((0, 0), text, fontsize=fontsize, width=cm2pts(width))
|
62
|
-
doc.save(temp_pdf.name)
|
63
|
-
|
64
|
-
# Get the size of the text rectangle
|
65
|
-
text_width = text_rect.width + padding * 2
|
66
|
-
text_height = text_rect.height + padding * 2
|
67
|
-
# Create a rectangle with the specified width and height
|
68
|
-
rect = pdf.Rect(0, 0, cm2pts(width), text_height)
|
69
|
-
# Adjust the rectangle to fit the text
|
70
|
-
rect.x0 -= padding
|
71
|
-
rect.y0 -= padding
|
72
|
-
rect.x1 += padding
|
73
|
-
rect.y1 += padding
|
74
|
-
return rect
|
75
|
-
|
76
|
-
|
77
|
-
def list_to_html(list_items, font_size="10pt", font_family="Helvetica"):
|
78
|
-
# Génère le CSS
|
79
|
-
css = f"""
|
80
|
-
ul.custom-list {{
|
81
|
-
font-size: {font_size};
|
82
|
-
font-family: {font_family};
|
83
|
-
color: #2C3E50;
|
84
|
-
padding-left: 20px;
|
85
|
-
}}
|
86
|
-
li {{
|
87
|
-
margin-bottom: 5px;
|
88
|
-
}}
|
89
|
-
"""
|
90
|
-
|
91
|
-
# Génère le HTML
|
92
|
-
html = "<ul class='custom-list'>\n"
|
93
|
-
for item in list_items:
|
94
|
-
html += f" <li>{item}</li>\n"
|
95
|
-
html += "</ul>"
|
96
|
-
|
97
|
-
return html, css
|
98
|
-
|
99
|
-
|
100
|
-
def list_to_html_aligned(list_items, font_size="10pt", font_family="Helvetica"):
|
101
|
-
# Génère le CSS
|
102
|
-
css = f"""
|
103
|
-
ul.custom-list {{
|
104
|
-
font-size: {font_size};
|
105
|
-
font-family: {font_family};
|
106
|
-
color: #2C3E50;
|
107
|
-
padding-left: 20px;
|
108
|
-
}}
|
109
|
-
li {{
|
110
|
-
margin-bottom: 5px;
|
111
|
-
}}
|
112
|
-
"""
|
113
|
-
|
114
|
-
# Génère le HTML
|
115
|
-
html = "<div class='custom-list'>\n"
|
116
|
-
html = " - ".join(list_items) # Join the items with a hyphen
|
117
|
-
html += "</div>"
|
118
|
-
|
119
|
-
return html, css
|
120
|
-
|
121
|
-
|
122
29
|
class SimpleSimGPU_Report():
|
123
30
|
|
124
31
|
def __init__(self, sim:SimpleSimulation | Path | str, **kwargs):
|
125
32
|
""" Initialize the Simple Simulation GPU Report Viewer """
|
126
33
|
|
34
|
+
self._summary = {}
|
127
35
|
self._doc = None
|
128
36
|
|
129
37
|
if isinstance(sim, Path):
|
@@ -132,6 +40,7 @@ class SimpleSimGPU_Report():
|
|
132
40
|
except Exception as e:
|
133
41
|
logging.error(f"Failed to load simulation from path {sim}: {e}")
|
134
42
|
self._sim = None
|
43
|
+
self._summary['errors'] = e
|
135
44
|
return
|
136
45
|
elif isinstance(sim, str):
|
137
46
|
try:
|
@@ -139,6 +48,7 @@ class SimpleSimGPU_Report():
|
|
139
48
|
except Exception as e:
|
140
49
|
logging.error(f"Failed to load simulation from string path {sim}: {e}")
|
141
50
|
self._sim = None
|
51
|
+
self._summary['errors'] = e
|
142
52
|
return
|
143
53
|
elif not isinstance(sim, SimpleSimulation):
|
144
54
|
try:
|
@@ -146,12 +56,12 @@ class SimpleSimGPU_Report():
|
|
146
56
|
except Exception as e:
|
147
57
|
logging.error(f"Failed to set simulation: {e}")
|
148
58
|
self._sim = None
|
59
|
+
self._summary['errors'] = e
|
149
60
|
return
|
150
61
|
else:
|
151
62
|
logging.error("Invalid type for simulation. Must be SimpleSimulation, Path, or str.")
|
152
63
|
return
|
153
64
|
|
154
|
-
self._summary = {}
|
155
65
|
self._summary['warnings'] = self._summary_warnings()
|
156
66
|
self._summary['errors'] = self._summary_errors()
|
157
67
|
|
@@ -366,9 +276,13 @@ class SimpleSimGPU_Report():
|
|
366
276
|
# Plot the histogram of waterdepth adn add it to the PDF
|
367
277
|
fig, ax = plt.subplots(figsize=(8, 6))
|
368
278
|
# Plot the histogram of water depth
|
369
|
-
|
370
|
-
|
371
|
-
|
279
|
+
|
280
|
+
h_min = np.min(sim.h[sim.nap == 1])
|
281
|
+
h_max = np.max(sim.h[sim.nap == 1])
|
282
|
+
|
283
|
+
if h_max > h_min:
|
284
|
+
ax.hist(sim.h[sim.h > 0.], bins=100, density=True)
|
285
|
+
ax.set_xlim(0, h_max) # Set xlim to 110% of max value
|
372
286
|
ax.set_xlabel('Water Depth [m]')
|
373
287
|
ax.set_ylabel('Frequency')
|
374
288
|
|
@@ -395,7 +309,7 @@ class SimpleSimGPU_Report():
|
|
395
309
|
ax.hist(sim.manning[sim.nap == 1], bins=100, density = True)
|
396
310
|
# ax.set_title('Histogram of Manning Coefficient')
|
397
311
|
ax.set_xlabel('Manning [$\\frac {s} {m^{1/3}} $]')
|
398
|
-
ax.set_xlim(0, np.max(sim.manning[sim.nap == 1]) * 1.
|
312
|
+
ax.set_xlim(0, np.max(sim.manning[sim.nap == 1]) * 1.1) # Set xlim to 110% of max value
|
399
313
|
ax.set_ylabel('Frequency')
|
400
314
|
|
401
315
|
# set font size of the labels
|
@@ -865,15 +779,21 @@ class SimpleSimGPU_Report():
|
|
865
779
|
|
866
780
|
class SimpleSimGPU_Report_wx(PDFViewer):
|
867
781
|
|
868
|
-
def __init__(self, sim:SimpleSimulation | Path | str, **kwargs):
|
782
|
+
def __init__(self, sim:SimpleSimulation | Path | str, show:bool=False, **kwargs):
|
869
783
|
""" Initialize the Simple Simulation GPU Report Viewer """
|
870
784
|
|
785
|
+
mpl.use('Agg') # Use a non-interactive backend for matplotlib
|
786
|
+
|
871
787
|
super(SimpleSimGPU_Report_wx, self).__init__(None, **kwargs)
|
872
788
|
|
873
789
|
self._report = SimpleSimGPU_Report(sim, **kwargs)
|
874
790
|
|
875
791
|
if self._report._sim is None:
|
876
792
|
logging.error("No simulation data available to create report.")
|
793
|
+
dlg = wx.MessageDialog(self, "No simulation data available to create report.\n\nPlease check the errors in the logs.",
|
794
|
+
"Error", wx.OK | wx.ICON_ERROR)
|
795
|
+
dlg.ShowModal()
|
796
|
+
dlg.Destroy()
|
877
797
|
return
|
878
798
|
|
879
799
|
self._report.create_report()
|
@@ -892,6 +812,11 @@ class SimpleSimGPU_Report_wx(PDFViewer):
|
|
892
812
|
|
893
813
|
self.Bind(wx.EVT_CLOSE, self.on_close)
|
894
814
|
|
815
|
+
if show:
|
816
|
+
self.Show()
|
817
|
+
|
818
|
+
mpl.use('WxAgg') # Reset matplotlib to use the WxAgg backend for other plots
|
819
|
+
|
895
820
|
def on_close(self, event):
|
896
821
|
""" Handle the close event of the frame """
|
897
822
|
|
wolfhece/report/tools.py
CHANGED
@@ -376,34 +376,57 @@ class Analysis_Scenarios():
|
|
376
376
|
|
377
377
|
def __init__(self, base_directory: Path | str, storage_directory: Path | str = None, name:str = ''):
|
378
378
|
|
379
|
+
# Default figure size for plots
|
380
|
+
self._fig_size = (20, 10)
|
381
|
+
|
382
|
+
# Name of the analysis scenarios instance
|
379
383
|
self.name = name.strip() if name else 'Analysis_Scenarios'
|
384
|
+
|
385
|
+
# Base directory for the analysis scenarios
|
386
|
+
# Must contains the directories 'projets', 'rapports', 'vecteurs', 'nuages_de_points', 'images' and 'cache'
|
380
387
|
self.base_directory = Path(base_directory)
|
388
|
+
|
389
|
+
# Storage directory for the analysis scenarios - Default is the base directory
|
381
390
|
self.storage_directory = storage_directory if storage_directory is not None else self.base_directory
|
382
391
|
|
392
|
+
# Check if the base directory exists and contains the necessary directories
|
383
393
|
self.check_directories()
|
394
|
+
# Fill the directories attribute with the paths of the analysis directories
|
384
395
|
self.directories = get_directories_as_dict(self.base_directory)
|
385
396
|
|
397
|
+
# Initialize the scenarios directories
|
386
398
|
self.scenarios_directories:dict[str:Path]
|
387
399
|
self.scenarios_directories = {}
|
400
|
+
# List of scenario names
|
388
401
|
self.scenarios = []
|
389
402
|
|
390
403
|
self.current_scenario = None
|
391
|
-
self.
|
404
|
+
self._return_periods = []
|
405
|
+
|
406
|
+
# RapidReport associated with the analysis scenarios
|
407
|
+
self.report:RapidReport = None
|
392
408
|
self._report_name = 'analysis_report.docx'
|
393
409
|
self._report_saved_once = False
|
394
|
-
self.mapviewer = None
|
395
|
-
self._background_images = None
|
396
410
|
|
397
|
-
|
411
|
+
# Map viewer for the analysis scenarios
|
412
|
+
self.mapviewer:WolfMapViewer = None
|
398
413
|
|
399
|
-
|
414
|
+
# Name of the WMS service
|
415
|
+
self._background_images:str = None
|
416
|
+
|
417
|
+
# Polygons associated with the analysis scenarios
|
418
|
+
self._polygons:dict[str, Polygons_Analyze] = {}
|
400
419
|
|
420
|
+
self._reference_polygon:Polygons_Analyze = None
|
421
|
+
|
422
|
+
# Modifications associated with the analysis scenarios
|
401
423
|
self._modifications = {}
|
402
424
|
|
425
|
+
# MultiProjects associated with the analysis scenarios.
|
426
|
+
# One project contains multiple suimulations.
|
403
427
|
self._multiprojects = None
|
404
|
-
self._cached_date = False
|
405
428
|
|
406
|
-
|
429
|
+
# Landmarks and measures associated with the analysis
|
407
430
|
self._landmarks:Zones = None
|
408
431
|
self._landmarks_s_label = []
|
409
432
|
|
@@ -414,10 +437,32 @@ class Analysis_Scenarios():
|
|
414
437
|
self._cloud:list[tuple[float, float, str]] = [] # List of tuples (s, z, label) for point clouds
|
415
438
|
|
416
439
|
self._images = {}
|
417
|
-
|
440
|
+
|
441
|
+
# Zoom (smin, smax, zmin, zmax) for the analysis
|
442
|
+
self._zoom:dict[str, tuple[float,float,float,float]] = {}
|
418
443
|
|
419
444
|
logging.info(f"Analysis directories initialized: {self.directories}")
|
420
445
|
|
446
|
+
@property
|
447
|
+
def fig_size(self) -> tuple[float]:
|
448
|
+
""" Return the default figure size for plots.
|
449
|
+
|
450
|
+
:return: A tuple (width, height) representing the default figure size.
|
451
|
+
"""
|
452
|
+
return self._fig_size
|
453
|
+
|
454
|
+
@fig_size.setter
|
455
|
+
def fig_size(self, size:tuple[float]) -> None:
|
456
|
+
""" Set the default figure size for plots.
|
457
|
+
|
458
|
+
:param size: A tuple (width, height) representing the default figure size.
|
459
|
+
"""
|
460
|
+
if not isinstance(size, tuple) or len(size) != 2:
|
461
|
+
logging.error("Default figure size must be a tuple of (width, height).")
|
462
|
+
raise ValueError("Default figure size must be a tuple of (width, height).")
|
463
|
+
self._fig_size = size
|
464
|
+
logging.info(f"Default figure size set to {self._fig_size}.")
|
465
|
+
|
421
466
|
def add_zoom(self, label:str, bounds:tuple[float]) -> None:
|
422
467
|
""" Add a zoom level to the analysis.
|
423
468
|
|
@@ -669,7 +714,13 @@ class Analysis_Scenarios():
|
|
669
714
|
logging.info(f"Landmark '{label}' added at coordinates ({x}, {y}).")
|
670
715
|
|
671
716
|
def update_landmark(self, label: str, s_xy:float |tuple[float] = None, z: float = None) -> None:
|
672
|
-
""" Update a landmark in the analysis.
|
717
|
+
""" Update a landmark in the analysis.
|
718
|
+
|
719
|
+
:param label: The label of the landmark to update.
|
720
|
+
:param s_xy: The s-coordinate or a tuple (s, xy) to update the landmark's position.
|
721
|
+
:param z: The z-coordinate to update the landmark's elevation (optional).
|
722
|
+
"""
|
723
|
+
|
673
724
|
if self._landmarks is None:
|
674
725
|
logging.error("No landmarks have been added to the analysis.")
|
675
726
|
raise ValueError("No landmarks have been added to the analysis.")
|
@@ -701,7 +752,12 @@ class Analysis_Scenarios():
|
|
701
752
|
self._landmarks_s_label[i] = (s_xy, z, label)
|
702
753
|
|
703
754
|
def plot_cloud(self, ax: plt.Axes, bounds:tuple[float]) -> plt.Axes:
|
755
|
+
""" Trace the cloud of points on an axis Matplotlib
|
704
756
|
|
757
|
+
:param ax: axe Matplotlib
|
758
|
+
:param bounds: tuple (xmin, xmax, ymin, ymax) for the plot limits
|
759
|
+
:return: The Matplotlib Axes object with the cloud plotted.
|
760
|
+
"""
|
705
761
|
xmin, xmax, ymin, ymax = bounds
|
706
762
|
|
707
763
|
used_cloud = [(s, z, label) for s, z, label in self._cloud if s >= xmin and s <= xmax]
|
@@ -713,7 +769,16 @@ class Analysis_Scenarios():
|
|
713
769
|
return ax
|
714
770
|
|
715
771
|
def plot_measures(self, ax:plt.Axes, bounds:tuple[float], style:dict = None) -> plt.Axes:
|
772
|
+
""" Trace les mesures sur un axe Matplotlib
|
773
|
+
|
774
|
+
:param ax: axe Matplotlib
|
775
|
+
:param bounds: tuple (xmin, xmax, ymin, ymax) for the plot limits
|
776
|
+
:param style: Optional style dictionary for the measures. Available properties:
|
777
|
+
- 'color': The color of the line.
|
778
|
+
- 'linestyle': The style of the line (e.g., '-', '--', '-.', ':').
|
716
779
|
|
780
|
+
:return: The Matplotlib Axes object with the measures plotted.
|
781
|
+
"""
|
717
782
|
xmin, xmax, ymin, ymax = bounds
|
718
783
|
i=0
|
719
784
|
for key, measure in self._projected_measures.items():
|
@@ -777,13 +842,17 @@ class Analysis_Scenarios():
|
|
777
842
|
bounds:tuple[float] | str,
|
778
843
|
operator:operators = operators.MEDIAN,
|
779
844
|
plot_annex:bool = True,
|
780
|
-
save:bool = False
|
845
|
+
save:bool = False,
|
846
|
+
figsize:tuple[float] = None) -> tuple[plt.Figure, plt.Axes]:
|
781
847
|
""" Plot the waterlines for a specific scenario.
|
782
848
|
|
783
849
|
:param scenario: The name of the scenario to plot waterlines for or a list of scenarios for comparison.
|
784
850
|
:param bounds: A tuple (xmin, xmax, ymin, ymax) representing the zoom bounds or a string label for a zoom level.
|
785
851
|
:param operator: The operator to apply on the waterlines.
|
786
852
|
:param save: If True, save the plot as an image file.
|
853
|
+
:param figsize: A tuple (width, height) representing the size of the figure. If None, uses the default figure size.
|
854
|
+
:param plot_annex: If True, plot the cloud of points, measures, and landmarks.
|
855
|
+
:return: A tuple (fig, ax) where fig is the matplotlib Figure and ax is the matplotlib Axes object.
|
787
856
|
"""
|
788
857
|
|
789
858
|
if isinstance(bounds, str):
|
@@ -850,7 +919,14 @@ class Analysis_Scenarios():
|
|
850
919
|
|
851
920
|
filename = self.directories[Directory_Analysis.IMAGES] / f"{self.name}_{ref}_{str(xmin)}_{str(xmax)}_waterlines_comparison.png"
|
852
921
|
|
853
|
-
|
922
|
+
if figsize is not None:
|
923
|
+
if not isinstance(figsize, tuple) or len(figsize) != 2:
|
924
|
+
logging.error("Figure size must be a tuple of (width, height).")
|
925
|
+
raise ValueError("Figure size must be a tuple of (width, height).")
|
926
|
+
fig.set_size_inches(figsize)
|
927
|
+
else:
|
928
|
+
fig.set_size_inches(self.fig_size[0], self.fig_size[1]) # Use the default figure size
|
929
|
+
|
854
930
|
ax.legend()
|
855
931
|
#zoomA
|
856
932
|
ax.set_xlim(xmin, xmax)
|
@@ -879,7 +955,8 @@ class Analysis_Scenarios():
|
|
879
955
|
bounds:tuple[float] | str,
|
880
956
|
operator:operators = operators.MEDIAN,
|
881
957
|
plot_annex:bool = True,
|
882
|
-
save:bool = False
|
958
|
+
save:bool = False,
|
959
|
+
figsize:tuple[float] = None) -> tuple[plt.Figure, plt.Axes]:
|
883
960
|
""" Plot the heads for a specific scenario.
|
884
961
|
|
885
962
|
:param scenario: The name of the scenario to plot heads for or a list of scenarios for comparison.
|
@@ -887,6 +964,8 @@ class Analysis_Scenarios():
|
|
887
964
|
:param operator: The operator to apply on the heads.
|
888
965
|
:param plot_annex: If True, plot the cloud of points, measures, and landmarks.
|
889
966
|
:param save: If True, save the plot as an image file.
|
967
|
+
:param figsize: A tuple (width, height) representing the figure size. If None, use the default figure size.
|
968
|
+
:return: A tuple (fig, ax) representing the figure and axes of the plot
|
890
969
|
"""
|
891
970
|
if isinstance(bounds, str):
|
892
971
|
# If bounds is a string, assume it's a label for a zoom level
|
@@ -922,7 +1001,14 @@ class Analysis_Scenarios():
|
|
922
1001
|
ax.plot(s, z, label=f"{scen_name} - {sim_name}", linestyle='--', linewidth=1.5)
|
923
1002
|
filename = self.directories[Directory_Analysis.IMAGES] / f"{self.name}_{ref}_{str(xmin)}_{str(xmax)}_heads_comparison.png"
|
924
1003
|
|
925
|
-
|
1004
|
+
if figsize is not None:
|
1005
|
+
if not isinstance(figsize, tuple) or len(figsize) != 2:
|
1006
|
+
logging.error("Figure size must be a tuple of (width, height).")
|
1007
|
+
raise ValueError("Figure size must be a tuple of (width, height).")
|
1008
|
+
fig.set_size_inches(figsize)
|
1009
|
+
else:
|
1010
|
+
fig.set_size_inches(self.fig_size[0], self.fig_size[1]) # Use the default figure size
|
1011
|
+
|
926
1012
|
ax.legend()
|
927
1013
|
#zoomA
|
928
1014
|
ax.set_xlim(xmin, xmax)
|
@@ -948,13 +1034,17 @@ class Analysis_Scenarios():
|
|
948
1034
|
bounds:tuple[float] | str,
|
949
1035
|
operator:operators = operators.MEDIAN,
|
950
1036
|
plot_annex:bool = True,
|
951
|
-
save:bool = False
|
1037
|
+
save:bool = False,
|
1038
|
+
figsize:tuple[float] = None) -> tuple[plt.Figure, plt.Axes]:
|
952
1039
|
""" Plot the Froude for a specific scenario.
|
953
1040
|
|
954
1041
|
:param scenario: The name of the scenario to plot waterlines for or a list of scenarios for comparison.
|
955
1042
|
:param bounds: A tuple (xmin, xmax, ymin, ymax) representing the zoom bounds or a string label for a zoom level.
|
956
1043
|
:param operator: The operator to apply on the waterlines.
|
957
1044
|
:param save: If True, save the plot as an image file.
|
1045
|
+
:param figsize: A tuple (width, height) representing the figure size. If None, use the default figure size.
|
1046
|
+
:param plot_annex: If True, plot the cloud of points, measures, and landmarks.
|
1047
|
+
:return: A tuple (fig, ax) representing the figure and axes of the plot
|
958
1048
|
"""
|
959
1049
|
|
960
1050
|
if isinstance(bounds, str):
|
@@ -1015,7 +1105,14 @@ class Analysis_Scenarios():
|
|
1015
1105
|
|
1016
1106
|
filename = self.directories[Directory_Analysis.IMAGES] / f"{self.name}_{ref}_{str(xmin)}_{str(xmax)}_Froude_comparison.png"
|
1017
1107
|
|
1018
|
-
|
1108
|
+
if figsize is not None:
|
1109
|
+
if not isinstance(figsize, tuple) or len(figsize) != 2:
|
1110
|
+
logging.error("Figure size must be a tuple of (width, height).")
|
1111
|
+
raise ValueError("Figure size must be a tuple of (width, height).")
|
1112
|
+
fig.set_size_inches(figsize)
|
1113
|
+
else:
|
1114
|
+
fig.set_size_inches(self.fig_size[0], self.fig_size[1]) # Use the default figure size
|
1115
|
+
|
1019
1116
|
ax.legend()
|
1020
1117
|
#zoomA
|
1021
1118
|
ax.set_xlim(xmin, xmax)
|
@@ -1739,6 +1836,8 @@ class Analysis_Scenarios():
|
|
1739
1836
|
|
1740
1837
|
:param scenario_name: The name of the scenario to set as current.
|
1741
1838
|
"""
|
1839
|
+
|
1840
|
+
scenario_name = _sanitize_scenario_name(scenario_name)
|
1742
1841
|
if scenario_name not in self.scenarios_directories:
|
1743
1842
|
logging.error(f"Scenario '{scenario_name}' not found in the analysis.")
|
1744
1843
|
raise ValueError(f"Scenario '{scenario_name}' not found in the analysis.")
|
@@ -1752,6 +1851,8 @@ class Analysis_Scenarios():
|
|
1752
1851
|
:param scenario_name: The name of the scenario to get.
|
1753
1852
|
:return: The path to the scenario directory.
|
1754
1853
|
"""
|
1854
|
+
|
1855
|
+
scenario_name = _sanitize_scenario_name(scenario_name)
|
1755
1856
|
if scenario_name not in self.scenarios_directories:
|
1756
1857
|
logging.error(f"Scenario '{scenario_name}' not found in the analysis.")
|
1757
1858
|
raise KeyError(f"Scenario '{scenario_name}' not found in the analysis.")
|
@@ -1863,7 +1964,11 @@ L'attention est toutefois attirée sur le fait que cette approche pourrait ne pa
|
|
1863
1964
|
return fig, ax
|
1864
1965
|
|
1865
1966
|
def load_modifications(self, ad2viewer:bool = True):
|
1866
|
-
""" Load modifications for scenarios from vecz files.
|
1967
|
+
""" Load modifications for scenarios from vecz files.
|
1968
|
+
|
1969
|
+
:param ad2viewer: If True, add the modifications to the map viewer.
|
1970
|
+
:raises ValueError: If the MapViewer is not initialized.
|
1971
|
+
"""
|
1867
1972
|
|
1868
1973
|
MODIF = 'bath_assembly.vecz'
|
1869
1974
|
|