wolfhece 2.1.107__py3-none-any.whl → 2.1.109__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.
@@ -743,6 +743,48 @@ class vectorproperties:
743
743
  self.attachedimage = Path(props[('Image','Attached image')])
744
744
  self.imagevisible = props[('Image','To show')]
745
745
 
746
+ posx = props[('Move','Start X')]
747
+ posy = props[('Move','Start Y')]
748
+ move_step = props[('Move','Step [m]')]
749
+
750
+ if posx != 99999. and posy != 99999.:
751
+ self.parent._start_move = (posx,posy)
752
+ else:
753
+ self.parent._start_move = None
754
+
755
+ if move_step != 99999.:
756
+ self.parent._move_step = move_step
757
+ else:
758
+ self.parent._move_step = None
759
+
760
+ posx = props[('Rotation','Center X')]
761
+ posy = props[('Rotation','Center Y')]
762
+ rot_step = props[('Rotation','Step [degree]')]
763
+
764
+ if posx != 99999. and posy != 99999.:
765
+ self.parent._rotation_center = (posx,posy)
766
+ else:
767
+ self.parent._rotation_center = None
768
+
769
+ if rot_step != 99999.:
770
+ self.parent._rotation_step = rot_step
771
+ else:
772
+ self.parent._rotation_step = None
773
+
774
+ angle = props[('Rotation','Angle [degree]')]
775
+ dx = props[('Move','Delta X')]
776
+ dy = props[('Move','Delta Y')]
777
+
778
+ if angle != 0. and (dx != 0. or dy != 0.):
779
+ logging.error('Both rotation and move are selected')
780
+ elif angle != 0.:
781
+ if self.parent._rotation_center is not None:
782
+ self.parent.rotate(angle, self.parent._rotation_center)
783
+ self.parent.clear_cache()
784
+ elif dx != 0. or dy != 0.:
785
+ self.parent.move(dx,dy)
786
+ self.parent.clear_cache()
787
+
746
788
  self.load_unload_image()
747
789
 
748
790
  try:
@@ -881,6 +923,35 @@ if :\n \
881
923
  self.myprops[('Image','Attached image')] = str(self.attachedimage)
882
924
  self.myprops[('Image','To show')] = self.imagevisible
883
925
 
926
+ if self.parent._rotation_center is not None:
927
+ self.myprops[('Rotation','center X')] = self.parent._rotation_center[0]
928
+ self.myprops[('Rotation','Center Y')] = self.parent._rotation_center[1]
929
+ else:
930
+ self.myprops[('Rotation','Center X')] = 99999.
931
+ self.myprops[('Rotation','Center Y')] = 99999.
932
+
933
+ if self.parent._rotation_step is not None:
934
+ self.myprops[('Rotation','Step [degree]')] = self.parent._rotation_step
935
+ else:
936
+ self.myprops[('Rotation','Step [degree]')] = 99999.
937
+
938
+ self.myprops[('Rotation', 'Angle [degree]')] = 0.
939
+
940
+ if self.parent._start_move is not None:
941
+ self.myprops[('Move','Start X')] = self.parent._start_move[0]
942
+ self.myprops[('Move','Start Y')] = self.parent._start_move[1]
943
+ else:
944
+ self.myprops[('Move','Start X')] = 99999.
945
+ self.myprops[('Move','Start Y')] = 99999.
946
+
947
+ if self.parent._move_step is not None:
948
+ self.myprops[('Move','Step [m]')] = self.parent._move_step
949
+ else:
950
+ self.myprops[('Move','Step [m]')] = 99999.
951
+
952
+ self.myprops[('Move','Delta X')] = 0.
953
+ self.myprops[('Move','Delta Y')] = 0.
954
+
884
955
  self.myprops.Populate()
885
956
  self.myprops.Layout()
886
957
  self.myprops.SetSizeHints(500,800)
@@ -1014,6 +1085,13 @@ class vector:
1014
1085
  self.myvertices=[]
1015
1086
  self.myprop=vectorproperties(parent=self)
1016
1087
 
1088
+ self._cache_vertices = None
1089
+ self._start_move = None
1090
+ self._move_step = None
1091
+
1092
+ self._rotation_center = None
1093
+ self._rotation_step = None
1094
+
1017
1095
  self.linestring = None
1018
1096
  self.polygon = None
1019
1097
 
@@ -1026,6 +1104,94 @@ class vector:
1026
1104
  if fromshapely is not None:
1027
1105
  self.import_shapelyobj(fromshapely)
1028
1106
 
1107
+ def set_cache(self):
1108
+ """ Set the cache for the vertices """
1109
+
1110
+ self._cache_vertices = self.asnparray3d()
1111
+
1112
+ def clear_cache(self):
1113
+ """ Clear the cache for the vertices """
1114
+
1115
+ self._cache_vertices = None
1116
+
1117
+ def move(self, deltax:float, deltay:float, use_cache:bool = True, inplace:bool = True):
1118
+ """
1119
+ Move the vector
1120
+
1121
+ :param deltax: delta x
1122
+ :param deltay: delta y
1123
+ :param use_cache: use the cache if available
1124
+ :param inplace: move the vector inplace or return a new vector
1125
+ """
1126
+
1127
+ if self._move_step is not None:
1128
+ deltax = np.round(deltax/self._move_step)*self._move_step
1129
+ deltay = np.round(deltay/self._move_step)*self._move_step
1130
+
1131
+ if inplace:
1132
+ if use_cache and self._cache_vertices is None:
1133
+ self.set_cache()
1134
+
1135
+ if use_cache:
1136
+ self.xy = self._cache_vertices[:,:2] + np.array([deltax, deltay])
1137
+ else:
1138
+ for curvert in self.myvertices:
1139
+ curvert.x += deltax
1140
+ curvert.y += deltay
1141
+
1142
+ return self
1143
+ else:
1144
+ new_vector = self.deepcopy_vector(self.myname + '_moved')
1145
+ new_vector.move(deltax, deltay, inplace=True)
1146
+
1147
+ return new_vector
1148
+
1149
+ def rotate(self, angle:float, center:tuple[float,float]=(0.,0.), use_cache:bool = True, inplace:bool = True):
1150
+ """
1151
+ Rotate the vector
1152
+
1153
+ :param angle: angle in degrees
1154
+ :param center: center of the rotation
1155
+ :param use_cache: use the cache if available
1156
+ :param inplace: rotate the vector inplace or return a new vector
1157
+ """
1158
+
1159
+ if inplace:
1160
+ if use_cache and self._cache_vertices is None:
1161
+ self.set_cache()
1162
+
1163
+ if use_cache:
1164
+ locxy = self._cache_vertices[:,:2] - np.array(center)
1165
+
1166
+ rotation_matrix = np.array([[np.cos(np.radians(angle)), -np.sin(np.radians(angle))],
1167
+ [np.sin(np.radians(angle)), np.cos(np.radians(angle))]])
1168
+
1169
+ self.xy = np.dot(locxy, rotation_matrix) + np.array(center)
1170
+ else:
1171
+ for curvert in self.myvertices:
1172
+ curvert.rotate(angle, center)
1173
+
1174
+ return self
1175
+ else:
1176
+ new_vector = self.deepcopy_vector(self.myname + '_rotated')
1177
+ new_vector.rotate(angle, center, inplace=True)
1178
+
1179
+ return new_vector
1180
+
1181
+ def rotate_xy(self, x:float, y:float, use_cache:bool = True, inplace:bool = True):
1182
+ """ Rotate the vector around the rotation center and a xy point """
1183
+
1184
+ if self._rotation_center is None:
1185
+ logging.error('No rotation center defined -- set it before rotating by this routine')
1186
+ return self
1187
+
1188
+ angle = np.degrees(np.arctan2(-(y-self._rotation_center[1]), x-self._rotation_center[0]))
1189
+
1190
+ if self._rotation_step is not None:
1191
+ angle = np.round(angle/self._rotation_step)*self._rotation_step
1192
+
1193
+ return self.rotate(angle, center=self._rotation_center, use_cache=use_cache, inplace=inplace)
1194
+
1029
1195
  def get_mapviewer(self):
1030
1196
  """
1031
1197
  Retourne l'instance de la mapviewer
@@ -1352,7 +1518,7 @@ class vector:
1352
1518
 
1353
1519
  for x,y in mypar.coords:
1354
1520
  xy = Point(x,y)
1355
- curs = mypar.project(xy,True)
1521
+ curs = mypar.project(xy, True)
1356
1522
  curz = myls.interpolate(curs,True).z
1357
1523
 
1358
1524
  newvert = wolfvertex(x,y,curz)
@@ -1924,6 +2090,10 @@ class vector:
1924
2090
 
1925
2091
  Retient également les longueurs de chaque segment
1926
2092
  """
2093
+ if self.nbvertices < 2:
2094
+ logging.warning(_('No enough vertices in vector to compute lenghts'))
2095
+ return
2096
+
1927
2097
  self._lengthparts2D=np.zeros(self.nbvertices-1)
1928
2098
  self._lengthparts3D=np.zeros(self.nbvertices-1)
1929
2099
 
@@ -2048,7 +2218,7 @@ class vector:
2048
2218
  self.myvertices = newvec.myvertices
2049
2219
  self.update_lengths()
2050
2220
 
2051
- def interpolate(self,s,is3D=True,adim=True,frombegin=True):
2221
+ def interpolate(self,s,is3D=True,adim=True,frombegin=True) -> wolfvertex:
2052
2222
  """
2053
2223
  Interpolation linéaire à une abscisse curviligne 's' donnée
2054
2224
 
@@ -2790,6 +2960,11 @@ class zone:
2790
2960
  self.has_legend = False # indicate if at least one vector in the zone has a legend
2791
2961
  self.has_image = False # indicate if at least one vector in the zone has an image
2792
2962
 
2963
+ self._start_move = None # starting point for a move
2964
+ self._move_step = None # step for a move
2965
+ self._rotation_center = None # center of rotation
2966
+ self._rotation_step = None # step for rotation
2967
+
2793
2968
  if len(lines)>0:
2794
2969
  # Decoding from lines -- lines is a list of strings provided by the parent during reading
2795
2970
  # The order of the lines is important to ensure compatibility with the WOLF2D format
@@ -2823,6 +2998,80 @@ class zone:
2823
2998
  # Object can be created from a shapely object
2824
2999
  self.import_shapelyobj(fromshapely)
2825
3000
 
3001
+ def set_cache(self):
3002
+ """
3003
+ Set the cache for the zone and all its vectors
3004
+ """
3005
+ for curvect in self.myvectors:
3006
+ curvect.set_cache()
3007
+
3008
+ def clear_cache(self):
3009
+ """
3010
+ Clear the cache for the zone and all its vectors
3011
+ """
3012
+ for curvect in self.myvectors:
3013
+ curvect.clear_cache()
3014
+
3015
+ self._start_move = None
3016
+ self._move_step = None
3017
+ self._rotation_center = None
3018
+ self._rotation_step = None
3019
+
3020
+ self.find_minmax(update=True)
3021
+
3022
+ def move(self, dx:float, dy:float, use_cache:bool=True, inplace:bool=True):
3023
+ """
3024
+ Move the zone and all its vectors
3025
+ """
3026
+
3027
+ if self._move_step is not None:
3028
+ dx = np.round(dx/self._move_step)*self._move_step
3029
+ dy = np.round(dy/self._move_step)*self._move_step
3030
+
3031
+ if inplace:
3032
+ for curvect in self.myvectors:
3033
+ curvect.move(dx, dy, use_cache)
3034
+ return self
3035
+ else:
3036
+ newzone = self.deepcopy()
3037
+ newzone.move(dx, dy, use_cache= False)
3038
+ return newzone
3039
+
3040
+ def rotate(self, angle:float, center:tuple[float,float], use_cache:bool=True, inplace:bool=True):
3041
+ """
3042
+ Rotate the zone and all its vectors
3043
+
3044
+ :param angle: angle in degrees (clockwise)
3045
+ :param center: center of rotation
3046
+ :param use_cache: use the cache for the vertices
3047
+ :param inplace: modify the zone in place or return a new one
3048
+ """
3049
+
3050
+ if inplace:
3051
+ for curvect in self.myvectors:
3052
+ curvect.rotate(angle, center, use_cache)
3053
+ return self
3054
+ else:
3055
+ newzone = self.deepcopy()
3056
+ newzone.rotate(angle, center, use_cache= False)
3057
+ return newzone
3058
+
3059
+ def rotate_xy(self, x:float, y:float, use_cache:bool=True, inplace:bool=True):
3060
+ """
3061
+ Rotate the zone and all its vectors in the xy plane
3062
+ """
3063
+
3064
+ if self._rotation_center is None:
3065
+ logging.error(_('No rotation center defined - Set it before rotating by this routine'))
3066
+ return self
3067
+
3068
+ angle = np.degree(np.arctan2(y-self._rotation_center[1], x-self._rotation_center[0]))
3069
+
3070
+ if self._rotation_step is not None:
3071
+ angle = np.round(angle/self._rotation_step)*self._rotation_step
3072
+
3073
+ return self.rotate(angle, self._rotation_center, use_cache, inplace)
3074
+
2826
3075
  @property
2827
3076
  def nbvectors(self):
2828
3077
  return len(self.myvectors)
@@ -4406,6 +4655,36 @@ class zone:
4406
4655
  self.myprops[('Legend','X')] = 99999.
4407
4656
  self.myprops[('Legend','Y')] = 99999.
4408
4657
  self.myprops[('Legend','Text')] = _('Not used')
4658
+
4659
+ if self._rotation_center is None:
4660
+ self.myprops[('Rotation','Center X')] = 99999.
4661
+ self.myprops[('Rotation','Center Y')] = 99999.
4662
+ else:
4663
+ self.myprops[('Rotation','Center X')] = self._rotation_center.x
4664
+ self.myprops[('Rotation','Center Y')] = self._rotation_center.y
4665
+
4666
+ if self._rotation_step is None:
4667
+ self.myprops[('Rotation','Step [degree]')] = 99999.
4668
+ else:
4669
+ self.myprops[('Rotation','Step [degree]')] = self._rotation_step
4670
+
4671
+ self.myprops[('Rotation', 'Angle [degree]')] = 0.
4672
+
4673
+ if self._move_start is None:
4674
+ self.myprops[('Move','Start X')] = 99999.
4675
+ self.myprops[('Move','Start Y')] = 99999.
4676
+ else:
4677
+ self.myprops[('Move','Start X')] = self._move_start.x
4678
+ self.myprops[('Move','Start Y')] = self._move_start.y
4679
+
4680
+ if self._move_step is None:
4681
+ self.myprops[('Move','Step [m]')] = 99999.
4682
+ else:
4683
+ self.myprops[('Move','Step [m]')] = self._move_step
4684
+
4685
+ self.myprops[('Move', 'Delta X')] = 0.
4686
+ self.myprops[('Move', 'Delta Y')] = 0.
4687
+
4409
4688
  self.myprops.Populate()
4410
4689
  self.myprops.set_callbacks(self._callback_prop, self._callback_destroy_props)
4411
4690
 
@@ -4442,6 +4721,24 @@ class zone:
4442
4721
  for curvec in self.myvectors:
4443
4722
  curvec.myprop.fill_property(self.myprops, updateOGL = False)
4444
4723
 
4724
+ angle = self.myprops[('Rotation', 'Angle [degree]')]
4725
+ dx = self.myprops[('Move', 'Delta X')]
4726
+ dy = self.myprops[('Move', 'Delta Y')]
4727
+
4728
+ if angle!=0. and (dx!=0. or dy!=0.):
4729
+ logging.warning(_('Rotation and translation are not compatible'))
4730
+ return
4731
+ elif angle!=0.:
4732
+ if self._rotation_center is None:
4733
+ logging.warning(_('No rotation center defined'))
4734
+ return
4735
+ else:
4736
+ self.rotate(angle, self._rotation_center)
4737
+ self.clear_cache()
4738
+ elif dx!=0. or dy!=0.:
4739
+ self.move(dx, dy)
4740
+ self.clear_cache()
4741
+
4445
4742
  if self.parent.mapviewer is not None:
4446
4743
  self.prep_listogl()
4447
4744
  self.parent.mapviewer.Refresh()
@@ -4559,6 +4856,11 @@ class Zones(wx.Frame, Element_To_Draw):
4559
4856
  self.ty=ty
4560
4857
  self.myzones=[]
4561
4858
 
4859
+ self._start_move = None
4860
+ self._move_step = None
4861
+ self._rotation_center = None
4862
+ self._rotation_step = None
4863
+
4562
4864
  if self.filename!='':
4563
4865
  # lecture du fichier
4564
4866
 
@@ -4630,6 +4932,70 @@ class Zones(wx.Frame, Element_To_Draw):
4630
4932
  if plotted and self.has_OGLContext and not self.shared:
4631
4933
  self.prep_listogl()
4632
4934
 
4935
+ def set_cache(self):
4936
+ """ Set cache for all zones """
4937
+
4938
+ for curzone in self.myzones:
4939
+ curzone.set_cache()
4940
+
4941
+ def clear_cache(self):
4942
+ """ Clear cache for all zones """
4943
+
4944
+ for curzone in self.myzones:
4945
+ curzone.clear_cache()
4946
+
4947
+ def move(self, dx:float, dy:float, use_cache:bool = True, inplace:bool = True):
4948
+ """ Move all zones """
4949
+
4950
+ if self._move_step is not None:
4951
+ dx = np.round(dx/self._move_step)*self._move_step
4952
+ dy = np.round(dy/self._move_step)*self._move_step
4953
+
4954
+ if inplace:
4955
+ for curzone in self.myzones:
4956
+ curzone.move(dx, dy, use_cache=use_cache, inplace=inplace)
4957
+
4958
+ return self
4959
+
4960
+ else:
4961
+ newzones = self.deepcopy_zones()
4962
+ newzones.move(dx, dy, use_cache=False, inplace=True)
4963
+ newzones.find_minmax(True)
4964
+ return newzones
4965
+
4966
+ def rotate(self, angle:float, center:Point = None, use_cache:bool = True, inplace:bool = True):
4967
+ """ Rotate all zones """
4968
+
4969
+ if self._rotation_step is not None:
4970
+ angle = np.round(angle/self._rotation_step)*self._rotation_step
4971
+
4972
+ if inplace:
4973
+ for curzone in self.myzones:
4974
+ curzone.rotate(angle, center, use_cache=use_cache, inplace=inplace)
4975
+
4976
+ return self
4977
+
4978
+ else:
4979
+ newzones = self.deepcopy_zones()
4980
+ newzones.rotate(angle, center, use_cache=False, inplace=True)
4981
+ newzones.find_minmax(True)
4982
+ return newzones
4983
+
4984
+ def rotate_xy(self, angle:float, center:Point = None, use_cache:bool = True, inplace:bool = True):
4985
+ """ Rotate all zones """
4986
+
4987
+ if inplace:
4988
+ for curzone in self.myzones:
4989
+ curzone.rotate_xy(angle, center, use_cache=use_cache, inplace=inplace)
4990
+
4991
+ return self
4992
+
4993
+ else:
4994
+ newzones = self.deepcopy_zones()
4995
+ newzones.rotate_xy(angle, center, use_cache=False, inplace=True)
4996
+ newzones.find_minmax(True)
4997
+ return newzones
4998
+
4633
4999
  def force_unique_zone_name(self):
4634
5000
  """
4635
5001
  Check if all zones have a unique id
@@ -4663,6 +5029,8 @@ class Zones(wx.Frame, Element_To_Draw):
4663
5029
  for idx, row in content.iterrows():
4664
5030
  if 'NAME' in row.keys():
4665
5031
  name = row['NAME']
5032
+ elif 'location' in row.keys():
5033
+ name = row['location'] # tuilage gdal
4666
5034
  elif 'name' in row.keys():
4667
5035
  name = row['name']
4668
5036
  elif 'MAJ_NIV3T' in row.keys():
@@ -4794,11 +5162,21 @@ class Zones(wx.Frame, Element_To_Draw):
4794
5162
  content = gpd.read_file(fn, bbox=bbox, layer=curlayer)
4795
5163
 
4796
5164
  if len(content)>1000:
5165
+ logging.warning(_('Number of elements in layer {} : {}'.format(curlayer, len(content))))
4797
5166
  logging.warning(_('Layer {} contains more than 1000 elements -- it may take a while to import'.format(curlayer)))
4798
5167
 
5168
+ if self.wx_exists:
5169
+ dlg = wx.MessageDialog(None, _('Layer {} contains more than 1000 elements -- it may take a while to import\n\nContinue ?'.format(curlayer)), _('Warning'), wx.OK | wx.CANCEL | wx.ICON_WARNING)
5170
+ ret = dlg.ShowModal()
5171
+ dlg.Destroy()
5172
+ if ret == wx.ID_CANCEL:
5173
+ return
5174
+
4799
5175
  for idx, row in content.iterrows():
4800
5176
  if 'NAME' in row.keys():
4801
5177
  name = row['NAME']
5178
+ elif 'CANU' in row.keys():
5179
+ name = row['CANU']
4802
5180
  elif 'MAJ_NIV3T' in row.keys():
4803
5181
  # WALOUS
4804
5182
  name = row['MAJ_NIV3T']
@@ -5285,6 +5663,10 @@ class Zones(wx.Frame, Element_To_Draw):
5285
5663
  self.evaluates.SetToolTip(_("Calculate the curvilinear 's' distance using a '2D' or '3D' approach and store the result in the 5th column of the grid, X being the first one"))
5286
5664
  self.evaluates.Bind(wx.EVT_BUTTON,self.Onevaluates)
5287
5665
 
5666
+ self.update_from_s = wx.Button(self,label=_('Update from sz (support)'))
5667
+ self.update_from_s.SetToolTip(_("Update the coordinates of the vertices based on the 's' distance \n The interpolation uses the 's' value contained in the 5th column of the grid, X being the first one.\nThe support vector is in XY. It will be replaced."))
5668
+ self.update_from_s.Bind(wx.EVT_BUTTON,self.Onupdate_from_sz_support)
5669
+
5288
5670
  # Modified
5289
5671
  self.zoomonactive = wx.Button(self,label=_('Zoom on active vector'))
5290
5672
  self.zoomonactive.SetToolTip(_("Zoom on the active vector and a default view size of 500 m x 500 m"))
@@ -5326,8 +5708,8 @@ class Zones(wx.Frame, Element_To_Draw):
5326
5708
  self.slidingpoly.Bind(wx.EVT_BUTTON,self.Oncreateslidingpoly)
5327
5709
 
5328
5710
  # Added
5329
- self.getxyfromsz = wx.Button(self, label = _('Get xy from sz'))
5330
- self.getxyfromsz.SetToolTip(_("Populate the X an Y columns based on: \n - Given sz coordinates, \n - The X and Y coordinates of the initial point (s = 0) and, \n - The X and Y coordinates of a second point (any other point with an S coordinate)"))
5711
+ self.getxyfromsz = wx.Button(self, label = _('Update from sz (2 points)'))
5712
+ self.getxyfromsz.SetToolTip(_("Populate the X an Y columns based on: \n - Given sz coordinates, \n - 2 Points \n - The X and Y coordinates of the initial point (s = 0) and, \n - The X and Y coordinates of a second point (for the direction)"))
5331
5713
  self.getxyfromsz.Bind(wx.EVT_BUTTON, self.get_xy_from_sz)
5332
5714
 
5333
5715
  boxright.Add(self.xls,1,wx.EXPAND)
@@ -5351,10 +5733,16 @@ class Zones(wx.Frame, Element_To_Draw):
5351
5733
 
5352
5734
  # boxright.Add(self.zoomonactive,0,wx.EXPAND)
5353
5735
  boxright.Add(boxzoom,0,wx.EXPAND)
5354
- boxright.Add(self.evaluates,0,wx.EXPAND)
5736
+
5737
+ box_s = wx.BoxSizer(wx.HORIZONTAL)
5738
+ boxright.Add(box_s,0,wx.EXPAND)
5739
+
5740
+ box_s.Add(self.evaluates,1,wx.EXPAND)
5741
+ box_s.Add(self.update_from_s,1,wx.EXPAND)
5742
+ box_s.Add(self.getxyfromsz,1,wx.EXPAND) # Added
5743
+
5355
5744
  boxright.Add(self.interpxyz,0,wx.EXPAND)
5356
5745
  boxright.Add(self.sascending,0,wx.EXPAND)
5357
- boxright.Add(self.getxyfromsz,0,wx.EXPAND) # Added
5358
5746
 
5359
5747
  self.butgetval = wx.Button(self,label=_('Get values (self or active array)'))
5360
5748
  self.butgetval.SetToolTip(_("Get values of the attached/active array (not working with 2D results) on each vertex of the active vector and update the editor"))
@@ -5369,8 +5757,22 @@ class Zones(wx.Frame, Element_To_Draw):
5369
5757
  self.butgetrefvallinked.SetToolTip(_("Get values of all the visible arrays and 2D results on each vertex of the active vector \n and more is the step size of the array is more precise \n\n Create a new zone containing the results"))
5370
5758
  self.butgetrefvallinked.Bind(wx.EVT_BUTTON,self.Ongetvalueslinkedandref)
5371
5759
 
5760
+ self._move_rotate_sizer = wx.BoxSizer(wx.HORIZONTAL)
5761
+
5762
+ self.butmove = wx.Button(self,label=_('Move'))
5763
+ self.butmove.SetToolTip(_("Move the active vector - If not defined in properties, the first right click is the origin of the move"))
5764
+ self.butmove.Bind(wx.EVT_BUTTON,self.OnMove)
5765
+
5766
+ self.butrotate = wx.Button(self,label=_('Rotate'))
5767
+ self.butrotate.SetToolTip(_("Rotate the active vector - If not defined in properties, the first right click is the origin of the rotation"))
5768
+ self.butrotate.Bind(wx.EVT_BUTTON,self.OnRotate)
5769
+
5770
+ self._move_rotate_sizer.Add(self.butmove, 1, wx.EXPAND)
5771
+ self._move_rotate_sizer.Add(self.butrotate, 1, wx.EXPAND)
5772
+
5372
5773
  boxright.Add(self.butgetvallinked,0,wx.EXPAND)
5373
5774
  boxright.Add(self.butgetrefvallinked,0,wx.EXPAND)
5775
+ boxright.Add(self._move_rotate_sizer, 0, wx.EXPAND)
5374
5776
 
5375
5777
  self.treelist = TreeListCtrl(self,style=TL_CHECKBOX|wx.TR_FULL_ROW_HIGHLIGHT|wx.TR_EDIT_LABELS)
5376
5778
  self.treelist.AppendColumn('Zones')
@@ -5389,6 +5791,10 @@ class Zones(wx.Frame, Element_To_Draw):
5389
5791
 
5390
5792
  self.addzone = wx.Button(self,label=_('Add zone'))
5391
5793
  self.addvector = wx.Button(self,label=_('Add vector'))
5794
+
5795
+ self.duplicatezone = wx.Button(self,label=_('Duplicate zone'))
5796
+ self.duplicatevector = wx.Button(self,label=_('Duplicate vector'))
5797
+
5392
5798
  self.deletezone = wx.Button(self,label=_('Delete zone'))
5393
5799
  self.findactivevector = wx.Button(self,label=_('Find in all'))
5394
5800
  self.findactivevector.SetToolTip(_("Search and activate the nearest vector by mouse click (Searching window : all zones)"))
@@ -5402,8 +5808,24 @@ class Zones(wx.Frame, Element_To_Draw):
5402
5808
  self.downzone = wx.Button(self,label=_('Down zone'))
5403
5809
  # self.interpolate = wx.Button(self,label=_('Interpolate vector'))
5404
5810
 
5811
+ self._move_rotate_zone_sizer = wx.BoxSizer(wx.HORIZONTAL)
5812
+ self.butmove_zone = wx.Button(self,label=_('Move zone'))
5813
+ self.butmove_zone.SetToolTip(_("Move the active zone - If not defined in properties, the first right click is the origin of the move"))
5814
+ self.butmove_zone.Bind(wx.EVT_BUTTON,self.OnMoveZone)
5815
+
5816
+ self.butrotate_zone = wx.Button(self,label=_('Rotate zone'))
5817
+ self.butrotate_zone.SetToolTip(_("Rotate the active zone - If not defined in properties, the first right click is the origin of the rotation"))
5818
+ self.butrotate_zone.Bind(wx.EVT_BUTTON,self.OnRotateZone)
5819
+
5820
+ self._move_rotate_zone_sizer.Add(self.butmove_zone, 1, wx.EXPAND)
5821
+ self._move_rotate_zone_sizer.Add(self.butrotate_zone, 1, wx.EXPAND)
5822
+
5405
5823
  self.addzone.Bind(wx.EVT_BUTTON,self.OnClickadd_zone)
5406
5824
  self.addvector.Bind(wx.EVT_BUTTON,self.OnClickadd_vector)
5825
+
5826
+ self.duplicatezone.Bind(wx.EVT_BUTTON,self.OnClickduplicate_zone)
5827
+ self.duplicatevector.Bind(wx.EVT_BUTTON,self.OnClickduplicate_vector)
5828
+
5407
5829
  self.deletezone.Bind(wx.EVT_BUTTON,self.OnClickdelete_zone)
5408
5830
  self.deletevector.Bind(wx.EVT_BUTTON,self.OnClickdelete_vector)
5409
5831
  self.upvector.Bind(wx.EVT_BUTTON,self.OnClickup_vector)
@@ -5421,6 +5843,11 @@ class Zones(wx.Frame, Element_To_Draw):
5421
5843
  boxadd.Add(self.addzone,1,wx.EXPAND)
5422
5844
  boxadd.Add(self.addvector,1,wx.EXPAND)
5423
5845
 
5846
+ boxduplicate = wx.BoxSizer(wx.HORIZONTAL)
5847
+ boxduplicate.Add(self.duplicatezone,1,wx.EXPAND)
5848
+ boxduplicate.Add(self.duplicatevector,1,wx.EXPAND)
5849
+ boxadd.Add(boxduplicate,1,wx.EXPAND)
5850
+
5424
5851
  subboxadd = wx.BoxSizer(wx.HORIZONTAL)
5425
5852
  subboxadd.Add(self.findactivevector,1,wx.EXPAND)
5426
5853
  subboxadd.Add(self.findactivevectorcurz,1,wx.EXPAND)
@@ -5455,6 +5882,7 @@ class Zones(wx.Frame, Element_To_Draw):
5455
5882
  boxleft.Add(boxdelete,0,wx.EXPAND)
5456
5883
  boxleft.Add(boxupdown,0,wx.EXPAND)
5457
5884
  boxleft.Add(boxtri,0,wx.EXPAND)
5885
+ boxleft.Add(self._move_rotate_zone_sizer, 0, wx.EXPAND)
5458
5886
 
5459
5887
  box.Add(boxleft,1,wx.EXPAND)
5460
5888
  box.Add(boxright,1,wx.EXPAND)
@@ -5482,7 +5910,6 @@ class Zones(wx.Frame, Element_To_Draw):
5482
5910
 
5483
5911
  self.init_struct=False
5484
5912
 
5485
-
5486
5913
  def get_xy_from_sz(self, event: wx.Event):
5487
5914
  """
5488
5915
  Add vertices and their respectives xy coordinates from s and Z entries in the xls grid:
@@ -5491,98 +5918,7 @@ class Zones(wx.Frame, Element_To_Draw):
5491
5918
  if self.wx_exists:
5492
5919
  if self.verify_activevec():
5493
5920
  return
5494
- curv = self.active_vector
5495
- n_rows = self.xls.GetNumberRows()
5496
-
5497
- # Getting the 2 first XY coordinates
5498
- X =[]
5499
- Y = []
5500
-
5501
- z_row = 1 #Starting from the second row because the first one is the initial point
5502
-
5503
- # First row coordinates
5504
- x1 = self.xls.GetCellValue(0,0)
5505
- y1 = self.xls.GetCellValue(0,1)
5506
-
5507
-
5508
- if x1 != '' and y1 != '':
5509
- X.append(float(x1))
5510
- Y.append(float(y1))
5511
-
5512
- else:
5513
- raise Exception('Encode the coordinates of the initial point (S = 0 --> first point)')
5514
-
5515
- # Coordinates of the second points
5516
- while z_row < n_rows:
5517
- if len(X) < 2 and len(Y) < 2:
5518
- x2 = self.xls.GetCellValue(z_row,0)
5519
- y2 = self.xls.GetCellValue(z_row,1)
5520
-
5521
- if x2 != '' and y2 != '':
5522
- X.append(float(x2))
5523
- Y.append(float(y2))
5524
-
5525
- z_row += 1
5526
-
5527
- else:
5528
- break
5529
-
5530
- xy1 = np.array([X[0], Y[0]])
5531
- xy2 = np.array([X[1], Y[1]])
5532
5921
 
5533
- # Collection of sz coordinates
5534
- row = 0
5535
-
5536
- SZ = []
5537
-
5538
- while row < n_rows:
5539
- s = self.xls.GetCellValue(row,4)
5540
- z = self.xls.GetCellValue(row,2)
5541
-
5542
- if z=='':
5543
- z=0.
5544
-
5545
- if s != '':
5546
- SZ.append((s,z))
5547
- row += 1
5548
-
5549
- elif s=='': #FIXME logging msg to notify the user a point is missing
5550
- break
5551
-
5552
- else:
5553
- raise Exception (_("Recheck your data inputs"))
5554
- break
5555
-
5556
- sz = np.asarray(SZ,dtype='float64') # FIXME The type is required otherwise type == <U
5557
-
5558
- # Creation of vertices
5559
- if sz.shape[1]==2 and xy1.shape==(2,) and xy2.shape==(2,):
5560
- if not np.array_equal(xy1,xy2):
5561
- curv.myvertices=[]
5562
- curv.nbvertices = 0
5563
-
5564
- dx, dy = xy2[0]-xy1[0], xy2[1]-xy1[1]
5565
- norm = np.linalg.norm([dx,dy])
5566
- dx, dy = dx/norm, dy/norm
5567
-
5568
- for cur in sz:
5569
- x, y = xy1[0] + dx*cur[0], xy1[1] + dy*cur[0]
5570
- curv.add_vertex(wolfvertex(x, y, float(cur[1])))
5571
-
5572
- # update of the xls grid
5573
- for k in range(curv.nbvertices ):
5574
- self.xls.SetCellValue(k,0,str(curv.myvertices[k].x))
5575
- self.xls.SetCellValue(k,1,str(curv.myvertices[k].y))
5576
-
5577
-
5578
- def get_xy_from_sz(self, event: wx.Event):
5579
- """
5580
- Add vertices and their respectives xy coordinates from s and Z entries in the xls grid:
5581
- - NB: The coordinates of the initial point s= 0 and one other points should be explicitly given in the xls grid.
5582
- """
5583
- if self.wx_exists:
5584
- if self.verify_activevec():
5585
- return
5586
5922
  curv = self.active_vector
5587
5923
  n_rows = self.xls.GetNumberRows()
5588
5924
 
@@ -5651,19 +5987,7 @@ class Zones(wx.Frame, Element_To_Draw):
5651
5987
 
5652
5988
  sz = np.asarray(SZ,dtype='float64') # FIXME The type is required otherwise type == <U
5653
5989
 
5654
- # Creation of vertices
5655
- if sz.shape[1]==2 and xy1.shape==(2,) and xy2.shape==(2,):
5656
- if not np.array_equal(xy1,xy2):
5657
- curv.myvertices=[]
5658
- curv.nbvertices = 0
5659
-
5660
- dx, dy = xy2[0]-xy1[0], xy2[1]-xy1[1]
5661
- norm = np.linalg.norm([dx,dy])
5662
- dx, dy = dx/norm, dy/norm
5663
-
5664
- for cur in sz:
5665
- x, y = xy1[0] + dx*cur[0], xy1[1] + dy*cur[0]
5666
- curv.add_vertex(wolfvertex(x, y, float(cur[1])))
5990
+ self.update_from_sz_direction(xy1, xy2, sz)
5667
5991
 
5668
5992
  # update of the xls grid
5669
5993
  for k in range(curv.nbvertices ):
@@ -5828,6 +6152,56 @@ class Zones(wx.Frame, Element_To_Draw):
5828
6152
  self.expand_tree(self.active_zone)
5829
6153
  self.active_zone.reset_listogl()
5830
6154
 
6155
+ def OnMove(self, event:wx.MouseEvent):
6156
+ """
6157
+ Déplacement du vecteur actif
6158
+ """
6159
+ if self.wx_exists:
6160
+ if self.verify_activevec():
6161
+ return
6162
+
6163
+ self.mapviewer.start_action('move vector', _('Move vector'))
6164
+ self.active_vector.set_cache()
6165
+ self.mapviewer.mimicme()
6166
+
6167
+ def OnMoveZone(self, event:wx.MouseEvent):
6168
+ """
6169
+ Déplacement de la zone active
6170
+ """
6171
+ if self.wx_exists:
6172
+ if self.verify_activezone():
6173
+ return
6174
+
6175
+ self.mapviewer.start_action('move zone', _('Move zone'))
6176
+ self.active_zone.set_cache()
6177
+ self.mapviewer.mimicme()
6178
+
6179
+ def OnRotate(self, event:wx.MouseEvent):
6180
+ """
6181
+ Rotation du vecteur actif
6182
+ """
6183
+
6184
+ if self.wx_exists:
6185
+ if self.verify_activevec():
6186
+ return
6187
+
6188
+ self.mapviewer.start_action('rotate vector', _('Rotate vector'))
6189
+ self.active_vector.set_cache()
6190
+ self.mapviewer.mimicme()
6191
+
6192
+ def OnRotateZone(self, event:wx.MouseEvent):
6193
+ """
6194
+ Rotation de la zone active
6195
+ """
6196
+
6197
+ if self.wx_exists:
6198
+ if self.verify_activezone():
6199
+ return
6200
+
6201
+ self.mapviewer.start_action('rotate zone', _('Rotate zone'))
6202
+ self.active_zone.set_cache()
6203
+ self.mapviewer.mimicme()
6204
+
5831
6205
  def OncaptureandDynapar(self, event:wx.MouseEvent):
5832
6206
  """
5833
6207
  Ajoute des vertices au vecteur courant et crée des parallèles gauche-droite
@@ -6387,6 +6761,98 @@ class Zones(wx.Frame, Element_To_Draw):
6387
6761
  s+=curv._lengthparts3D[k]
6388
6762
  self.xls.SetCellValue(k+1,4,str(s))
6389
6763
 
6764
+ def Onupdate_from_sz_support(self, event:wx.MouseEvent):
6765
+ """ Update the active vector from the sz values in the xls grid """
6766
+
6767
+ if self.active_vector is None:
6768
+ logging.info(_('No active vector -- Nothing to do'))
6769
+ return
6770
+
6771
+ sz = []
6772
+
6773
+ i = 0
6774
+ while self.xls.GetCellValue(i,4) != '':
6775
+ sz.append((float(self.xls.GetCellValue(i,4)), float(self.xls.GetCellValue(i,2))))
6776
+ i += 1
6777
+
6778
+ logging.info(f'Number of points: {len(sz)}')
6779
+
6780
+ vec_sz = np.array(sz)
6781
+
6782
+ self.update_from_sz_support(vec=self.active_vector, sz=vec_sz)
6783
+
6784
+ # update of the xls grid
6785
+ for k in range(self.active_vector.nbvertices ):
6786
+ self.xls.SetCellValue(k,0,str(self.active_vector.myvertices[k].x))
6787
+ self.xls.SetCellValue(k,1,str(self.active_vector.myvertices[k].y))
6788
+
6789
+
6790
+ def update_from_sz_direction(self, xy1:np.ndarray, xy2:np.ndarray, sz:np.ndarray):
6791
+ """ Update the active vector from the sz values in the xls grid """
6792
+
6793
+ if self.active_vector is None:
6794
+ logging.info(_('No active vector -- Nothing to do'))
6795
+ return
6796
+
6797
+ curv = self.active_vector
6798
+
6799
+ # Creation of vertices
6800
+ if sz.shape[1]==2 and xy1.shape==(2,) and xy2.shape==(2,):
6801
+ if not np.array_equal(xy1,xy2):
6802
+ curv.myvertices=[]
6803
+
6804
+ dx, dy = xy2[0]-xy1[0], xy2[1]-xy1[1]
6805
+ norm = np.linalg.norm([dx,dy])
6806
+ dx, dy = dx/norm, dy/norm
6807
+
6808
+ for cur in sz:
6809
+ x, y = xy1[0] + dx*cur[0], xy1[1] + dy*cur[0]
6810
+ curv.add_vertex(wolfvertex(x, y, float(cur[1])))
6811
+
6812
+ self.find_minmax(True)
6813
+
6814
+ def update_from_sz_support(self, vec: vector, sz:np.ndarray, dialog_box = True):
6815
+
6816
+ if sz.shape[0] ==0:
6817
+ logging.warning(_('No data to update'))
6818
+ return
6819
+
6820
+ support_vec = vec.deepcopy_vector()
6821
+ support_vec.update_lengths()
6822
+
6823
+ if support_vec.length2D is None or support_vec.length3D is None:
6824
+ logging.warning(_('The support vector must be updated before updating the active vector'))
6825
+ return
6826
+
6827
+ if dialog_box:
6828
+ dlg = wx.SingleChoiceDialog(None, "Which mode?", "How to evaluate lengths?", ['2D','3D'])
6829
+ ret=dlg.ShowModal()
6830
+ if ret==wx.ID_CANCEL:
6831
+ dlg.Destroy()
6832
+ return
6833
+
6834
+ method=dlg.GetStringSelection()
6835
+ dlg.Destroy()
6836
+ else:
6837
+ method = '2D'
6838
+
6839
+ if method == '2D':
6840
+ if sz[-1,0] > support_vec.length2D:
6841
+ logging.warning(_('The last point is beyond the vector length. You must add more points !'))
6842
+ return
6843
+ else:
6844
+ if sz[-1,0] > support_vec.length3D:
6845
+ logging.warning(_('The last point is beyond the vector length. You must add more points !'))
6846
+ return
6847
+
6848
+ vec.myvertices = []
6849
+ for s,z in sz:
6850
+ new_vertex = support_vec.interpolate(s, method == '3D', adim= False)
6851
+ new_vertex.z = z
6852
+ vec.add_vertex(new_vertex)
6853
+
6854
+ self.find_minmax(True)
6855
+
6390
6856
  def evaluate_s (self, vec: vector =None, dialog_box = True):
6391
6857
  """
6392
6858
  Calcule la position curviligne du vecteur encodé
@@ -6515,6 +6981,32 @@ class Zones(wx.Frame, Element_To_Draw):
6515
6981
  else:
6516
6982
  return
6517
6983
 
6984
+ def OnClickduplicate_zone(self, event:wx.MouseEvent):
6985
+ """ Duplication de la zone active """
6986
+ if self.wx_exists:
6987
+ if self.verify_activezone():
6988
+ return
6989
+
6990
+ newzone = self.active_zone.deepcopy_zone()
6991
+ newzone.myname = self.active_zone.myname + '_copy'
6992
+ self.add_zone(newzone, forceparent=True)
6993
+ self.fill_structure()
6994
+ self.Activate_zone(newzone)
6995
+
6996
+ def OnClickduplicate_vector(self, event:wx.MouseEvent):
6997
+ """
6998
+ Duplication du vecteur actif
6999
+ """
7000
+ if self.wx_exists:
7001
+ if self.verify_activevec():
7002
+ return
7003
+
7004
+ newvec = self.active_vector.deepcopy_vector()
7005
+ newvec.myname = self.active_vector.myname + '_copy'
7006
+ self.active_zone.add_vector(newvec, forceparent=True)
7007
+ self.fill_structure()
7008
+ self.Activate_vector(newvec)
7009
+
6518
7010
  def OnClickdelete_zone(self, event:wx.MouseEvent):
6519
7011
  """
6520
7012
  Suppression de la zone courante
@@ -6769,6 +7261,48 @@ class Zones(wx.Frame, Element_To_Draw):
6769
7261
  for curvec in curzone.myvectors:
6770
7262
  curvec.myprop.fill_property(self._myprops, updateOGL = False)
6771
7263
 
7264
+ posx = self._myprops[('Move','Start X')]
7265
+ posy = self._myprops[('Move','Start Y')]
7266
+ if posx != 99999. and posy != 99999.:
7267
+ self._start_move = (posx,posy)
7268
+ else:
7269
+ self._start_move = None
7270
+
7271
+ step = self._myprops[('Move','Step [m]')]
7272
+ if step != 99999.:
7273
+ self._move_step = step
7274
+ else:
7275
+ self._move_step = None
7276
+
7277
+ posx = self._myprops[('Rotation','Center X')]
7278
+ posy = self._myprops[('Rotation','Center Y')]
7279
+ if posx != 99999. and posy != 99999.:
7280
+ self._rotation_center = (posx,posy)
7281
+ else:
7282
+ self._rotation_center = None
7283
+
7284
+ step = self._myprops[('Rotation','Step [degree]')]
7285
+ if step != 99999.:
7286
+ self._rotation_step = step
7287
+ else:
7288
+ self._rotation_step = None
7289
+
7290
+ angle = self._myprops[('Rotation','Angle [degree]')]
7291
+ dx = self._myprops[('Move','Delta X')]
7292
+ dy = self._myprops[('Move','Delta Y')]
7293
+
7294
+ if angle != 0. and (dx!= 0. or dy!=0.):
7295
+ logging.error(_('Rotation and move are not compatible - Choose one and only one'))
7296
+ elif angle!= 0.:
7297
+ if self._rotation_center is None:
7298
+ logging.error(_('No rotation center defined - Choose one'))
7299
+ else:
7300
+ self.rotate(angle, self._rotation_center)
7301
+ self.clear_cache()
7302
+ elif dx!= 0. or dy!=0.:
7303
+ self.move(dx,dy)
7304
+ self.clear_cache()
7305
+
6772
7306
  if self.mapviewer is not None:
6773
7307
  self.prep_listogl()
6774
7308
  self.mapviewer.Refresh()
@@ -6786,10 +7320,40 @@ class Zones(wx.Frame, Element_To_Draw):
6786
7320
  self._myprops[('Legend','X')] = 99999.
6787
7321
  self._myprops[('Legend','Y')] = 99999.
6788
7322
  self._myprops[('Legend','Text')] = _('Not used')
7323
+
7324
+ if self._rotation_center is not None:
7325
+ self._myprops[('Rotation', 'Center X')] = self._rotation_center[0]
7326
+ self._myprops[('Rotation', 'Center Y')] = self._rotation_center[1]
7327
+ else:
7328
+ self._myprops[('Rotation', 'Center X')] = 99999.
7329
+ self._myprops[('Rotation', 'Center Y')] = 99999.
7330
+
7331
+ if self._rotation_step is not None:
7332
+ self._myprops[('Rotation', 'Step [degree]')] = self._rotation_step
7333
+ else:
7334
+ self._myprops[('Rotation', 'Step [degree]')] = 99999.
7335
+
7336
+ self._myprops['Rotation', 'Angle [degree]'] = 0.
7337
+
7338
+ if self._start_move is not None:
7339
+ self._myprops[('Move', 'Start X')] = self._start_move[0]
7340
+ self._myprops[('Move', 'Start Y')] = self._start_move[1]
7341
+ else:
7342
+ self._myprops[('Move', 'Start X')] = 99999.
7343
+ self._myprops[('Move', 'Start Y')] = 99999.
7344
+
7345
+ self._myprops[('Move', 'Delta X')] = 0.
7346
+ self._myprops[('Move', 'Delta Y')] = 0.
7347
+
7348
+ if self._move_step is not None:
7349
+ self._myprops[('Move', 'Step [m]')] = self._move_step
7350
+ else:
7351
+ self._myprops[('Move', 'Step [m]')] = 99999.
7352
+
6789
7353
  self._myprops.Populate()
6790
7354
  self._myprops.set_callbacks(self._callback_prop, self._callback_destroy_props)
6791
7355
 
6792
- self._myprops.SetTitle(_('Properties for all vectors in {}'.format(self.myname)))
7356
+ self._myprops.SetTitle(_('Properties for all vectors in {}'.format(self.filename)))
6793
7357
  self._myprops.Center()
6794
7358
  self._myprops.Raise()
6795
7359