wolfhece 2.2.34__py3-none-any.whl → 2.2.35__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/PyCrosssections.py +293 -16
- wolfhece/PyDraw.py +24 -7
- wolfhece/PyGui.py +9 -1
- wolfhece/PyVertexvectors.py +1 -1
- wolfhece/__init__.py +1 -0
- wolfhece/analyze_poly.py +1 -1
- wolfhece/apps/version.py +1 -1
- wolfhece/wolf_array.py +1233 -305
- wolfhece/wolf_zi_db.py +155 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/METADATA +1 -1
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/RECORD +14 -14
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/top_level.txt +0 -0
wolfhece/PyCrosssections.py
CHANGED
@@ -27,6 +27,8 @@ import logging
|
|
27
27
|
import wx
|
28
28
|
from typing import Union, Literal
|
29
29
|
from pathlib import Path
|
30
|
+
import pandas as pd
|
31
|
+
|
30
32
|
|
31
33
|
from .PyTranslate import _
|
32
34
|
from .drawing_obj import Element_To_Draw
|
@@ -130,6 +132,7 @@ class postype(Enum):
|
|
130
132
|
BY_VERTEX = 0 # sur base de coordonnées (wolfvertex)
|
131
133
|
BY_S3D = 1 # sur base d'une position curviligne 3D (le long de la trace de la section)
|
132
134
|
BY_INDEX = 2 # sur base d'un index de vertex
|
135
|
+
|
133
136
|
class profile(vector):
|
134
137
|
"""
|
135
138
|
Surcharge d'un vecteur en vue de définir un profil de rivière
|
@@ -156,6 +159,9 @@ class profile(vector):
|
|
156
159
|
self._bankright=None
|
157
160
|
self._bed=None
|
158
161
|
|
162
|
+
self._bankleft_down=None
|
163
|
+
self._bankright_down=None
|
164
|
+
|
159
165
|
self.banksbed_postype = postype.BY_VERTEX
|
160
166
|
|
161
167
|
self.refpoints={}
|
@@ -206,6 +212,22 @@ class profile(vector):
|
|
206
212
|
def bankright(self,value):
|
207
213
|
self._bankright=value
|
208
214
|
|
215
|
+
@ property
|
216
|
+
def bankleft_down(self):
|
217
|
+
return self._bankleft_down
|
218
|
+
|
219
|
+
@bankleft_down.setter
|
220
|
+
def bankleft_down(self,value):
|
221
|
+
self._bankleft_down=value
|
222
|
+
|
223
|
+
@property
|
224
|
+
def bankright_down(self):
|
225
|
+
return self._bankright_down
|
226
|
+
|
227
|
+
@bankright_down.setter
|
228
|
+
def bankright_down(self,value):
|
229
|
+
self._bankright_down=value
|
230
|
+
|
209
231
|
@property
|
210
232
|
def bed(self):
|
211
233
|
return self._bed
|
@@ -232,6 +254,24 @@ class profile(vector):
|
|
232
254
|
else:
|
233
255
|
return self.interpolate(self._bankright, adim=False)
|
234
256
|
|
257
|
+
@property
|
258
|
+
def bankleft_down_vertex(self):
|
259
|
+
if self.banksbed_postype == postype.BY_VERTEX:
|
260
|
+
return self._bankleft_down
|
261
|
+
elif self.banksbed_postype == postype.BY_INDEX:
|
262
|
+
return self.myvertices[self._bankleft_down]
|
263
|
+
else:
|
264
|
+
return self.interpolate(self._bankleft_down, adim=False)
|
265
|
+
|
266
|
+
@property
|
267
|
+
def bankright_down_vertex(self):
|
268
|
+
if self.banksbed_postype == postype.BY_VERTEX:
|
269
|
+
return self._bankright_down
|
270
|
+
elif self.banksbed_postype == postype.BY_INDEX:
|
271
|
+
return self.myvertices[self._bankright_down]
|
272
|
+
else:
|
273
|
+
return self.interpolate(self._bankright_down, adim=False)
|
274
|
+
|
235
275
|
@property
|
236
276
|
def bed_vertex(self):
|
237
277
|
if self.banksbed_postype == postype.BY_VERTEX:
|
@@ -277,6 +317,42 @@ class profile(vector):
|
|
277
317
|
s3d = self.length3D
|
278
318
|
return s3d
|
279
319
|
|
320
|
+
@property
|
321
|
+
def bankleft_down_s3D(self):
|
322
|
+
if self.banksbed_postype == postype.BY_S3D:
|
323
|
+
return self._bankleft_down
|
324
|
+
else:
|
325
|
+
# on dispose d'un vertex 3D et on doit retnvoyer une position s3D
|
326
|
+
# --> projection x,y sur la trace --> récupération de 's'
|
327
|
+
# --> calcul de la distance s3D via shapely LineString sz en projetant '(s,z)'
|
328
|
+
if self.bankleft_down_vertex is not None:
|
329
|
+
ls2d = self.asshapely_ls()
|
330
|
+
lssz = self.asshapely_sz()
|
331
|
+
curvert = self.bankleft_down_vertex
|
332
|
+
s = ls2d.project(Point(curvert.x, curvert.y))
|
333
|
+
s3d = lssz.project(Point(s,curvert.z))
|
334
|
+
else:
|
335
|
+
s3d = 0.
|
336
|
+
return s3d
|
337
|
+
|
338
|
+
@property
|
339
|
+
def bankright_down_s3D(self):
|
340
|
+
if self.banksbed_postype == postype.BY_S3D:
|
341
|
+
return self._bankright_down
|
342
|
+
else:
|
343
|
+
# on dispose d'un vertex 3D et on doit retnvoyer une position s3D
|
344
|
+
# --> projection x,y sur la trace --> récupération de 's'
|
345
|
+
# --> calcul de la distance s3D via shapely LineString sz en projetant '(s,z)'
|
346
|
+
if self.bankright_down_vertex is not None:
|
347
|
+
ls2d = self.asshapely_ls()
|
348
|
+
lssz = self.asshapely_sz()
|
349
|
+
curvert = self.bankright_down_vertex
|
350
|
+
s = ls2d.project(Point(curvert.x, curvert.y))
|
351
|
+
s3d = lssz.project(Point(s,curvert.z))
|
352
|
+
else:
|
353
|
+
s3d = self.length3D
|
354
|
+
return s3d
|
355
|
+
|
280
356
|
@property
|
281
357
|
def bed_s3D(self):
|
282
358
|
if self.banksbed_postype == postype.BY_S3D:
|
@@ -527,7 +603,8 @@ class profile(vector):
|
|
527
603
|
curv.x +=dx
|
528
604
|
curv.y +=dy
|
529
605
|
|
530
|
-
def movebankbed_index(self,which,
|
606
|
+
def movebankbed_index(self, which:Literal['left', 'right', 'bed', 'left_down', 'right_down'],
|
607
|
+
orientation:Literal['left', 'right']) -> None:
|
531
608
|
"""
|
532
609
|
Déplacement des points de référence sur base d'un index
|
533
610
|
Le cas échéant, adaptation du mode de stockage
|
@@ -539,6 +616,10 @@ class profile(vector):
|
|
539
616
|
k = self.myvertices.index(self._bankright)
|
540
617
|
elif which=='bed':
|
541
618
|
k = self.myvertices.index(self._bed)
|
619
|
+
elif which=='left_down':
|
620
|
+
k = self.myvertices.index(self._bankleft_down)
|
621
|
+
elif which=='right_down':
|
622
|
+
k = self.myvertices.index(self._bankright_down)
|
542
623
|
|
543
624
|
if orientation=='left':
|
544
625
|
k=max(0,k-1)
|
@@ -551,6 +632,11 @@ class profile(vector):
|
|
551
632
|
self._bankright=k
|
552
633
|
elif which=='bed':
|
553
634
|
self._bed=k
|
635
|
+
elif which=='left_down':
|
636
|
+
self._bankleft_down=k
|
637
|
+
elif which=='right_down':
|
638
|
+
self._bankright_down=k
|
639
|
+
|
554
640
|
elif self.banksbed_postype == postype.BY_S3D:
|
555
641
|
if which=='left':
|
556
642
|
k = 0
|
@@ -561,6 +647,14 @@ class profile(vector):
|
|
561
647
|
elif which=='bed':
|
562
648
|
k = int(self.nbvertices/2)
|
563
649
|
self._bed=k
|
650
|
+
elif which=='left_down':
|
651
|
+
k = 1
|
652
|
+
self._bankleft_down=k
|
653
|
+
elif which=='right_down':
|
654
|
+
k = self.nbvertices-2
|
655
|
+
self._bankright_down=k
|
656
|
+
|
657
|
+
|
564
658
|
if self.banksbed_postype == postype.BY_INDEX:
|
565
659
|
if which=='left':
|
566
660
|
k = self._bankleft
|
@@ -568,6 +662,10 @@ class profile(vector):
|
|
568
662
|
k = self._bankright
|
569
663
|
elif which=='bed':
|
570
664
|
k = self._bed
|
665
|
+
elif which=='left_down':
|
666
|
+
k = self._bankleft_down
|
667
|
+
elif which=='right_down':
|
668
|
+
k = self._bankright_down
|
571
669
|
|
572
670
|
if orientation=='left':
|
573
671
|
k=max(0,k-1)
|
@@ -580,6 +678,10 @@ class profile(vector):
|
|
580
678
|
self._bankright=k
|
581
679
|
elif which=='bed':
|
582
680
|
self._bed=k
|
681
|
+
elif which=='left_down':
|
682
|
+
self._bankleft_down=k
|
683
|
+
elif which=='right_down':
|
684
|
+
self._bankright_down=k
|
583
685
|
|
584
686
|
self.banksbed_postype = postype.BY_INDEX
|
585
687
|
|
@@ -862,7 +964,7 @@ class profile(vector):
|
|
862
964
|
self.zmax = -99999
|
863
965
|
self.sz_bankbed = None
|
864
966
|
self.s3d_bankbed = None
|
865
|
-
self.
|
967
|
+
self.reset_linestring()
|
866
968
|
|
867
969
|
self.prepared=False
|
868
970
|
|
@@ -1695,6 +1797,7 @@ class crosssections(Element_To_Draw):
|
|
1695
1797
|
Gestion de sections en travers pour différents formats
|
1696
1798
|
- SPW 2000 --> format ='2000'
|
1697
1799
|
- SPW 2022 --> format ='2022'
|
1800
|
+
- SPW_2025 --> format ='2025_xlsx'
|
1698
1801
|
- WOLF vecz --> format ='vecz'
|
1699
1802
|
- WOLF sxy --> format ='sxy'
|
1700
1803
|
|
@@ -1704,6 +1807,8 @@ class crosssections(Element_To_Draw):
|
|
1704
1807
|
['left'] : wolfvertex
|
1705
1808
|
['bed'] : wolfvertex
|
1706
1809
|
['right'] : wolfvertex
|
1810
|
+
['left_down'] : wolfvertex
|
1811
|
+
['right_down']: wolfvertex
|
1707
1812
|
['cs'] : profile (surcharge de vector)
|
1708
1813
|
|
1709
1814
|
Pour le moment, il est possible de lire les fichiers et d'effectuer cerrains traitements (tri selon vecteur, export gltf...).
|
@@ -1715,7 +1820,7 @@ class crosssections(Element_To_Draw):
|
|
1715
1820
|
- cloud
|
1716
1821
|
- cloud_all
|
1717
1822
|
|
1718
|
-
:
|
1823
|
+
:attention: !! La classe n'est pas encore prévue pour créer des sections en travers!!
|
1719
1824
|
|
1720
1825
|
"""
|
1721
1826
|
|
@@ -1724,12 +1829,13 @@ class crosssections(Element_To_Draw):
|
|
1724
1829
|
|
1725
1830
|
def __init__(self,
|
1726
1831
|
myfile:str = '',
|
1727
|
-
format:typing.Literal['2000','2022','vecz','sxy']='2022',
|
1832
|
+
format:typing.Literal['2000','2022', '2025_xlsx','vecz','sxy']='2022',
|
1728
1833
|
dirlaz:typing.Union[str, xyz_laz_grids] =r'D:\OneDrive\OneDrive - Universite de Liege\Crues\2021-07 Vesdre\CSC - Convention - ARNE\Data\LAZ_Vesdre\2023',
|
1729
1834
|
mapviewer = None,
|
1730
1835
|
idx='',
|
1731
1836
|
plotted=True) -> None:
|
1732
1837
|
|
1838
|
+
assert format in ['2000','2022','2025_xlsx','vecz','sxy'], _('Format %s not supported!')%format
|
1733
1839
|
|
1734
1840
|
super().__init__(idx=idx, plotted= plotted, mapviewer=mapviewer, need_for_wx=False)
|
1735
1841
|
|
@@ -1759,6 +1865,21 @@ class crosssections(Element_To_Draw):
|
|
1759
1865
|
f=open(myfile,'r')
|
1760
1866
|
lines=f.read().splitlines()
|
1761
1867
|
f.close()
|
1868
|
+
elif format=='2025_xlsx':
|
1869
|
+
# For the 2025_xlsx format, we need to read the file using pandas
|
1870
|
+
if Path(myfile).exists() and myfile!='':
|
1871
|
+
# read the first sheet of the excel file
|
1872
|
+
# Note: header=1 means that the first row is not the header, but the second row is.
|
1873
|
+
logging.info(_('Reading cross section data from %s')%myfile)
|
1874
|
+
try:
|
1875
|
+
lines = pd.read_excel(myfile, sheet_name=0, header=1)
|
1876
|
+
logging.info(_('Cross section data read successfully from %s')%myfile)
|
1877
|
+
except Exception as e:
|
1878
|
+
logging.error(_('Error reading the file %s: %s')%(myfile, str(e)))
|
1879
|
+
lines = pd.DataFrame()
|
1880
|
+
else:
|
1881
|
+
logging.error(_('File %s does not exist!')%myfile)
|
1882
|
+
lines = []
|
1762
1883
|
# For other formats (e.g. vecz)
|
1763
1884
|
else:
|
1764
1885
|
lines=[]
|
@@ -1773,7 +1894,80 @@ class crosssections(Element_To_Draw):
|
|
1773
1894
|
self.sorted = {}
|
1774
1895
|
self.plotted = False
|
1775
1896
|
|
1776
|
-
if
|
1897
|
+
if isinstance(lines, pd.DataFrame):
|
1898
|
+
|
1899
|
+
self.format='2025_xlsx'
|
1900
|
+
|
1901
|
+
# We attend these columns:
|
1902
|
+
# 'Num', 'X', 'Y', 'Z', 'Code'
|
1903
|
+
if 'Num' not in lines.columns or 'X' not in lines.columns or 'Y' not in lines.columns or 'Z' not in lines.columns or 'Code' not in lines.columns:
|
1904
|
+
logging.error(_('The file %s does not contain the required columns: Num, X, Y, Z, Code')%self.filename)
|
1905
|
+
return
|
1906
|
+
|
1907
|
+
nbsects = int(lines['Num'].max())
|
1908
|
+
|
1909
|
+
# Convert X and Y to float
|
1910
|
+
lines['X'] = lines['X'].apply(lambda x: float(x.replace('.', '').replace(',', '.')) if isinstance(x, str) else float(x))
|
1911
|
+
lines['Y'] = lines['Y'].apply(lambda x: float(x.replace('.', '').replace(',', '.')) if isinstance(x, str) else float(x))
|
1912
|
+
# Convert Z to float and in meters
|
1913
|
+
# Note: The Z values are in mm, so we divide by 1000 to convert to meters.
|
1914
|
+
lines['Z'] = lines['Z'].apply(lambda x: float(x.replace('.', '').replace(',', '.'))/1000. if isinstance(x, str) else float(x)/1000.)
|
1915
|
+
|
1916
|
+
for index in range(1, nbsects + 1):
|
1917
|
+
#création d'un nouveau dictionnaire
|
1918
|
+
name = str(index)
|
1919
|
+
curdict = self.myprofiles[name] = {}
|
1920
|
+
curdict['index'] = index
|
1921
|
+
curdict['cs'] = profile(name=name, parent=self)
|
1922
|
+
cursect:profile
|
1923
|
+
cursect = curdict['cs']
|
1924
|
+
|
1925
|
+
df = lines[lines['Num'] == index]
|
1926
|
+
|
1927
|
+
for ___, row in df.iterrows():
|
1928
|
+
x = row['X']
|
1929
|
+
y = row['Y']
|
1930
|
+
z = row['Z']
|
1931
|
+
label = row['Code']
|
1932
|
+
|
1933
|
+
# Add vertex to the current section
|
1934
|
+
curvertex=wolfvertex(x,y,z)
|
1935
|
+
cursect.add_vertex(curvertex)
|
1936
|
+
|
1937
|
+
if label == 'HBG':
|
1938
|
+
if cursect.bankleft is None:
|
1939
|
+
cursect.bankleft = wolfvertex(x,y,z)
|
1940
|
+
curdict['left'] = cursect.bankleft
|
1941
|
+
else:
|
1942
|
+
logging.debug(name)
|
1943
|
+
elif label == 'THA':
|
1944
|
+
if cursect.bed is None:
|
1945
|
+
cursect.bed=wolfvertex(x,y,z)
|
1946
|
+
curdict['bed']=cursect.bed
|
1947
|
+
else:
|
1948
|
+
logging.debug(name)
|
1949
|
+
elif label == 'HBD':
|
1950
|
+
if cursect.bankright is None:
|
1951
|
+
cursect.bankright=wolfvertex(x,y,z)
|
1952
|
+
curdict['right']=cursect.bankright
|
1953
|
+
else:
|
1954
|
+
logging.debug(name)
|
1955
|
+
elif label == 'BBG':
|
1956
|
+
# This is a special case for the 2025 format, where the bank left down is defined as BBG (Bas Berge Gauche in French).
|
1957
|
+
if cursect.bankleft_down is None:
|
1958
|
+
cursect.bankleft_down = wolfvertex(x,y,z)
|
1959
|
+
curdict['left_down'] = cursect.bankleft_down
|
1960
|
+
else:
|
1961
|
+
logging.debug(name)
|
1962
|
+
elif label == 'BBD':
|
1963
|
+
# This is a special case for the 2025 format, where the bank right down is defined as BBD (Bas Berge Droite in French).
|
1964
|
+
if cursect.bankright_down is None:
|
1965
|
+
cursect.bankright_down = wolfvertex(x,y,z)
|
1966
|
+
curdict['right_down'] = cursect.bankright_down
|
1967
|
+
else:
|
1968
|
+
logging.debug(name)
|
1969
|
+
|
1970
|
+
elif len(lines)>0:
|
1777
1971
|
if format=='2000':
|
1778
1972
|
self.format='2000'
|
1779
1973
|
lines.pop(0)
|
@@ -1953,6 +2147,7 @@ class crosssections(Element_To_Draw):
|
|
1953
2147
|
cursect.bankright=wolfvertex(rbs,rbz)
|
1954
2148
|
curdict['right']=cursect.bankright
|
1955
2149
|
|
2150
|
+
|
1956
2151
|
# To make a distinction between cases for vecz
|
1957
2152
|
elif len(lines)==0:
|
1958
2153
|
if format=='vecz' or format=='zones':
|
@@ -1990,6 +2185,7 @@ class crosssections(Element_To_Draw):
|
|
1990
2185
|
self.init_cloud()
|
1991
2186
|
|
1992
2187
|
def init_cloud(self):
|
2188
|
+
""" Initialiaze cloud points for cross-sections. """
|
1993
2189
|
|
1994
2190
|
self.cloud = cloud_vertices()
|
1995
2191
|
self.cloud_all = cloud_vertices()
|
@@ -2001,6 +2197,11 @@ class crosssections(Element_To_Draw):
|
|
2001
2197
|
self.cloud_all.myprop.width=4
|
2002
2198
|
|
2003
2199
|
def add(self, newprofile:profile | vector):
|
2200
|
+
""" Add a new profile or vector to the cross-sections.
|
2201
|
+
|
2202
|
+
:param newprofile: A profile or vector to be added.
|
2203
|
+
:type newprofile: profile | vector
|
2204
|
+
"""
|
2004
2205
|
|
2005
2206
|
if isinstance(newprofile, profile):
|
2006
2207
|
curvec = newprofile
|
@@ -2008,9 +2209,11 @@ class crosssections(Element_To_Draw):
|
|
2008
2209
|
|
2009
2210
|
curdict['index']=len(self.myprofiles)
|
2010
2211
|
curdict['cs'] = newprofile
|
2011
|
-
curdict['left']= newprofile.bankleft_vertex.copy()
|
2012
|
-
curdict['bed']= newprofile.bed_vertex.copy()
|
2013
|
-
curdict['right']=newprofile.bankright_vertex.copy()
|
2212
|
+
curdict['left']= newprofile.bankleft_vertex.copy() if newprofile.bankleft_vertex else None
|
2213
|
+
curdict['bed']= newprofile.bed_vertex.copy() if newprofile.bed_vertex else None
|
2214
|
+
curdict['right']=newprofile.bankright_vertex.copy() if newprofile.bankright_vertex else None
|
2215
|
+
curdict['left_down'] = newprofile.bankleft_down_vertex.copy() if newprofile.bankleft_down_vertex else None
|
2216
|
+
curdict['right_down'] = newprofile.bankright_down_vertex.copy() if newprofile.bankright_down_vertex else None
|
2014
2217
|
|
2015
2218
|
elif isinstance(newprofile, vector):
|
2016
2219
|
curvec = newprofile
|
@@ -2020,6 +2223,8 @@ class crosssections(Element_To_Draw):
|
|
2020
2223
|
curdict['left']=None
|
2021
2224
|
curdict['bed']=None
|
2022
2225
|
curdict['right']=None
|
2226
|
+
curdict['left_down'] = None
|
2227
|
+
curdict['right_down'] = None
|
2023
2228
|
|
2024
2229
|
cursect = curdict['cs'] = profile(name=curvec.myname,parent=self)
|
2025
2230
|
cursect.myvertices = curvec.myvertices
|
@@ -2035,7 +2240,12 @@ class crosssections(Element_To_Draw):
|
|
2035
2240
|
|
2036
2241
|
def get_profile(self, which_prof, which_dict:str=None):
|
2037
2242
|
"""
|
2038
|
-
Recherche et renvoi d'un profil sur base du nom ou de son index et éventuellement de la liste triée
|
2243
|
+
Recherche et renvoi d'un profil sur base du nom ou de son index et éventuellement de la liste triée.
|
2244
|
+
|
2245
|
+
:param which_prof: Nom du profil ou index du profil à rechercher.
|
2246
|
+
:type which_prof: str | int
|
2247
|
+
:param which_dict: Nom du dictionnaire trié à utiliser, si applicable.
|
2248
|
+
:type which_dict: str | None
|
2039
2249
|
"""
|
2040
2250
|
if which_dict is not None:
|
2041
2251
|
# on travaille sur les vecteurs triés
|
@@ -2059,6 +2269,7 @@ class crosssections(Element_To_Draw):
|
|
2059
2269
|
return None
|
2060
2270
|
|
2061
2271
|
def fillin_cloud_all(self):
|
2272
|
+
""" Fill the cloud_all with all vertices from all profiles. """
|
2062
2273
|
|
2063
2274
|
curprof:profile
|
2064
2275
|
for idx,vect in self.myprofiles.items():
|
@@ -2069,6 +2280,7 @@ class crosssections(Element_To_Draw):
|
|
2069
2280
|
self.cloud_all.find_minmax()
|
2070
2281
|
|
2071
2282
|
def update_cloud(self):
|
2283
|
+
""" Update the cloud with vertices from all profiles. """
|
2072
2284
|
|
2073
2285
|
curprof:profile
|
2074
2286
|
for idx,vect in self.myprofiles.items():
|
@@ -2089,6 +2301,7 @@ class crosssections(Element_To_Draw):
|
|
2089
2301
|
self.cloud.find_minmax(True)
|
2090
2302
|
|
2091
2303
|
def create_zone_from_banksbed(self):
|
2304
|
+
""" Create a zone from the banks and bed of the cross-sections."""
|
2092
2305
|
|
2093
2306
|
if self.linked_zones is None:
|
2094
2307
|
return
|
@@ -2112,11 +2325,14 @@ class crosssections(Element_To_Draw):
|
|
2112
2325
|
newvec.myvertices=right
|
2113
2326
|
newzone.add_vector(newvec)
|
2114
2327
|
|
2115
|
-
def link_external_zones(self,mylink:Zones):
|
2328
|
+
def link_external_zones(self, mylink:Zones):
|
2329
|
+
""" Link the cross-sections to external zones. """
|
2330
|
+
|
2116
2331
|
self.linked_zones = mylink
|
2117
2332
|
self.find_intersect_with_link_zones()
|
2118
2333
|
|
2119
2334
|
def find_intersect_with_link_zones(self):
|
2335
|
+
""" Find intersections between the cross-sections and linked zones. """
|
2120
2336
|
|
2121
2337
|
if self.linked_zones is None:
|
2122
2338
|
return
|
@@ -2168,6 +2384,7 @@ class crosssections(Element_To_Draw):
|
|
2168
2384
|
self.update_cloud()
|
2169
2385
|
|
2170
2386
|
def export_gltf(self,zmin,fn=''):
|
2387
|
+
""" Export the cross-sections to a GLTF file. """
|
2171
2388
|
|
2172
2389
|
points=[]
|
2173
2390
|
triangles=[]
|
@@ -2249,6 +2466,7 @@ class crosssections(Element_To_Draw):
|
|
2249
2466
|
gltf.save(fn)
|
2250
2467
|
|
2251
2468
|
def export_gltf_gen(self,points,triangles,fn=''):
|
2469
|
+
""" Export generated cross-sections to a GLTF file. """
|
2252
2470
|
|
2253
2471
|
triangles_binary_blob = triangles.flatten().tobytes()
|
2254
2472
|
points_binary_blob = points.tobytes()
|
@@ -2311,6 +2529,8 @@ class crosssections(Element_To_Draw):
|
|
2311
2529
|
gltf.save(fn)
|
2312
2530
|
|
2313
2531
|
def set_zones(self, forceupdate:bool=False):
|
2532
|
+
""" Set/Prepare the zones for the cross-sections. """
|
2533
|
+
|
2314
2534
|
if forceupdate:
|
2315
2535
|
self.myzone=None
|
2316
2536
|
self.myzones=None
|
@@ -2331,10 +2551,13 @@ class crosssections(Element_To_Draw):
|
|
2331
2551
|
self._prep_listogl() #FIXME : Does not work in the context of a 1D model
|
2332
2552
|
|
2333
2553
|
def showstructure(self, parent=None, forceupdate=False):
|
2554
|
+
""" Show the structure of the cross-sections in the zones. """
|
2555
|
+
|
2334
2556
|
self.set_zones()
|
2335
2557
|
self.myzones.showstructure(parent, forceupdate)
|
2336
2558
|
|
2337
2559
|
def get_upstream(self) -> dict:
|
2560
|
+
""" Get the upstream profile of the cross-sections."""
|
2338
2561
|
curprof:profile
|
2339
2562
|
curprof=self.myprofiles[list(self.myprofiles.keys())[0]]['cs']
|
2340
2563
|
|
@@ -2344,6 +2567,7 @@ class crosssections(Element_To_Draw):
|
|
2344
2567
|
return self.myprofiles[curprof.myname]
|
2345
2568
|
|
2346
2569
|
def get_downstream(self) -> dict:
|
2570
|
+
""" Get the downstream profile of the cross-sections. """
|
2347
2571
|
curprof:profile
|
2348
2572
|
curprof=self.myprofiles[list(self.myprofiles.keys())[0]]['cs']
|
2349
2573
|
|
@@ -2352,7 +2576,14 @@ class crosssections(Element_To_Draw):
|
|
2352
2576
|
|
2353
2577
|
return self.myprofiles[curprof.myname]
|
2354
2578
|
|
2355
|
-
def rename(self,fromidx,updown=True):
|
2579
|
+
def rename(self, fromidx:int, updown:bool=True):
|
2580
|
+
"""" Rename the cross-sections starting from a given index.
|
2581
|
+
|
2582
|
+
:param fromidx: The index from which to start renaming.
|
2583
|
+
:type fromidx: int
|
2584
|
+
:param updown: If True, renames upstream sections; if False, renames all sections.
|
2585
|
+
:type updown: bool
|
2586
|
+
"""
|
2356
2587
|
|
2357
2588
|
idx=fromidx
|
2358
2589
|
|
@@ -2383,6 +2614,8 @@ class crosssections(Element_To_Draw):
|
|
2383
2614
|
self.set_zones(True)
|
2384
2615
|
|
2385
2616
|
def saveas(self,filename=None):
|
2617
|
+
""" Save the cross-sections to a file in the specified format. """
|
2618
|
+
|
2386
2619
|
self.forcesuper=False
|
2387
2620
|
|
2388
2621
|
if filename is not None:
|
@@ -2395,12 +2628,38 @@ class crosssections(Element_To_Draw):
|
|
2395
2628
|
if self.filename.endswith('.vecz'):
|
2396
2629
|
self.forcesuper=True
|
2397
2630
|
self.saveas_wolfvec(self.filename)
|
2631
|
+
|
2398
2632
|
elif self.format=='2000' or self.format=='2022':
|
2633
|
+
|
2399
2634
|
with open(self.filename,'w') as f:
|
2400
2635
|
f.write("Profile\tx\ty\tBerge\tz\n")
|
2401
2636
|
for idx,curvect in self.myprofiles.items():
|
2402
2637
|
curprof=curvect['cs']
|
2403
2638
|
curprof.save(f)
|
2639
|
+
|
2640
|
+
elif self.format=='2025_xlsx':
|
2641
|
+
# For the 2025_xlsx format, we need to save the data using pandas
|
2642
|
+
data = []
|
2643
|
+
for idx, curvect in self.myprofiles.items():
|
2644
|
+
curprof = curvect['cs']
|
2645
|
+
for vertex in curprof.myvertices:
|
2646
|
+
data.append({
|
2647
|
+
'Num': idx,
|
2648
|
+
'X': vertex.x,
|
2649
|
+
'Y': vertex.y,
|
2650
|
+
'Z': vertex.z,
|
2651
|
+
'Code': curprof.get_label(vertex)
|
2652
|
+
})
|
2653
|
+
|
2654
|
+
df = pd.DataFrame(data)
|
2655
|
+
# we need to ensure that the header begins at the second row
|
2656
|
+
df = df[['Num', 'X', 'Y', 'Z', 'Code']]
|
2657
|
+
# Save to Excel file
|
2658
|
+
with pd.ExcelWriter(self.filename, engine='openpyxl') as writer:
|
2659
|
+
# Write a blank row first, then the data starting from row 2
|
2660
|
+
pd.DataFrame([]).to_excel(writer, sheet_name='Sheet1', index=False, header=False)
|
2661
|
+
df.to_excel(writer, sheet_name='Sheet1', index=False, startrow=1)
|
2662
|
+
|
2404
2663
|
elif self.format=='sxy':
|
2405
2664
|
with open(self.filename,'w') as f:
|
2406
2665
|
f.write(str(len(self.mygenprofiles)))
|
@@ -2433,22 +2692,28 @@ class crosssections(Element_To_Draw):
|
|
2433
2692
|
|
2434
2693
|
def verif_bed(self):
|
2435
2694
|
"""Verification de l'existence du point lit mineur sinon attribution de l'altitude minimale"""
|
2695
|
+
|
2436
2696
|
for idx,curvect in self.myprofiles.items():
|
2437
2697
|
curprof=curvect['cs']
|
2438
2698
|
if curprof.bed is None:
|
2439
2699
|
curprof.bed = curprof.get_min()
|
2440
2700
|
|
2441
|
-
def get_min(self,whichname='',whichprofile=None):
|
2701
|
+
def get_min(self, whichname:str = '', whichprofile:profile = None):
|
2702
|
+
""" Get the minimum vertex of a profile or cross-section. """
|
2703
|
+
|
2442
2704
|
curvect:profile
|
2443
2705
|
if whichname!='':
|
2444
2706
|
curvect=self.myprofiles[whichname]['cs']
|
2445
2707
|
curvert=curvect.myvertices
|
2446
|
-
|
2708
|
+
|
2709
|
+
elif whichprofile is not None:
|
2447
2710
|
curvect=whichprofile['cs']
|
2448
2711
|
curvert=curvect.myvertices
|
2449
2712
|
return sorted(curvert,key=lambda x:x.z)[0]
|
2450
2713
|
|
2451
2714
|
def asshapely_ls(self):
|
2715
|
+
""" Convert the cross-sections to a MultiLineString using Shapely. """
|
2716
|
+
|
2452
2717
|
mylines=[]
|
2453
2718
|
curvect:profile
|
2454
2719
|
for idx,curvect in self.myprofiles.items():
|
@@ -2456,6 +2721,8 @@ class crosssections(Element_To_Draw):
|
|
2456
2721
|
return MultiLineString(mylines)
|
2457
2722
|
|
2458
2723
|
def prepare_shapely(self):
|
2724
|
+
""" Prepare the cross-sections for Shapely operations. """
|
2725
|
+
|
2459
2726
|
self.multils = self.asshapely_ls()
|
2460
2727
|
|
2461
2728
|
def sort_along(self,vecsupport:LineString,name:str,downfirst=True):
|
@@ -2502,6 +2769,11 @@ class crosssections(Element_To_Draw):
|
|
2502
2769
|
return len(mysorted)
|
2503
2770
|
|
2504
2771
|
def find_minmax(self, update:bool=False):
|
2772
|
+
""" Find the minimum and maximum coordinates of the cross-sections.
|
2773
|
+
|
2774
|
+
:param update: If True, updates the min/max values based on the current profiles.
|
2775
|
+
:type update: bool
|
2776
|
+
"""
|
2505
2777
|
|
2506
2778
|
if len(self.myprofiles)==0:
|
2507
2779
|
self.xmin = 0
|
@@ -2509,7 +2781,7 @@ class crosssections(Element_To_Draw):
|
|
2509
2781
|
self.xmax = 0
|
2510
2782
|
self.ymax = 0
|
2511
2783
|
return
|
2512
|
-
|
2784
|
+
|
2513
2785
|
if update:
|
2514
2786
|
for idx,vect in self.myprofiles.items():
|
2515
2787
|
vect['cs'].find_minmax(only_firstlast = True)
|
@@ -2529,11 +2801,16 @@ class crosssections(Element_To_Draw):
|
|
2529
2801
|
self.myzones.prep_listogl()
|
2530
2802
|
|
2531
2803
|
def saveas_wolfvec(self,filename:str):
|
2532
|
-
|
2804
|
+
""" Save the cross-sections as a WOLF vector file. """
|
2533
2805
|
self.set_zones()
|
2534
2806
|
self.myzones.saveas(filename=filename)
|
2535
2807
|
|
2536
|
-
def select_profile(self,x,y):
|
2808
|
+
def select_profile(self, x:float, y:float):
|
2809
|
+
""" Select the profile closest to the given coordinates (x, y).
|
2810
|
+
|
2811
|
+
:param x: X coordinate of the point.
|
2812
|
+
:param y: Y coordinate of the point.
|
2813
|
+
"""
|
2537
2814
|
|
2538
2815
|
mypt = Point(x,y)
|
2539
2816
|
distmin=1.e300
|