wolfhece 2.2.32__py3-none-any.whl → 2.2.34__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 +26 -3
- wolfhece/PyDraw.py +329 -4
- wolfhece/PyGui.py +35 -1
- wolfhece/PyPictures.py +420 -3
- wolfhece/PyVertexvectors.py +153 -11
- wolfhece/Results2DGPU.py +10 -1
- wolfhece/apps/version.py +1 -1
- wolfhece/pydownloader.py +82 -0
- wolfhece/textpillow.py +1 -1
- wolfhece/wolf_texture.py +81 -13
- wolfhece/wolf_zi_db.py +586 -2
- {wolfhece-2.2.32.dist-info → wolfhece-2.2.34.dist-info}/METADATA +1 -1
- {wolfhece-2.2.32.dist-info → wolfhece-2.2.34.dist-info}/RECORD +16 -16
- {wolfhece-2.2.32.dist-info → wolfhece-2.2.34.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.32.dist-info → wolfhece-2.2.34.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.32.dist-info → wolfhece-2.2.34.dist-info}/top_level.txt +0 -0
wolfhece/wolf_zi_db.py
CHANGED
@@ -17,11 +17,15 @@ from enum import Enum
|
|
17
17
|
from pathlib import Path
|
18
18
|
from typing import Literal, Union
|
19
19
|
import wx
|
20
|
+
from tqdm import tqdm
|
21
|
+
import re
|
20
22
|
|
21
23
|
from shapely.geometry import Polygon
|
22
24
|
|
23
25
|
from .PyVertexvectors import Zones, zone, vector, wolfvertex
|
26
|
+
from .textpillow import Font_Priority
|
24
27
|
from .wolf_texture import genericImagetexture
|
28
|
+
from .PyPictures import PictureCollection
|
25
29
|
from .PyTranslate import _
|
26
30
|
|
27
31
|
class ColNames_PlansTerriers(Enum):
|
@@ -38,6 +42,90 @@ class ColNames_PlansTerriers(Enum):
|
|
38
42
|
LOWRES = 'Acces2'
|
39
43
|
RIVER = 'River'
|
40
44
|
|
45
|
+
class ColNames_Ouvrages(Enum):
|
46
|
+
""" Enum for the column names in the database """
|
47
|
+
|
48
|
+
KEY = 'Clé primaire'
|
49
|
+
X1 = 'X Lambert gauche'
|
50
|
+
X2 = 'X Lambert droit'
|
51
|
+
Y1 = 'Y Lambert gauche'
|
52
|
+
Y2 = 'Y Lambert droit'
|
53
|
+
REMARK = 'Remarques'
|
54
|
+
RIVER = 'Lieu'
|
55
|
+
PHOTO1 = 'Photo1'
|
56
|
+
PHOTO2 = 'Photo2'
|
57
|
+
PHOTO3 = 'Photo3'
|
58
|
+
PHOTO4 = 'Photo4'
|
59
|
+
PHOTO5 = 'Photo5'
|
60
|
+
PHOTO6 = 'Photo6'
|
61
|
+
PHOTO7 = 'Photo7'
|
62
|
+
PHOTO8 = 'Photo8'
|
63
|
+
PHOTO9 = 'Photo9'
|
64
|
+
PHOTO10 = 'Photo10'
|
65
|
+
DATE = 'Date'
|
66
|
+
|
67
|
+
class ColNames_Particularites(Enum):
|
68
|
+
""" Enum for the column names in the database """
|
69
|
+
|
70
|
+
KEY = 'Clé primaire'
|
71
|
+
X = 'Xlambert'
|
72
|
+
Y = 'Ylambert'
|
73
|
+
REMARK = 'Commentaires'
|
74
|
+
RIVER = 'Rivière'
|
75
|
+
PHOTO1 = 'Photo 1'
|
76
|
+
PHOTO2 = 'Photo 2'
|
77
|
+
PHOTO3 = 'Photo 3'
|
78
|
+
PHOTO4 = 'Photo 4'
|
79
|
+
PHOTO5 = 'Photo 5'
|
80
|
+
ORIENTATION = 'Orientation'
|
81
|
+
DATE = 'Date'
|
82
|
+
|
83
|
+
class ColNames_Enquetes(Enum):
|
84
|
+
""" Enum for the column names in the database """
|
85
|
+
|
86
|
+
KEY = 'Clé primaire'
|
87
|
+
X = 'XLambert'
|
88
|
+
Y = 'YLambert'
|
89
|
+
RIVER = 'Rivière'
|
90
|
+
PHOTO = 'Photo'
|
91
|
+
ORIENTATION = 'Orientation'
|
92
|
+
DATE = 'Date'
|
93
|
+
|
94
|
+
def _test_bounds(x:float, y:float, bounds:list[list[float, float], list[float, float]]) -> bool:
|
95
|
+
""" Test if the coordinates are inside the bounds
|
96
|
+
|
97
|
+
:param x: The x coordinate
|
98
|
+
:type x: float
|
99
|
+
:param y: The y coordinate
|
100
|
+
:type y: float
|
101
|
+
:param bounds: The bounds to test against - [ [xmin, xmax], [ymin, ymax] ]
|
102
|
+
:type bounds: list[list[float, float], list[float, float]]
|
103
|
+
:return: True if the coordinates are inside the bounds, False otherwise
|
104
|
+
:rtype: bool
|
105
|
+
"""
|
106
|
+
|
107
|
+
if bounds is None:
|
108
|
+
return True
|
109
|
+
|
110
|
+
xmin, xmax = bounds[0]
|
111
|
+
ymin, ymax = bounds[1]
|
112
|
+
|
113
|
+
return xmin <= x <= xmax and ymin <= y <= ymax
|
114
|
+
|
115
|
+
|
116
|
+
def _sanitize_legendtext(text:str) -> str:
|
117
|
+
""" Sanitize the legend text by replacing newlines and special characters
|
118
|
+
|
119
|
+
:param text: The text to sanitize
|
120
|
+
:type text: str
|
121
|
+
:return: The sanitized text
|
122
|
+
:rtype: str
|
123
|
+
"""
|
124
|
+
text = str(text)
|
125
|
+
# replace newlines and special characters
|
126
|
+
text = re.sub(r'(_x000D_\n|\n)', ' - ', text)
|
127
|
+
|
128
|
+
return text.strip()
|
41
129
|
class ZI_Databse_Elt():
|
42
130
|
""" Class to store the database elements """
|
43
131
|
|
@@ -211,7 +299,9 @@ class PlansTerrier(Zones):
|
|
211
299
|
logging.error('No file selected or the file does not exist.')
|
212
300
|
return
|
213
301
|
|
302
|
+
logging.info(f'Reading database from {self.filename}')
|
214
303
|
self.db = pd.read_excel(self.filename, sheet_name='Plans_Terriers')
|
304
|
+
logging.info(f'Database read successfully from {self.filename}')
|
215
305
|
|
216
306
|
rivers = list(self.db[ColNames_PlansTerriers.RIVER.value].unique())
|
217
307
|
rivers.sort()
|
@@ -289,7 +379,7 @@ class PlansTerrier(Zones):
|
|
289
379
|
curvector.add_vertex(wolfvertex(x=curelt.origx, y=curelt.endy))
|
290
380
|
curvector.close_force()
|
291
381
|
else:
|
292
|
-
logging.
|
382
|
+
logging.debug(f'File {fullpath} does not exist')
|
293
383
|
|
294
384
|
break
|
295
385
|
|
@@ -348,4 +438,498 @@ class PlansTerrier(Zones):
|
|
348
438
|
super().plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
349
439
|
|
350
440
|
for curtexture in self.textures.values():
|
351
|
-
curtexture.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
441
|
+
curtexture.plot(sx, sy, xmin, ymin, xmax, ymax, size)
|
442
|
+
|
443
|
+
|
444
|
+
class Ouvrages(PictureCollection):
|
445
|
+
""" Class to handle the "Ouvrages" -- Pictures of the structures in the ZI. """
|
446
|
+
|
447
|
+
def __init__(self, parent=None, idx: str = '', plotted: bool = True, mapviewer=None, rivers:list[str] = None) -> None:
|
448
|
+
"""
|
449
|
+
Constructor for the Ouvrages class.
|
450
|
+
|
451
|
+
:param parent: The wx parent of the object
|
452
|
+
:type parent: wx.Window
|
453
|
+
:param idx: The index of the object
|
454
|
+
:type idx: str
|
455
|
+
:param plotted: If the object is plotted
|
456
|
+
:type plotted: bool
|
457
|
+
:param mapviewer: The mapviewer object
|
458
|
+
:type mapviewer: MapViewer
|
459
|
+
:param rivers: The list of rivers to display
|
460
|
+
:type rivers: list[str]
|
461
|
+
"""
|
462
|
+
|
463
|
+
super().__init__(parent = parent, idx = idx, plotted = plotted, mapviewer = mapviewer)
|
464
|
+
|
465
|
+
self.wx_exists = wx.GetApp() is not None
|
466
|
+
self.db = None
|
467
|
+
self.rivers = rivers
|
468
|
+
self.initialized = False
|
469
|
+
|
470
|
+
self._columns = ColNames_Ouvrages
|
471
|
+
|
472
|
+
def check_plot(self):
|
473
|
+
""" Activate the plot if the object is initialized """
|
474
|
+
|
475
|
+
if not self.initialized:
|
476
|
+
|
477
|
+
# try to get the filename from the parent mapviewer
|
478
|
+
if self.mapviewer is not None:
|
479
|
+
self.filename = self.mapviewer.default_hece_database
|
480
|
+
bounds = self.mapviewer.get_bounds()
|
481
|
+
|
482
|
+
if 'bridge' in self.idx.lower() or 'pont' in self.idx.lower():
|
483
|
+
self.read_db(self.filename, sel_rivers=self.rivers, sheet_name='Ponts', bounds=bounds)
|
484
|
+
elif 'weir' in self.idx.lower() or 'seuil' in self.idx.lower():
|
485
|
+
self.read_db(self.filename, sel_rivers=self.rivers, sheet_name='Seuils', bounds=bounds)
|
486
|
+
elif 'survey' in self.idx.lower() or 'enquete' in self.idx.lower():
|
487
|
+
self.read_db(self.filename, sel_rivers=self.rivers, sheet_name='Photos', bounds=bounds)
|
488
|
+
elif 'features' in self.idx.lower() or 'particularit' in self.idx.lower():
|
489
|
+
self.read_db(self.filename, sel_rivers=self.rivers, sheet_name='Particularités', bounds=bounds)
|
490
|
+
|
491
|
+
if self.initialized:
|
492
|
+
super().check_plot()
|
493
|
+
|
494
|
+
def read_db(self, filename:str | Path,
|
495
|
+
sel_rivers: list[str] = None,
|
496
|
+
sheet_name: str = 'Ponts',
|
497
|
+
bounds: list[list[float, float], list[float, float]] = None):
|
498
|
+
""" Read the database (Excel file) and create the zones and the vectors.
|
499
|
+
|
500
|
+
The user will be prompted to select the rivers to display.
|
501
|
+
|
502
|
+
:param filename: The path to the Excel file containing the database
|
503
|
+
:type filename: str | Path
|
504
|
+
:param sel_rivers: The list of rivers to display, if None, the user will be prompted to select the rivers
|
505
|
+
:type sel_rivers: list[str] | None
|
506
|
+
:param sheet_name: The name of the sheet in the Excel file to read
|
507
|
+
:type sheet_name: str
|
508
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
509
|
+
:type bounds: list[list[float, float], list[float, float]] | None
|
510
|
+
"""
|
511
|
+
|
512
|
+
self.filename = Path(filename)
|
513
|
+
|
514
|
+
if not self.filename.exists() or filename == '':
|
515
|
+
|
516
|
+
if self.wx_exists:
|
517
|
+
|
518
|
+
dlg= wx.FileDialog(None, _("Choose a file"), defaultDir= "", wildcard="Excel (*.xlsx)|*.xlsx", style = wx.FD_OPEN)
|
519
|
+
ret = dlg.ShowModal()
|
520
|
+
if ret == wx.ID_OK:
|
521
|
+
self.filename = Path(dlg.GetPath())
|
522
|
+
dlg.Destroy()
|
523
|
+
else:
|
524
|
+
logging.error('No file selected')
|
525
|
+
dlg.Destroy()
|
526
|
+
return
|
527
|
+
|
528
|
+
else:
|
529
|
+
logging.error('No file selected or the file does not exist.')
|
530
|
+
return
|
531
|
+
|
532
|
+
try:
|
533
|
+
logging.info(f'Reading database from {self.filename}')
|
534
|
+
self.db = pd.read_excel(self.filename, sheet_name=sheet_name)
|
535
|
+
logging.info(f'Database read successfully from {self.filename}')
|
536
|
+
except ValueError as e:
|
537
|
+
logging.error(f"Error reading the Excel file: {e}")
|
538
|
+
return
|
539
|
+
|
540
|
+
rivers = list(self.db[ColNames_Ouvrages.RIVER.value].unique())
|
541
|
+
rivers.sort()
|
542
|
+
|
543
|
+
self.rivers = []
|
544
|
+
|
545
|
+
if sel_rivers is None and self.wx_exists:
|
546
|
+
|
547
|
+
with wx.MessageDialog(None, _("Choose the rivers to display"), _("Rivers"), wx.YES_NO | wx.ICON_QUESTION) as dlg:
|
548
|
+
|
549
|
+
if dlg.ShowModal() == wx.ID_YES:
|
550
|
+
|
551
|
+
with wx.MultiChoiceDialog(None, _("Choose the rivers to display"), _("Rivers"), rivers) as dlg_river:
|
552
|
+
ret = dlg_river.ShowModal()
|
553
|
+
|
554
|
+
if ret == wx.ID_OK:
|
555
|
+
for curidx in dlg_river.GetSelections():
|
556
|
+
self.rivers.append(rivers[curidx])
|
557
|
+
else:
|
558
|
+
self.rivers = rivers
|
559
|
+
|
560
|
+
elif sel_rivers is not None:
|
561
|
+
|
562
|
+
for curruver in sel_rivers:
|
563
|
+
if curruver in rivers:
|
564
|
+
self.rivers.append(curruver)
|
565
|
+
else:
|
566
|
+
logging.error(f'River {curruver} not found in the database -- Ignoring !')
|
567
|
+
|
568
|
+
self._filter_db(bounds)
|
569
|
+
|
570
|
+
self.initialized = True
|
571
|
+
|
572
|
+
def _filter_db(self, bounds: list[list[float, float], list[float, float]] = None):
|
573
|
+
""" Filter the database based on the selected rivers and bounds.
|
574
|
+
|
575
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
576
|
+
:type bounds: list[list[float, float], list[float, float]] | None
|
577
|
+
"""
|
578
|
+
|
579
|
+
if len(self.rivers) == 0:
|
580
|
+
locdb = self.db
|
581
|
+
else:
|
582
|
+
locdb = self.db[self.db[ColNames_Ouvrages.RIVER.value].isin(self.rivers)]
|
583
|
+
|
584
|
+
for id, curline in tqdm(locdb.iterrows()):
|
585
|
+
river = curline[ColNames_Ouvrages.RIVER.value]
|
586
|
+
|
587
|
+
paths = []
|
588
|
+
for col in [ColNames_Ouvrages.PHOTO1,
|
589
|
+
ColNames_Ouvrages.PHOTO2,
|
590
|
+
ColNames_Ouvrages.PHOTO3,
|
591
|
+
ColNames_Ouvrages.PHOTO4,
|
592
|
+
ColNames_Ouvrages.PHOTO5,
|
593
|
+
ColNames_Ouvrages.PHOTO6,
|
594
|
+
ColNames_Ouvrages.PHOTO7,
|
595
|
+
ColNames_Ouvrages.PHOTO8,
|
596
|
+
ColNames_Ouvrages.PHOTO9,
|
597
|
+
ColNames_Ouvrages.PHOTO10]:
|
598
|
+
|
599
|
+
fullpath = curline[col.value]
|
600
|
+
|
601
|
+
fullpath = fullpath.replace(r'\\192.168.2.185\Intranet\Data\Données et Photos de crues\Ouvrages',
|
602
|
+
str(self.filename.parent) +r'\Ouvrages')
|
603
|
+
if fullpath == '0':
|
604
|
+
break
|
605
|
+
|
606
|
+
fullpath = Path(fullpath)
|
607
|
+
|
608
|
+
if fullpath.exists():
|
609
|
+
paths.append(fullpath)
|
610
|
+
else:
|
611
|
+
logging.debug(f'File {fullpath} does not exist')
|
612
|
+
|
613
|
+
if not paths:
|
614
|
+
logging.debug(f'No valid paths found for river {river} in the database')
|
615
|
+
continue
|
616
|
+
|
617
|
+
nb = len(paths)
|
618
|
+
x1 = curline[ColNames_Ouvrages.X1.value]
|
619
|
+
x2 = curline[ColNames_Ouvrages.X2.value]
|
620
|
+
y1 = curline[ColNames_Ouvrages.Y1.value]
|
621
|
+
y2 = curline[ColNames_Ouvrages.Y2.value]
|
622
|
+
|
623
|
+
keyzone = river.strip() + '_' + paths[0].stem
|
624
|
+
# make a mosaic - max 3 pictures per row
|
625
|
+
|
626
|
+
xref = (x1 + x2) / 2
|
627
|
+
yref = (y1 + y2) / 2
|
628
|
+
|
629
|
+
if bounds is not None and not _test_bounds(xref, yref, bounds):
|
630
|
+
logging.debug(f'Coordinates are out of bounds -- Skipping line {id}')
|
631
|
+
continue
|
632
|
+
|
633
|
+
for i in range(nb):
|
634
|
+
picture = paths[i]
|
635
|
+
|
636
|
+
x = xref + (i % 3) * self._default_size
|
637
|
+
y = yref + (i // 3) * self._default_size
|
638
|
+
|
639
|
+
if x < 1000. and y < 1000.:
|
640
|
+
logging.error(f'Coordinates for river {river} are not set -- Skipping picture {picture}')
|
641
|
+
continue
|
642
|
+
|
643
|
+
self.add_picture(picture, x=x, y=y, name=picture.stem, keyzone=keyzone)
|
644
|
+
|
645
|
+
pic = self[(keyzone, picture.stem)]
|
646
|
+
pic.myprop.legendtext = _sanitize_legendtext(curline[ColNames_Ouvrages.REMARK.value])
|
647
|
+
pic.myprop.legendx = pic.centroid.x
|
648
|
+
pic.myprop.legendy = pic.centroid.y
|
649
|
+
pic.myprop.legendpriority = Font_Priority.WIDTH
|
650
|
+
pic.myprop.legendlength = 100
|
651
|
+
|
652
|
+
self.find_minmax(True)
|
653
|
+
|
654
|
+
class Particularites(Ouvrages):
|
655
|
+
""" Class to handle the "Particularités" -- Pictures of the particularities in the ZI. """
|
656
|
+
|
657
|
+
def __init__(self, parent=None, idx = '', plotted = True, mapviewer=None, rivers = None):
|
658
|
+
super().__init__(parent = parent, idx = idx, plotted = plotted, mapviewer = mapviewer, rivers = rivers)
|
659
|
+
|
660
|
+
self._columns = ColNames_Particularites
|
661
|
+
|
662
|
+
def read_db(self, filename:str | Path,
|
663
|
+
sel_rivers: list[str] = None,
|
664
|
+
sheet_name: str = 'Particularités',
|
665
|
+
bounds: list[list[float, float], list[float, float]] = None):
|
666
|
+
""" Read the database (Excel file) and create the zones and the vectors.
|
667
|
+
|
668
|
+
The user will be prompted to select the rivers to display.
|
669
|
+
|
670
|
+
:param filename: The path to the Excel file containing the database
|
671
|
+
:type filename: str | Path
|
672
|
+
:param sel_rivers: The list of rivers to display, if None, the user will be prompted to select the rivers
|
673
|
+
:type sel_rivers: list[str] | None
|
674
|
+
:param sheet_name: The name of the sheet in the Excel file to read
|
675
|
+
:type sheet_name: str
|
676
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
677
|
+
:type bounds: list[list[float, float], list[float, float]] | None
|
678
|
+
"""
|
679
|
+
|
680
|
+
self.filename = Path(filename)
|
681
|
+
|
682
|
+
if not self.filename.exists() or filename == '':
|
683
|
+
|
684
|
+
if self.wx_exists:
|
685
|
+
|
686
|
+
dlg= wx.FileDialog(None, _("Choose a file"), defaultDir= "", wildcard="Excel (*.xlsx)|*.xlsx", style = wx.FD_OPEN)
|
687
|
+
ret = dlg.ShowModal()
|
688
|
+
if ret == wx.ID_OK:
|
689
|
+
self.filename = Path(dlg.GetPath())
|
690
|
+
dlg.Destroy()
|
691
|
+
else:
|
692
|
+
logging.error('No file selected')
|
693
|
+
dlg.Destroy()
|
694
|
+
return
|
695
|
+
|
696
|
+
else:
|
697
|
+
logging.error('No file selected or the file does not exist.')
|
698
|
+
return
|
699
|
+
|
700
|
+
try:
|
701
|
+
logging.info(f'Reading database from {self.filename}')
|
702
|
+
self.db = pd.read_excel(self.filename, sheet_name=sheet_name)
|
703
|
+
logging.info(f'Database read successfully from {self.filename}')
|
704
|
+
except ValueError as e:
|
705
|
+
logging.error(f"Error reading the Excel file: {e}")
|
706
|
+
return
|
707
|
+
|
708
|
+
rivers = list(self.db[ColNames_Particularites.RIVER.value].unique())
|
709
|
+
rivers.sort()
|
710
|
+
|
711
|
+
self.rivers = []
|
712
|
+
|
713
|
+
if sel_rivers is None and self.wx_exists:
|
714
|
+
|
715
|
+
with wx.MessageDialog(None, _("Choose the rivers to display"), _("Rivers"), wx.YES_NO | wx.ICON_QUESTION) as dlg:
|
716
|
+
|
717
|
+
if dlg.ShowModal() == wx.ID_YES:
|
718
|
+
|
719
|
+
with wx.MultiChoiceDialog(None, _("Choose the rivers to display"), _("Rivers"), rivers) as dlg_river:
|
720
|
+
ret = dlg_river.ShowModal()
|
721
|
+
|
722
|
+
if ret == wx.ID_OK:
|
723
|
+
for curidx in dlg_river.GetSelections():
|
724
|
+
self.rivers.append(rivers[curidx])
|
725
|
+
else:
|
726
|
+
self.rivers = rivers
|
727
|
+
|
728
|
+
elif sel_rivers is not None:
|
729
|
+
|
730
|
+
for curruver in sel_rivers:
|
731
|
+
if curruver in rivers:
|
732
|
+
self.rivers.append(curruver)
|
733
|
+
else:
|
734
|
+
logging.error(f'River {curruver} not found in the database -- Ignoring !')
|
735
|
+
|
736
|
+
self._filter_db(bounds)
|
737
|
+
|
738
|
+
self.initialized = True
|
739
|
+
|
740
|
+
def _filter_db(self, bounds: list[list[float, float], list[float, float]] = None):
|
741
|
+
""" Filter the database based on the selected rivers and bounds.
|
742
|
+
|
743
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
744
|
+
:type bounds: list[list[float, float], list[float, float]] |
|
745
|
+
"""
|
746
|
+
|
747
|
+
if len(self.rivers) == 0:
|
748
|
+
locdb = self.db
|
749
|
+
else:
|
750
|
+
locdb = self.db[self.db[ColNames_Particularites.RIVER.value].isin(self.rivers)]
|
751
|
+
|
752
|
+
for id, curline in tqdm(locdb.iterrows()):
|
753
|
+
river = curline[ColNames_Particularites.RIVER.value]
|
754
|
+
|
755
|
+
paths = []
|
756
|
+
for col in [ColNames_Particularites.PHOTO1,
|
757
|
+
ColNames_Particularites.PHOTO2,
|
758
|
+
ColNames_Particularites.PHOTO3,
|
759
|
+
ColNames_Particularites.PHOTO4,
|
760
|
+
ColNames_Particularites.PHOTO5]:
|
761
|
+
|
762
|
+
fullpath = curline[col.value]
|
763
|
+
|
764
|
+
fullpath = fullpath.replace(r'\\192.168.2.185\Intranet\Data\Données et Photos de crues',
|
765
|
+
str(self.filename.parent))
|
766
|
+
fullpath = Path(fullpath)
|
767
|
+
|
768
|
+
if fullpath.exists():
|
769
|
+
paths.append(fullpath)
|
770
|
+
else:
|
771
|
+
logging.debug(f'File {fullpath} does not exist')
|
772
|
+
|
773
|
+
if not paths:
|
774
|
+
logging.debug(f'No valid paths found for river {river} in the database')
|
775
|
+
continue
|
776
|
+
|
777
|
+
nb = len(paths)
|
778
|
+
xref = curline[ColNames_Particularites.X.value]
|
779
|
+
yref = curline[ColNames_Particularites.Y.value]
|
780
|
+
|
781
|
+
keyzone = river.strip() + '_' + paths[0].stem
|
782
|
+
# make a mosaic - max 3 pictures per row
|
783
|
+
|
784
|
+
if bounds is not None and not _test_bounds(xref, yref, bounds):
|
785
|
+
logging.info(f'Coordinates are out of bounds -- Skipping line {id}')
|
786
|
+
continue
|
787
|
+
|
788
|
+
for i in range(nb):
|
789
|
+
picture = paths[i]
|
790
|
+
x = xref + (i % 3) * self._default_size
|
791
|
+
y = yref + (i // 3) * self._default_size
|
792
|
+
|
793
|
+
self.add_picture(picture, x=x, y=y, name=picture.stem, keyzone=keyzone)
|
794
|
+
|
795
|
+
pic = self[(keyzone, picture.stem)]
|
796
|
+
pic.myprop.legendtext = _sanitize_legendtext(curline[ColNames_Particularites.REMARK.value])
|
797
|
+
pic.myprop.legendx = pic.centroid.x
|
798
|
+
pic.myprop.legendy = pic.centroid.y
|
799
|
+
pic.myprop.legendpriority = Font_Priority.WIDTH
|
800
|
+
pic.myprop.legendlength = 100
|
801
|
+
|
802
|
+
|
803
|
+
self.find_minmax(True)
|
804
|
+
|
805
|
+
class Enquetes(Ouvrages):
|
806
|
+
""" Class to handle the "Enquêtes" -- Pictures of the surveys in the ZI. """
|
807
|
+
def __init__(self, parent=None, idx = '', plotted = True, mapviewer=None, rivers = None):
|
808
|
+
super().__init__(parent = parent, idx = idx, plotted = plotted, mapviewer = mapviewer, rivers = rivers)
|
809
|
+
|
810
|
+
self._columns = ColNames_Enquetes
|
811
|
+
|
812
|
+
def read_db(self, filename:str | Path,
|
813
|
+
sel_rivers: list[str] = None,
|
814
|
+
sheet_name: str = 'Photos',
|
815
|
+
bounds: list[list[float, float], list[float, float]] = None):
|
816
|
+
""" Read the database (Excel file) and create the zones and the vectors.
|
817
|
+
|
818
|
+
The user will be prompted to select the rivers to display.
|
819
|
+
|
820
|
+
:param filename: The path to the Excel file containing the database
|
821
|
+
:type filename: str | Path
|
822
|
+
:param sel_rivers: The list of rivers to display, if None, the user will be prompted to select the rivers
|
823
|
+
:type sel_rivers: list[str] | None
|
824
|
+
:param sheet_name: The name of the sheet in the Excel file to read
|
825
|
+
:type sheet_name: str
|
826
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
827
|
+
:type bounds: list[list[float, float], list[float, float]] |
|
828
|
+
"""
|
829
|
+
|
830
|
+
self.filename = Path(filename)
|
831
|
+
|
832
|
+
if not self.filename.exists() or filename == '':
|
833
|
+
|
834
|
+
if self.wx_exists:
|
835
|
+
|
836
|
+
dlg= wx.FileDialog(None, _("Choose a file"), defaultDir= "", wildcard="Excel (*.xlsx)|*.xlsx", style = wx.FD_OPEN)
|
837
|
+
ret = dlg.ShowModal()
|
838
|
+
if ret == wx.ID_OK:
|
839
|
+
self.filename = Path(dlg.GetPath())
|
840
|
+
dlg.Destroy()
|
841
|
+
else:
|
842
|
+
logging.error('No file selected')
|
843
|
+
dlg.Destroy()
|
844
|
+
return
|
845
|
+
|
846
|
+
else:
|
847
|
+
logging.error('No file selected or the file does not exist.')
|
848
|
+
return
|
849
|
+
|
850
|
+
try:
|
851
|
+
logging.info(f'Reading database from {self.filename}')
|
852
|
+
self.db = pd.read_excel(self.filename, sheet_name=sheet_name)
|
853
|
+
logging.info(f'Database read successfully from {self.filename}')
|
854
|
+
except ValueError as e:
|
855
|
+
logging.error(f"Error reading the Excel file: {e}")
|
856
|
+
return
|
857
|
+
|
858
|
+
rivers = list(self.db[ColNames_Enquetes.RIVER.value].unique())
|
859
|
+
rivers.sort()
|
860
|
+
|
861
|
+
self.rivers = []
|
862
|
+
|
863
|
+
if sel_rivers is None and self.wx_exists:
|
864
|
+
|
865
|
+
with wx.MessageDialog(None, _("Choose the rivers to display"), _("Rivers"), wx.YES_NO | wx.ICON_QUESTION) as dlg:
|
866
|
+
|
867
|
+
if dlg.ShowModal() == wx.ID_YES:
|
868
|
+
|
869
|
+
with wx.MultiChoiceDialog(None, _("Choose the rivers to display"), _("Rivers"), rivers) as dlg_river:
|
870
|
+
ret = dlg_river.ShowModal()
|
871
|
+
|
872
|
+
if ret == wx.ID_OK:
|
873
|
+
for curidx in dlg_river.GetSelections():
|
874
|
+
self.rivers.append(rivers[curidx])
|
875
|
+
else:
|
876
|
+
self.rivers = rivers
|
877
|
+
|
878
|
+
elif sel_rivers is not None:
|
879
|
+
|
880
|
+
for curruver in sel_rivers:
|
881
|
+
if curruver in rivers:
|
882
|
+
self.rivers.append(curruver)
|
883
|
+
else:
|
884
|
+
logging.error(f'River {curruver} not found in the database -- Ignoring !')
|
885
|
+
|
886
|
+
self._filter_db(bounds)
|
887
|
+
|
888
|
+
self.initialized = True
|
889
|
+
|
890
|
+
def _filter_db(self, bounds: list[list[float, float], list[float, float]] = None):
|
891
|
+
""" Filter the database based on the selected rivers and bounds.
|
892
|
+
|
893
|
+
:param bounds: The bounds of the area to display, if None, no test on coordinates will be done - [ [xmin, xmax], [ymin, ymax] ]
|
894
|
+
:type bounds: list[list[float, float], list[float, float]] |
|
895
|
+
"""
|
896
|
+
|
897
|
+
if len(self.rivers) == 0:
|
898
|
+
locdb = self.db
|
899
|
+
else:
|
900
|
+
locdb = self.db[self.db[ColNames_Enquetes.RIVER.value].isin(self.rivers)]
|
901
|
+
|
902
|
+
for id, curline in tqdm(locdb.iterrows()):
|
903
|
+
river = curline[ColNames_Enquetes.RIVER.value]
|
904
|
+
|
905
|
+
fullpath = curline[ColNames_Enquetes.PHOTO.value]
|
906
|
+
|
907
|
+
fullpath = fullpath.replace(r'\\192.168.2.185\Intranet\Data\Données et Photos de crues',
|
908
|
+
str(self.filename.parent))
|
909
|
+
fullpath = Path(fullpath)
|
910
|
+
|
911
|
+
if not fullpath.exists():
|
912
|
+
logging.debug(f'File {fullpath} does not exist')
|
913
|
+
continue
|
914
|
+
|
915
|
+
x = curline[ColNames_Enquetes.X.value]
|
916
|
+
y = curline[ColNames_Enquetes.Y.value]
|
917
|
+
|
918
|
+
if bounds is not None and not _test_bounds(x, y, bounds):
|
919
|
+
logging.info(f'Coordinates are out of bounds -- Skipping line {id}')
|
920
|
+
continue
|
921
|
+
|
922
|
+
keyzone = river.strip()
|
923
|
+
|
924
|
+
picture = fullpath
|
925
|
+
self.add_picture(picture, x=x, y=y, name=picture.stem, keyzone=keyzone)
|
926
|
+
|
927
|
+
pic = self[(keyzone, picture.stem)]
|
928
|
+
pic.myprop.legendtext = _sanitize_legendtext(curline[ColNames_Enquetes.DATE.value])
|
929
|
+
pic.myprop.legendx = pic.centroid.x
|
930
|
+
pic.myprop.legendy = pic.centroid.y
|
931
|
+
pic.myprop.legendpriority = Font_Priority.WIDTH
|
932
|
+
pic.myprop.legendlength = 100
|
933
|
+
|
934
|
+
|
935
|
+
self.find_minmax(True)
|