wolfhece 2.1.108__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
@@ -5297,6 +5663,10 @@ class Zones(wx.Frame, Element_To_Draw):
5297
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"))
5298
5664
  self.evaluates.Bind(wx.EVT_BUTTON,self.Onevaluates)
5299
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
+
5300
5670
  # Modified
5301
5671
  self.zoomonactive = wx.Button(self,label=_('Zoom on active vector'))
5302
5672
  self.zoomonactive.SetToolTip(_("Zoom on the active vector and a default view size of 500 m x 500 m"))
@@ -5338,8 +5708,8 @@ class Zones(wx.Frame, Element_To_Draw):
5338
5708
  self.slidingpoly.Bind(wx.EVT_BUTTON,self.Oncreateslidingpoly)
5339
5709
 
5340
5710
  # Added
5341
- self.getxyfromsz = wx.Button(self, label = _('Get xy from sz'))
5342
- 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)"))
5343
5713
  self.getxyfromsz.Bind(wx.EVT_BUTTON, self.get_xy_from_sz)
5344
5714
 
5345
5715
  boxright.Add(self.xls,1,wx.EXPAND)
@@ -5363,10 +5733,16 @@ class Zones(wx.Frame, Element_To_Draw):
5363
5733
 
5364
5734
  # boxright.Add(self.zoomonactive,0,wx.EXPAND)
5365
5735
  boxright.Add(boxzoom,0,wx.EXPAND)
5366
- 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
+
5367
5744
  boxright.Add(self.interpxyz,0,wx.EXPAND)
5368
5745
  boxright.Add(self.sascending,0,wx.EXPAND)
5369
- boxright.Add(self.getxyfromsz,0,wx.EXPAND) # Added
5370
5746
 
5371
5747
  self.butgetval = wx.Button(self,label=_('Get values (self or active array)'))
5372
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"))
@@ -5381,8 +5757,22 @@ class Zones(wx.Frame, Element_To_Draw):
5381
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"))
5382
5758
  self.butgetrefvallinked.Bind(wx.EVT_BUTTON,self.Ongetvalueslinkedandref)
5383
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
+
5384
5773
  boxright.Add(self.butgetvallinked,0,wx.EXPAND)
5385
5774
  boxright.Add(self.butgetrefvallinked,0,wx.EXPAND)
5775
+ boxright.Add(self._move_rotate_sizer, 0, wx.EXPAND)
5386
5776
 
5387
5777
  self.treelist = TreeListCtrl(self,style=TL_CHECKBOX|wx.TR_FULL_ROW_HIGHLIGHT|wx.TR_EDIT_LABELS)
5388
5778
  self.treelist.AppendColumn('Zones')
@@ -5401,6 +5791,10 @@ class Zones(wx.Frame, Element_To_Draw):
5401
5791
 
5402
5792
  self.addzone = wx.Button(self,label=_('Add zone'))
5403
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
+
5404
5798
  self.deletezone = wx.Button(self,label=_('Delete zone'))
5405
5799
  self.findactivevector = wx.Button(self,label=_('Find in all'))
5406
5800
  self.findactivevector.SetToolTip(_("Search and activate the nearest vector by mouse click (Searching window : all zones)"))
@@ -5414,8 +5808,24 @@ class Zones(wx.Frame, Element_To_Draw):
5414
5808
  self.downzone = wx.Button(self,label=_('Down zone'))
5415
5809
  # self.interpolate = wx.Button(self,label=_('Interpolate vector'))
5416
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
+
5417
5823
  self.addzone.Bind(wx.EVT_BUTTON,self.OnClickadd_zone)
5418
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
+
5419
5829
  self.deletezone.Bind(wx.EVT_BUTTON,self.OnClickdelete_zone)
5420
5830
  self.deletevector.Bind(wx.EVT_BUTTON,self.OnClickdelete_vector)
5421
5831
  self.upvector.Bind(wx.EVT_BUTTON,self.OnClickup_vector)
@@ -5433,6 +5843,11 @@ class Zones(wx.Frame, Element_To_Draw):
5433
5843
  boxadd.Add(self.addzone,1,wx.EXPAND)
5434
5844
  boxadd.Add(self.addvector,1,wx.EXPAND)
5435
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
+
5436
5851
  subboxadd = wx.BoxSizer(wx.HORIZONTAL)
5437
5852
  subboxadd.Add(self.findactivevector,1,wx.EXPAND)
5438
5853
  subboxadd.Add(self.findactivevectorcurz,1,wx.EXPAND)
@@ -5467,6 +5882,7 @@ class Zones(wx.Frame, Element_To_Draw):
5467
5882
  boxleft.Add(boxdelete,0,wx.EXPAND)
5468
5883
  boxleft.Add(boxupdown,0,wx.EXPAND)
5469
5884
  boxleft.Add(boxtri,0,wx.EXPAND)
5885
+ boxleft.Add(self._move_rotate_zone_sizer, 0, wx.EXPAND)
5470
5886
 
5471
5887
  box.Add(boxleft,1,wx.EXPAND)
5472
5888
  box.Add(boxright,1,wx.EXPAND)
@@ -5494,7 +5910,6 @@ class Zones(wx.Frame, Element_To_Draw):
5494
5910
 
5495
5911
  self.init_struct=False
5496
5912
 
5497
-
5498
5913
  def get_xy_from_sz(self, event: wx.Event):
5499
5914
  """
5500
5915
  Add vertices and their respectives xy coordinates from s and Z entries in the xls grid:
@@ -5503,98 +5918,7 @@ class Zones(wx.Frame, Element_To_Draw):
5503
5918
  if self.wx_exists:
5504
5919
  if self.verify_activevec():
5505
5920
  return
5506
- curv = self.active_vector
5507
- n_rows = self.xls.GetNumberRows()
5508
-
5509
- # Getting the 2 first XY coordinates
5510
- X =[]
5511
- Y = []
5512
-
5513
- z_row = 1 #Starting from the second row because the first one is the initial point
5514
-
5515
- # First row coordinates
5516
- x1 = self.xls.GetCellValue(0,0)
5517
- y1 = self.xls.GetCellValue(0,1)
5518
-
5519
5921
 
5520
- if x1 != '' and y1 != '':
5521
- X.append(float(x1))
5522
- Y.append(float(y1))
5523
-
5524
- else:
5525
- raise Exception('Encode the coordinates of the initial point (S = 0 --> first point)')
5526
-
5527
- # Coordinates of the second points
5528
- while z_row < n_rows:
5529
- if len(X) < 2 and len(Y) < 2:
5530
- x2 = self.xls.GetCellValue(z_row,0)
5531
- y2 = self.xls.GetCellValue(z_row,1)
5532
-
5533
- if x2 != '' and y2 != '':
5534
- X.append(float(x2))
5535
- Y.append(float(y2))
5536
-
5537
- z_row += 1
5538
-
5539
- else:
5540
- break
5541
-
5542
- xy1 = np.array([X[0], Y[0]])
5543
- xy2 = np.array([X[1], Y[1]])
5544
-
5545
- # Collection of sz coordinates
5546
- row = 0
5547
-
5548
- SZ = []
5549
-
5550
- while row < n_rows:
5551
- s = self.xls.GetCellValue(row,4)
5552
- z = self.xls.GetCellValue(row,2)
5553
-
5554
- if z=='':
5555
- z=0.
5556
-
5557
- if s != '':
5558
- SZ.append((s,z))
5559
- row += 1
5560
-
5561
- elif s=='': #FIXME logging msg to notify the user a point is missing
5562
- break
5563
-
5564
- else:
5565
- raise Exception (_("Recheck your data inputs"))
5566
- break
5567
-
5568
- sz = np.asarray(SZ,dtype='float64') # FIXME The type is required otherwise type == <U
5569
-
5570
- # Creation of vertices
5571
- if sz.shape[1]==2 and xy1.shape==(2,) and xy2.shape==(2,):
5572
- if not np.array_equal(xy1,xy2):
5573
- curv.myvertices=[]
5574
- curv.nbvertices = 0
5575
-
5576
- dx, dy = xy2[0]-xy1[0], xy2[1]-xy1[1]
5577
- norm = np.linalg.norm([dx,dy])
5578
- dx, dy = dx/norm, dy/norm
5579
-
5580
- for cur in sz:
5581
- x, y = xy1[0] + dx*cur[0], xy1[1] + dy*cur[0]
5582
- curv.add_vertex(wolfvertex(x, y, float(cur[1])))
5583
-
5584
- # update of the xls grid
5585
- for k in range(curv.nbvertices ):
5586
- self.xls.SetCellValue(k,0,str(curv.myvertices[k].x))
5587
- self.xls.SetCellValue(k,1,str(curv.myvertices[k].y))
5588
-
5589
-
5590
- def get_xy_from_sz(self, event: wx.Event):
5591
- """
5592
- Add vertices and their respectives xy coordinates from s and Z entries in the xls grid:
5593
- - NB: The coordinates of the initial point s= 0 and one other points should be explicitly given in the xls grid.
5594
- """
5595
- if self.wx_exists:
5596
- if self.verify_activevec():
5597
- return
5598
5922
  curv = self.active_vector
5599
5923
  n_rows = self.xls.GetNumberRows()
5600
5924
 
@@ -5663,19 +5987,7 @@ class Zones(wx.Frame, Element_To_Draw):
5663
5987
 
5664
5988
  sz = np.asarray(SZ,dtype='float64') # FIXME The type is required otherwise type == <U
5665
5989
 
5666
- # Creation of vertices
5667
- if sz.shape[1]==2 and xy1.shape==(2,) and xy2.shape==(2,):
5668
- if not np.array_equal(xy1,xy2):
5669
- curv.myvertices=[]
5670
- curv.nbvertices = 0
5671
-
5672
- dx, dy = xy2[0]-xy1[0], xy2[1]-xy1[1]
5673
- norm = np.linalg.norm([dx,dy])
5674
- dx, dy = dx/norm, dy/norm
5675
-
5676
- for cur in sz:
5677
- x, y = xy1[0] + dx*cur[0], xy1[1] + dy*cur[0]
5678
- curv.add_vertex(wolfvertex(x, y, float(cur[1])))
5990
+ self.update_from_sz_direction(xy1, xy2, sz)
5679
5991
 
5680
5992
  # update of the xls grid
5681
5993
  for k in range(curv.nbvertices ):
@@ -5840,6 +6152,56 @@ class Zones(wx.Frame, Element_To_Draw):
5840
6152
  self.expand_tree(self.active_zone)
5841
6153
  self.active_zone.reset_listogl()
5842
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
+
5843
6205
  def OncaptureandDynapar(self, event:wx.MouseEvent):
5844
6206
  """
5845
6207
  Ajoute des vertices au vecteur courant et crée des parallèles gauche-droite
@@ -6399,6 +6761,98 @@ class Zones(wx.Frame, Element_To_Draw):
6399
6761
  s+=curv._lengthparts3D[k]
6400
6762
  self.xls.SetCellValue(k+1,4,str(s))
6401
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
+
6402
6856
  def evaluate_s (self, vec: vector =None, dialog_box = True):
6403
6857
  """
6404
6858
  Calcule la position curviligne du vecteur encodé
@@ -6527,6 +6981,32 @@ class Zones(wx.Frame, Element_To_Draw):
6527
6981
  else:
6528
6982
  return
6529
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
+
6530
7010
  def OnClickdelete_zone(self, event:wx.MouseEvent):
6531
7011
  """
6532
7012
  Suppression de la zone courante
@@ -6781,6 +7261,48 @@ class Zones(wx.Frame, Element_To_Draw):
6781
7261
  for curvec in curzone.myvectors:
6782
7262
  curvec.myprop.fill_property(self._myprops, updateOGL = False)
6783
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
+
6784
7306
  if self.mapviewer is not None:
6785
7307
  self.prep_listogl()
6786
7308
  self.mapviewer.Refresh()
@@ -6798,10 +7320,40 @@ class Zones(wx.Frame, Element_To_Draw):
6798
7320
  self._myprops[('Legend','X')] = 99999.
6799
7321
  self._myprops[('Legend','Y')] = 99999.
6800
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
+
6801
7353
  self._myprops.Populate()
6802
7354
  self._myprops.set_callbacks(self._callback_prop, self._callback_destroy_props)
6803
7355
 
6804
- self._myprops.SetTitle(_('Properties for all vectors in {}'.format(self.myname)))
7356
+ self._myprops.SetTitle(_('Properties for all vectors in {}'.format(self.filename)))
6805
7357
  self._myprops.Center()
6806
7358
  self._myprops.Raise()
6807
7359