wolfhece 2.2.28__py3-none-any.whl → 2.2.30__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.
@@ -4,13 +4,17 @@ import numpy.ma as ma
4
4
  from pathlib import Path
5
5
  import matplotlib.pyplot as plt
6
6
  from enum import Enum
7
- from scipy.ndimage import label, sum_labels
7
+ from scipy.ndimage import label, sum_labels, find_objects
8
8
  import pymupdf as pdf
9
+ import wx
10
+ from tqdm import tqdm
11
+ from matplotlib import use, get_backend
9
12
 
10
13
  from .common import A4_rect, rect_cm, list_to_html, list_to_html_aligned, get_rect_from_text
11
14
  from .common import inches2cm, pts2cm, cm2pts, cm2inches, DefaultLayoutA4, NamedTemporaryFile, pt2inches, TemporaryDirectory
12
15
  from ..wolf_array import WolfArray, header_wolf, vector, zone, Zones, wolfvertex as wv, wolfpalette
13
16
  from ..PyTranslate import _
17
+ from .pdf import PDFViewer
14
18
 
15
19
  class ArrayDifferenceLayout(DefaultLayoutA4):
16
20
  """
@@ -98,8 +102,16 @@ class ArrayDifference():
98
102
 
99
103
  self._background = 'IGN'
100
104
 
105
+ self._contour = None
106
+ self._external_border = None
107
+
101
108
  @property
102
109
  def contour(self) -> vector:
110
+ """ Get the contour of the difference part. """
111
+
112
+ if self._contour is not None and isinstance(self._contour, vector):
113
+ return self._contour
114
+
103
115
  ret = self.reference.suxsuy_contour(abs=True)
104
116
  ret = ret[2]
105
117
 
@@ -111,8 +123,11 @@ class ArrayDifference():
111
123
  @property
112
124
  def external_border(self) -> vector:
113
125
  """
114
- Get the bounds of the reference array.
126
+ Get the bounds of the difference part.
115
127
  """
128
+ if self._external_border is not None and isinstance(self._external_border, vector):
129
+ return self._external_border
130
+
116
131
  ret = vector(name=_("External border"))
117
132
  (xmin, xmax), (ymin, ymax) = self.reference.get_bounds()
118
133
  ret.add_vertex(wv(xmin, ymin))
@@ -194,6 +209,13 @@ class ArrayDifference():
194
209
  Walonmap= True,
195
210
  cat = 'IMAGERIE/ORTHO_2022_ETE',
196
211
  )
212
+ else:
213
+ self.reference.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
214
+ first_mask_data=False, with_legend=False,
215
+ update_palette= False,
216
+ Walonmap= False,
217
+ )
218
+
197
219
 
198
220
  self.reference.array.mask[:,:] = old_mask
199
221
 
@@ -225,11 +247,23 @@ class ArrayDifference():
225
247
  new = WolfArray(srcheader=h)
226
248
  new.array.mask[:,:] = True
227
249
 
228
- new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
229
- first_mask_data=False, with_legend=False,
230
- update_palette= False,
231
- Walonmap= True,
232
- cat = 'IMAGERIE/ORTHO_2022_ETE')
250
+ if self._background.upper() == 'IGN' or self._background.upper() == 'NGI':
251
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
252
+ first_mask_data=False, with_legend=False,
253
+ update_palette= False,
254
+ IGN= True,
255
+ cat = 'orthoimage_coverage')
256
+ elif self._background.upper() == 'WALONMAP':
257
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
258
+ first_mask_data=False, with_legend=False,
259
+ update_palette= False,
260
+ Walonmap= True,
261
+ cat = 'IMAGERIE/ORTHO_2022_ETE')
262
+ else:
263
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
264
+ first_mask_data=False, with_legend=False,
265
+ update_palette= False,
266
+ Walonmap= False)
233
267
 
234
268
  self.external_border.plot_matplotlib(ax=ax)
235
269
 
@@ -295,7 +329,8 @@ class ArrayDifference():
295
329
 
296
330
  fig, ax = figax
297
331
 
298
- ax.hist(self.reference.array.compressed(), density=density, alpha = alpha, **kwargs)
332
+ data = self.reference.array.compressed()
333
+ ax.hist(data, bins = min(100, int(len(data)/4)), density=density, alpha = alpha, **kwargs)
299
334
  # ax.set_xlabel("Value")
300
335
  # ax.set_ylabel("Frequency")
301
336
  return fig, ax
@@ -309,7 +344,8 @@ class ArrayDifference():
309
344
 
310
345
  fig, ax = figax
311
346
 
312
- ax.hist(self.to_compare.array.compressed(), density=density, alpha = alpha, **kwargs)
347
+ data = self.to_compare.array.compressed()
348
+ ax.hist(data, bins= min(100, int(len(data)/4)), density=density, alpha = alpha, **kwargs)
313
349
  # ax.set_xlabel("Value")
314
350
  # ax.set_ylabel("Frequency")
315
351
  return fig, ax
@@ -349,7 +385,7 @@ class ArrayDifference():
349
385
  fig, ax = figax
350
386
 
351
387
  difference_data = self.difference.array.compressed()
352
- ax.hist(difference_data, density=density, alpha=alpha, **kwargs)
388
+ ax.hist(difference_data, bins= min(100, int(len(difference_data)/4)), density=density, alpha=alpha, **kwargs)
353
389
 
354
390
  # ax.set_xlabel("Value")
355
391
  # ax.set_ylabel("Frequency")
@@ -396,7 +432,6 @@ class ArrayDifference():
396
432
  if 'Histogram' in key:
397
433
  fig.tight_layout()
398
434
 
399
-
400
435
  # convert canvas to PNG and insert it into the PDF
401
436
  temp_file = NamedTemporaryFile(delete=False, suffix='.png')
402
437
  fig.savefig(temp_file, format='png', bbox_inches='tight', dpi=self._dpi)
@@ -419,6 +454,7 @@ class ArrayDifference():
419
454
  def create_report(self, output_file: str | Path = None) -> Path:
420
455
  """ Create a page report for the array difference. """
421
456
 
457
+ from time import sleep
422
458
  if output_file is None:
423
459
  output_file = Path(f"array_difference_{self.index}.pdf")
424
460
 
@@ -429,6 +465,7 @@ class ArrayDifference():
429
465
  layout.create_report()
430
466
  self._complete_report(layout)
431
467
  layout.save_report(output_file)
468
+ sleep(0.2) # Ensure the file is saved before returning
432
469
 
433
470
  return output_file
434
471
 
@@ -459,6 +496,10 @@ class CompareArrays:
459
496
 
460
497
  self.difference_parts:dict[int, ArrayDifference] = {}
461
498
 
499
+ self._pdf_path = None
500
+
501
+ self._background = 'IGN'
502
+
462
503
  @property
463
504
  def difference(self) -> WolfArray:
464
505
 
@@ -467,6 +508,24 @@ class CompareArrays:
467
508
 
468
509
  return self.array_to_compare - self.array_reference
469
510
 
511
+ def get_zones(self):
512
+ """
513
+ Get a Zones object containing the differences.
514
+ """
515
+
516
+ ret_zones = Zones()
517
+ exterior = zone(name=_("External border"))
518
+ contours = zone(name=_("Contours"))
519
+
520
+ ret_zones.add_zone(exterior, forceparent=True)
521
+ ret_zones.add_zone(contours, forceparent=True)
522
+
523
+ for diff in self.difference_parts.values():
524
+ exterior.add_vector(diff.external_border, forceparent=True)
525
+ contours.add_vector(diff.contour, forceparent=True)
526
+
527
+ return ret_zones
528
+
470
529
  def plot_position(self, figax:tuple[plt.Figure, plt.Axes]=None) -> tuple[plt.Figure, plt.Axes]:
471
530
  """
472
531
  Plot the reference array with a background.
@@ -487,14 +546,27 @@ class CompareArrays:
487
546
  new = WolfArray(srcheader=h)
488
547
  new.array.mask[:,:] = True
489
548
 
549
+ if self._background.upper() == 'IGN' or self._background.upper() == 'NGI':
490
550
 
491
- new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
492
- first_mask_data=False, with_legend=False,
493
- update_palette= False,
494
- IGN= True,
495
- cat = 'orthoimage_coverage',
496
- )
497
-
551
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
552
+ first_mask_data=False, with_legend=False,
553
+ update_palette= False,
554
+ IGN= True,
555
+ cat = 'orthoimage_coverage',
556
+ )
557
+ elif self._background.upper() == 'WALONMAP':
558
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
559
+ first_mask_data=False, with_legend=False,
560
+ update_palette= False,
561
+ Walonmap= True,
562
+ cat = 'IMAGERIE/ORTHO_2022_ETE',
563
+ )
564
+ else:
565
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
566
+ first_mask_data=False, with_legend=False,
567
+ update_palette= False,
568
+ Walonmap= False,
569
+ )
498
570
  return fig, ax
499
571
 
500
572
  def plot_cartoweb(self, figax:tuple[plt.Figure, plt.Axes]=None) -> tuple[plt.Figure, plt.Axes]:
@@ -517,14 +589,20 @@ class CompareArrays:
517
589
  new = WolfArray(srcheader=h)
518
590
  new.array.mask[:,:] = True
519
591
 
520
-
521
- new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
522
- first_mask_data=False, with_legend=False,
523
- update_palette= False,
524
- Cartoweb= True,
525
- cat = 'overlay',
526
- )
527
-
592
+ if self._background.upper() == 'IGN' or self._background.upper() == 'NGI':
593
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
594
+ first_mask_data=False, with_legend=False,
595
+ update_palette= False,
596
+ Cartoweb= True,
597
+ cat = 'overlay',
598
+ )
599
+ else:
600
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
601
+ first_mask_data=False, with_legend=False,
602
+ update_palette= False,
603
+ Cartoweb= False,
604
+ cat = 'overlay',
605
+ )
528
606
  return fig, ax
529
607
 
530
608
  def plot_topo_grey(self, figax:tuple[plt.Figure, plt.Axes]=None) -> tuple[plt.Figure, plt.Axes]:
@@ -547,14 +625,20 @@ class CompareArrays:
547
625
  new = WolfArray(srcheader=h)
548
626
  new.array.mask[:,:] = True
549
627
 
550
-
551
- new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
552
- first_mask_data=False, with_legend=False,
553
- update_palette= False,
554
- Cartoweb= True,
555
- cat = 'topo_grey',
556
- )
557
-
628
+ if self._background.upper() == 'IGN' or self._background.upper() == 'NGI':
629
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
630
+ first_mask_data=False, with_legend=False,
631
+ update_palette= False,
632
+ Cartoweb= True,
633
+ cat = 'topo_grey',
634
+ )
635
+ else:
636
+ new.plot_matplotlib(figax=figax, figsize = self.default_size_arrays,
637
+ first_mask_data=False, with_legend=False,
638
+ update_palette= False,
639
+ Cartoweb= False,
640
+ cat = 'topo_grey',
641
+ )
558
642
  return fig, ax
559
643
 
560
644
  def plot_reference(self, figax:tuple[plt.Figure, plt.Axes]=None) -> tuple[plt.Figure, plt.Axes]:
@@ -568,7 +652,7 @@ class CompareArrays:
568
652
 
569
653
  self.array_reference.plot_matplotlib(figax=figax, figsize = self.default_size_arrays, first_mask_data=False, with_legend=True, update_palette= False)
570
654
 
571
- for diff in self.difference_parts.values():
655
+ for diff in tqdm(self.difference_parts.values(), desc="Plotting external borders"):
572
656
  diff.external_border.plot_matplotlib(ax=ax)
573
657
 
574
658
  return fig, ax
@@ -584,7 +668,7 @@ class CompareArrays:
584
668
 
585
669
  self.array_to_compare.plot_matplotlib(figax=figax, figsize = self.default_size_arrays, first_mask_data=False, with_legend=True, update_palette= False)
586
670
 
587
- for diff in self.difference_parts.values():
671
+ for diff in tqdm(self.difference_parts.values(), desc="Plotting contours"):
588
672
  diff.contour.plot_matplotlib(ax=ax)
589
673
 
590
674
  return fig, ax
@@ -606,11 +690,22 @@ class CompareArrays:
606
690
  diff.plot_matplotlib(figax=figax, figsize = self.default_size_arrays, first_mask_data=False, with_legend=True, update_palette= False)
607
691
  return fig, ax
608
692
 
609
- def localize_differences(self, threshold: float = 0.0) -> np.ndarray:
693
+ def localize_differences(self, threshold: float = 0.0,
694
+ ignored_patche_area:float = 1.) -> np.ndarray:
695
+ """ Localize the differences between the two arrays and label them.
696
+
697
+ :param threshold: The threshold value to consider a difference significant.
698
+ :param ignored_patche_area: The area of patches to ignore (in m²).
699
+ """
700
+
701
+ assert threshold >= 0, "Threshold must be a non-negative value."
610
702
 
611
703
  labeled_array = self.difference.array.data.copy()
612
704
  labeled_array[self.array_reference.array.mask] = 0
613
705
 
706
+ # apply threshold
707
+ labeled_array[np.abs(labeled_array) < threshold] = 0
708
+
614
709
  self.labeled_array, self.num_features = label(labeled_array)
615
710
 
616
711
  self.nb_cells = []
@@ -620,13 +715,29 @@ class CompareArrays:
620
715
 
621
716
  self.nb_cells.sort(key=lambda x: x[0], reverse=True)
622
717
 
718
+ # find features where nb_cells is lower than ignored_patche_area / (dx * dy)
719
+ ignored_patche_cells = int(ignored_patche_area / (self.array_reference.dx * self.array_reference.dy))
720
+ self.last_features = self.num_features
721
+ for idx, (nb_cell, idx_feature) in enumerate(self.nb_cells):
722
+ if nb_cell <= ignored_patche_cells:
723
+ self.last_features = idx
724
+ break
725
+
726
+ all_slices = find_objects(self.labeled_array)
727
+
728
+ logging.info(f"Total number of features found: {self.last_features}")
729
+
623
730
  # find xmin, ymin, xmax, ymax for each feature
624
- for idx_feature in range(1, self.num_features + 1):
625
- mask = self.labeled_array == idx_feature
626
- i, j = np.where(mask)
731
+ for idx_feature, slices in tqdm(zip(range(1, self.num_features+1), all_slices), desc="Processing features", unit="feature"):
732
+ mask = self.labeled_array[slices] == idx_feature
733
+ nb_in_patch = np.count_nonzero(mask)
734
+
735
+ if nb_in_patch <= ignored_patche_cells:
736
+ logging.debug(f"Feature {idx_feature} has too few cells ({np.count_nonzero(mask)}) and will be ignored.")
737
+ continue
627
738
 
628
- imin, imax = i.min(), i.max()
629
- jmin, jmax = j.min(), j.max()
739
+ imin, imax = slices[0].start, slices[0].stop - 1
740
+ jmin, jmax = slices[1].start, slices[1].stop - 1
630
741
 
631
742
  imin = int(max(imin - 1, 0))
632
743
  imax = int(min(imax + 1, self.labeled_array.shape[0] - 1))
@@ -635,17 +746,25 @@ class CompareArrays:
635
746
 
636
747
  ref_crop = self.array_reference.crop(imin, jmin, imax-imin+1, jmax-jmin+1)
637
748
  to_compare_crop = self.array_to_compare.crop(imin, jmin, imax-imin+1, jmax-jmin+1)
749
+ label_crop = self.labeled_array[imin:imax+1, jmin:jmax+1].copy()
638
750
 
639
- ref_crop.array.mask[:,:] = ~mask[imin:imax+1, jmin:jmax+1]
640
- ref_crop.set_nullvalue_in_mask()
641
751
 
642
- to_compare_crop.array.mask[:,:] = ~mask[imin:imax+1, jmin:jmax+1]
752
+ to_compare_crop.array.mask[:,:] = ref_crop.array.mask[:,:] = self.labeled_array[imin:imax+1, jmin:jmax+1] != idx_feature
753
+
754
+ ref_crop.set_nullvalue_in_mask()
643
755
  to_compare_crop.set_nullvalue_in_mask()
756
+ label_crop[label_crop != idx_feature] = 0
644
757
 
645
- ref_crop.count()
646
- to_compare_crop.nbnotnull = ref_crop.nbnotnull
758
+ ref_crop.nbnotnull = nb_in_patch
759
+ to_compare_crop.nbnotnull = nb_in_patch
647
760
 
648
- self.difference_parts[idx_feature] = ArrayDifference(ref_crop, to_compare_crop, idx_feature, self.labeled_array[imin:imax+1, jmin:jmax+1].copy())
761
+ self.difference_parts[idx_feature] = ArrayDifference(ref_crop, to_compare_crop, idx_feature, label_crop)
762
+
763
+ assert self.last_features == len(self.difference_parts), \
764
+ f"Last feature index {self.last_features} does not match the number of differences found"
765
+
766
+ self.num_features = self.last_features
767
+ logging.info(f"Number of features after filtering: {self.num_features}")
649
768
 
650
769
  return self.labeled_array
651
770
 
@@ -721,9 +840,10 @@ class CompareArrays:
721
840
  surf = self.array_reference.dx * self.array_reference.dy
722
841
 
723
842
  # Extract the number of cells for each feature
724
- nb_cells = [item[0] * surf for item in self.nb_cells]
843
+ nb_cells = [item[0] * surf for item in self.nb_cells[:self.last_features]]
725
844
 
726
- ax.hist(nb_cells, density=density, alpha=alpha, **kwargs)
845
+ if len(nb_cells) > 0:
846
+ ax.hist(nb_cells, bins= min(100, int(len(nb_cells)/4)), density=density, alpha=alpha, **kwargs)
727
847
 
728
848
  ax.set_title(_("Histogram of surface in each feature [m²]"))
729
849
 
@@ -748,13 +868,22 @@ class CompareArrays:
748
868
 
749
869
  fig, ax = figax
750
870
 
751
- # Calculate the difference between the two arrays
752
- diff = self.difference
871
+ # # Calculate the difference between the two arrays
872
+ # diff = self.difference
753
873
 
754
- volumes = np.bincount(self.labeled_array.ravel(), weights=diff.array.ravel()) * self.array_reference.dx * self.array_reference.dy
874
+ volumes = []
875
+ for idx in tqdm(self.nb_cells[:self.last_features], desc="Calculating volumes"):
876
+ # Get the feature index
877
+ feature_index = idx[1]
878
+ part = self.difference_parts[feature_index]
879
+ # Create a mask for the feature
880
+ mask = part.label == feature_index
881
+ # Calculate the volume for this feature
882
+ volumes.append(np.ma.sum(part.difference.array[mask]) * self.array_reference.dx * self.array_reference.dy)
755
883
 
756
884
  # Create a histogram of the differences
757
- ax.hist(volumes, density=density, alpha=alpha, **kwargs)
885
+ if len(volumes) > 0:
886
+ ax.hist(volumes, bins= min(100, int(len(volumes)/4)), density=density, alpha=alpha, **kwargs)
758
887
 
759
888
  ax.set_title(_("Histogram of net volumes [m³]"))
760
889
 
@@ -809,7 +938,9 @@ class CompareArrays:
809
938
 
810
939
 
811
940
 
812
- def create_report(self, output_file: str | Path = None, append_all_differences: bool = True) -> None:
941
+ def create_report(self, output_file: str | Path = None,
942
+ append_all_differences: bool = True,
943
+ nb_max_differences:int = -1) -> None:
813
944
  """ Create a page report for the array comparison. """
814
945
 
815
946
  if output_file is None:
@@ -822,6 +953,14 @@ class CompareArrays:
822
953
  layout.create_report()
823
954
  self._complete_report(layout)
824
955
 
956
+ if nb_max_differences < 0:
957
+ nb_max_differences = len(self.difference_parts)
958
+ elif nb_max_differences > len(self.difference_parts):
959
+ logging.warning(f"Requested {nb_max_differences} differences, but only {len(self.difference_parts)} are available. Using all available differences.")
960
+ elif nb_max_differences < len(self.difference_parts):
961
+ logging.info(f"Limiting to {nb_max_differences} differences.")
962
+
963
+ features_to_treat = [feature[1] for feature in self.nb_cells[:nb_max_differences]]
825
964
 
826
965
  with TemporaryDirectory() as temp_dir:
827
966
 
@@ -833,12 +972,83 @@ class CompareArrays:
833
972
 
834
973
  if append_all_differences:
835
974
  # Add each difference report to the main layout
836
- all_pdfs.extend([diff.create_report(Path(temp_dir) / f"array_difference_{idx}.pdf") for idx, diff in self.difference_parts.items()])
975
+ all_pdfs.extend([self.difference_parts[idx].create_report(Path(temp_dir) / f"array_difference_{idx}.pdf") for idx in tqdm(features_to_treat, desc="Creating individual difference reports")])
837
976
 
838
- for pdf_file in all_pdfs:
977
+ for pdf_file in tqdm(all_pdfs, desc="Compiling PDFs"):
839
978
  layout._doc.insert_file(pdf_file)
840
979
 
841
980
  # create a TOC
842
981
  layout._doc.set_toc(layout._doc.get_toc())
843
982
 
844
- layout.save_report(output_file)
983
+ layout.save_report(output_file)
984
+ self._pdf_path = output_file
985
+
986
+ @property
987
+ def pdf_path(self) -> Path:
988
+ """ Return the path to the generated PDF report. """
989
+ if hasattr(self, '_pdf_path'):
990
+ return self._pdf_path
991
+ else:
992
+ raise AttributeError("PDF path not set. Please create the report first.")
993
+
994
+ class CompareArrays_wx(PDFViewer):
995
+
996
+ def __init__(self, reference: WolfArray | str | Path,
997
+ to_compare: WolfArray | str | Path,
998
+ ignored_patche_area:float = 2.0,
999
+ nb_max_patches:int = 10,
1000
+ threshold: float = 0.01,
1001
+ dpi=200, **kwargs):
1002
+ """ Initialize the Simple Simulation GPU Report Viewer for comparison """
1003
+
1004
+ super(CompareArrays_wx, self).__init__(None, **kwargs)
1005
+
1006
+ use('agg')
1007
+
1008
+ if isinstance(reference, WolfArray) and isinstance(to_compare, WolfArray):
1009
+ if np.any(reference.array.mask != to_compare.array.mask):
1010
+ logging.warning("The masks of the two arrays are not identical. This may lead to unexpected results.")
1011
+ dlg = wx.MessageDialog(self,
1012
+ _("The masks of the two arrays are not identical.\nThis may lead to unexpected results.\n\nWe will use the reference mask for the comparison."),
1013
+ _("Warning"),
1014
+ wx.OK | wx.ICON_WARNING)
1015
+ dlg.ShowModal()
1016
+ dlg.Destroy()
1017
+ to_compare = WolfArray(mold=to_compare)
1018
+ to_compare.array.mask[:,:] = reference.array.mask[:,:]
1019
+
1020
+ self._report = CompareArrays(reference, to_compare)
1021
+
1022
+ self._report._dpi = dpi
1023
+ self._report.localize_differences(threshold=threshold,
1024
+ ignored_patche_area=ignored_patche_area)
1025
+ self._report.create_report(nb_max_differences=nb_max_patches)
1026
+
1027
+ # Load the PDF into the viewer
1028
+ if self._report.pdf_path is None:
1029
+ logging.error("No report created. Cannot load PDF.")
1030
+ return
1031
+
1032
+ self.load_pdf(self._report.pdf_path)
1033
+ self.viewer.SetZoom(-1) # Fit to width
1034
+
1035
+ # Set the title of the frame
1036
+ self.SetTitle("Simple Simulation GPU Comparison Report")
1037
+
1038
+ self.Bind(wx.EVT_CLOSE, self.on_close)
1039
+
1040
+ use('wxagg')
1041
+
1042
+ def on_close(self, event):
1043
+ """ Handle the close event to clean up resources """
1044
+ self.viewer.pdfdoc.pdfdoc.close()
1045
+ self.Destroy()
1046
+
1047
+ def get_zones(self) -> Zones:
1048
+ """
1049
+ Get the zones from the report.
1050
+ """
1051
+ ret = self._report.get_zones()
1052
+ ret.prep_listogl()
1053
+
1054
+ return ret
@@ -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
@@ -30,6 +31,7 @@ class SimpleSimGPU_Report():
30
31
  def __init__(self, sim:SimpleSimulation | Path | str, **kwargs):
31
32
  """ Initialize the Simple Simulation GPU Report Viewer """
32
33
 
34
+ self._summary = {}
33
35
  self._doc = None
34
36
 
35
37
  if isinstance(sim, Path):
@@ -38,6 +40,7 @@ class SimpleSimGPU_Report():
38
40
  except Exception as e:
39
41
  logging.error(f"Failed to load simulation from path {sim}: {e}")
40
42
  self._sim = None
43
+ self._summary['errors'] = e
41
44
  return
42
45
  elif isinstance(sim, str):
43
46
  try:
@@ -45,6 +48,7 @@ class SimpleSimGPU_Report():
45
48
  except Exception as e:
46
49
  logging.error(f"Failed to load simulation from string path {sim}: {e}")
47
50
  self._sim = None
51
+ self._summary['errors'] = e
48
52
  return
49
53
  elif not isinstance(sim, SimpleSimulation):
50
54
  try:
@@ -52,12 +56,12 @@ class SimpleSimGPU_Report():
52
56
  except Exception as e:
53
57
  logging.error(f"Failed to set simulation: {e}")
54
58
  self._sim = None
59
+ self._summary['errors'] = e
55
60
  return
56
61
  else:
57
62
  logging.error("Invalid type for simulation. Must be SimpleSimulation, Path, or str.")
58
63
  return
59
64
 
60
- self._summary = {}
61
65
  self._summary['warnings'] = self._summary_warnings()
62
66
  self._summary['errors'] = self._summary_errors()
63
67
 
@@ -272,9 +276,13 @@ class SimpleSimGPU_Report():
272
276
  # Plot the histogram of waterdepth adn add it to the PDF
273
277
  fig, ax = plt.subplots(figsize=(8, 6))
274
278
  # Plot the histogram of water depth
275
- ax.hist(sim.h[sim.h > 0.], bins=100, density=True)
276
- # ax.set_title('Histogram of Water Depth')
277
- ax.set_xlim(0, np.max(sim.h[sim.h > 0.]) * 1.25) # Set xlim to 110% of max value
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
278
286
  ax.set_xlabel('Water Depth [m]')
279
287
  ax.set_ylabel('Frequency')
280
288
 
@@ -301,7 +309,7 @@ class SimpleSimGPU_Report():
301
309
  ax.hist(sim.manning[sim.nap == 1], bins=100, density = True)
302
310
  # ax.set_title('Histogram of Manning Coefficient')
303
311
  ax.set_xlabel('Manning [$\\frac {s} {m^{1/3}} $]')
304
- ax.set_xlim(0, np.max(sim.manning[sim.nap == 1]) * 1.25) # Set xlim to 110% of max value
312
+ ax.set_xlim(0, np.max(sim.manning[sim.nap == 1]) * 1.1) # Set xlim to 110% of max value
305
313
  ax.set_ylabel('Frequency')
306
314
 
307
315
  # set font size of the labels
@@ -771,15 +779,21 @@ class SimpleSimGPU_Report():
771
779
 
772
780
  class SimpleSimGPU_Report_wx(PDFViewer):
773
781
 
774
- def __init__(self, sim:SimpleSimulation | Path | str, **kwargs):
782
+ def __init__(self, sim:SimpleSimulation | Path | str, show:bool=False, **kwargs):
775
783
  """ Initialize the Simple Simulation GPU Report Viewer """
776
784
 
785
+ mpl.use('Agg') # Use a non-interactive backend for matplotlib
786
+
777
787
  super(SimpleSimGPU_Report_wx, self).__init__(None, **kwargs)
778
788
 
779
789
  self._report = SimpleSimGPU_Report(sim, **kwargs)
780
790
 
781
791
  if self._report._sim is None:
782
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()
783
797
  return
784
798
 
785
799
  self._report.create_report()
@@ -798,6 +812,11 @@ class SimpleSimGPU_Report_wx(PDFViewer):
798
812
 
799
813
  self.Bind(wx.EVT_CLOSE, self.on_close)
800
814
 
815
+ if show:
816
+ self.Show()
817
+
818
+ mpl.use('WxAgg') # Reset matplotlib to use the WxAgg backend for other plots
819
+
801
820
  def on_close(self, event):
802
821
  """ Handle the close event of the frame """
803
822