ign-borea 0.1.5__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.
Files changed (98) hide show
  1. borea/__init__.py +0 -0
  2. borea/datastruct/__init__.py +0 -0
  3. borea/datastruct/camera.py +25 -0
  4. borea/datastruct/dtm.py +119 -0
  5. borea/datastruct/gcp.py +22 -0
  6. borea/datastruct/shot.py +222 -0
  7. borea/datastruct/workdata.py +220 -0
  8. borea/format/__init__.py +0 -0
  9. borea/format/conl.py +143 -0
  10. borea/format/rpc.py +244 -0
  11. borea/geodesy/__init__.py +0 -0
  12. borea/geodesy/approx_euclidean_proj.py +91 -0
  13. borea/geodesy/euclidean_proj.py +25 -0
  14. borea/geodesy/local_euclidean_proj.py +127 -0
  15. borea/geodesy/proj_engine.py +70 -0
  16. borea/geodesy/projectionlist/__init__.py +0 -0
  17. borea/geodesy/projectionlist/search_proj.py +60 -0
  18. borea/geodesy/transform_geodesy.py +114 -0
  19. borea/process/__init__.py +0 -0
  20. borea/process/p_add_data/__init__.py +0 -0
  21. borea/process/p_add_data/p_add_shot.py +63 -0
  22. borea/process/p_add_data/p_file_gcp2d.py +55 -0
  23. borea/process/p_add_data/p_file_gcp3d.py +53 -0
  24. borea/process/p_add_data/p_gen_param.py +76 -0
  25. borea/process/p_add_data/p_pt2d.py +48 -0
  26. borea/process/p_add_data/p_pt3d.py +48 -0
  27. borea/process/p_add_data/p_unit_shot.py +48 -0
  28. borea/process/p_add_data/p_write.py +23 -0
  29. borea/process/p_format/__init__.py +0 -0
  30. borea/process/p_format/p_read_opk.py +78 -0
  31. borea/process/p_format/p_write_con.py +36 -0
  32. borea/process/p_format/p_write_opk.py +64 -0
  33. borea/process/p_format/p_write_rpc.py +48 -0
  34. borea/process/p_func/__init__.py +0 -0
  35. borea/process/p_func/p_control.py +67 -0
  36. borea/process/p_func/p_image_world.py +48 -0
  37. borea/process/p_func/p_spaceresection.py +51 -0
  38. borea/process/p_func/p_world_image.py +49 -0
  39. borea/reader/__init__.py +0 -0
  40. borea/reader/orientation/__init__.py +0 -0
  41. borea/reader/orientation/manage_reader.py +33 -0
  42. borea/reader/orientation/reader_opk.py +58 -0
  43. borea/reader/reader_camera.py +52 -0
  44. borea/reader/reader_point.py +113 -0
  45. borea/stat/__init__.py +0 -0
  46. borea/stat/statistics.py +215 -0
  47. borea/transform_world_image/__init__.py +0 -0
  48. borea/transform_world_image/transform_dtm/__init__.py +0 -0
  49. borea/transform_world_image/transform_dtm/world_image_dtm.py +47 -0
  50. borea/transform_world_image/transform_shot/__init__.py +0 -0
  51. borea/transform_world_image/transform_shot/conversion_coor_shot.py +58 -0
  52. borea/transform_world_image/transform_shot/image_world_shot.py +153 -0
  53. borea/transform_world_image/transform_shot/world_image_shot.py +117 -0
  54. borea/transform_world_image/transform_worksite/__init__.py +0 -0
  55. borea/transform_world_image/transform_worksite/image_world_intersection.py +154 -0
  56. borea/transform_world_image/transform_worksite/image_world_least_square.py +184 -0
  57. borea/transform_world_image/transform_worksite/image_world_work.py +49 -0
  58. borea/transform_world_image/transform_worksite/space_resection.py +343 -0
  59. borea/transform_world_image/transform_worksite/world_image_work.py +43 -0
  60. borea/utils/__init__.py +0 -0
  61. borea/utils/check/__init__.py +0 -0
  62. borea/utils/check/check_args_opk.py +59 -0
  63. borea/utils/check/check_args_reader_pt.py +44 -0
  64. borea/utils/check/check_array.py +56 -0
  65. borea/utils/check/check_header.py +90 -0
  66. borea/utils/check/check_order_axe.py +50 -0
  67. borea/utils/miscellaneous/__init__.py +0 -0
  68. borea/utils/miscellaneous/miscellaneous.py +83 -0
  69. borea/utils/miscellaneous/param_bundle.py +36 -0
  70. borea/utils/miscellaneous/sparse.py +31 -0
  71. borea/utils/singleton/__init__.py +0 -0
  72. borea/utils/singleton/singleton.py +23 -0
  73. borea/utils/xml/__init__.py +0 -0
  74. borea/utils/xml/xml.py +63 -0
  75. borea/worksite/__init__.py +0 -0
  76. borea/worksite/worksite.py +240 -0
  77. borea/writer/__init__.py +0 -0
  78. borea/writer/manage_writer.py +23 -0
  79. borea/writer/writer_con.py +29 -0
  80. borea/writer/writer_df_to_txt.py +32 -0
  81. borea/writer/writer_opk.py +70 -0
  82. borea/writer/writer_rpc.py +55 -0
  83. borea_tools/__init__.py +0 -0
  84. borea_tools/opk_control.py +33 -0
  85. borea_tools/opk_to_conl.py +33 -0
  86. borea_tools/opk_to_opk.py +33 -0
  87. borea_tools/opk_to_rpc.py +33 -0
  88. borea_tools/pt_image_to_world.py +32 -0
  89. borea_tools/pt_world_to_image.py +32 -0
  90. borea_tools/ptfile_image_to_world.py +32 -0
  91. borea_tools/ptfile_world_to_image.py +32 -0
  92. borea_tools/spaceresection_opk.py +34 -0
  93. ign_borea-0.1.5.dist-info/LICENSE +21 -0
  94. ign_borea-0.1.5.dist-info/METADATA +274 -0
  95. ign_borea-0.1.5.dist-info/RECORD +98 -0
  96. ign_borea-0.1.5.dist-info/WHEEL +5 -0
  97. ign_borea-0.1.5.dist-info/entry_points.txt +10 -0
  98. ign_borea-0.1.5.dist-info/top_level.txt +2 -0
@@ -0,0 +1,343 @@
1
+ """
2
+ Module for recalculate shooting position
3
+ """
4
+ import pandas as pd
5
+ import numpy as np
6
+ from scipy.spatial.transform import Rotation as R
7
+ from borea.utils.miscellaneous.miscellaneous import angle_degree_2vect, min_max_pt, normalize
8
+ from borea.worksite.worksite import Worksite
9
+ from borea.datastruct.camera import Camera
10
+ from borea.datastruct.shot import Shot
11
+ from borea.transform_world_image.transform_shot.world_image_shot import WorldImageShot
12
+ from borea.transform_world_image.transform_shot.image_world_shot import ImageWorldShot
13
+ # pylint: disable-next=line-too-long
14
+ from borea.transform_world_image.transform_worksite.image_world_intersection import WorldIntersection # noqa: E501
15
+ from borea.utils.miscellaneous.param_bundle import set_param_bundle_diff
16
+
17
+
18
+ class SpaceResection:
19
+ """
20
+ Recalculates the shot's 6 external orientation parameters,
21
+ the 3 angles omega, phi, kappa and its position x, y, z.
22
+
23
+ Args:
24
+ work (Woksite): Worksite to make space resection
25
+ """
26
+ def __init__(self, work: Worksite) -> None:
27
+ self.work = work
28
+
29
+ def space_resection_to_worksite(self, pt2d: pd.DataFrame, pt3d: pd.DataFrame,
30
+ pinit: dict) -> None:
31
+ """
32
+ Calculates the shot's 6 external orientation parameters,
33
+ the 3 angles omega, phi, kappa and its position x, y, z.
34
+ For all shot with a variation pixel.
35
+
36
+ Args:
37
+ pt2d (pd.Dataframe): Table of image coordinate point with image.
38
+ pt3d (pd.Dataframe): Table of ground coordinate point.
39
+ pinit (dict): Dictionnary with 3d coordinate for initialization "coor_init".
40
+ """
41
+ for name_shot, group in pt2d.groupby("id_shot"):
42
+ if group.shape[0] < 3:
43
+ print(f"Shot {name_shot} has fewer than three points,"
44
+ " so it cannot calculate its position and orientation.")
45
+ continue
46
+
47
+ dfm = group.merge(pt3d, how="inner", on="id_pt")
48
+ obs_img = np.array([dfm["column"].to_numpy(), dfm["line"].to_numpy()])
49
+ obs_world = np.array([dfm["x"].to_numpy(), dfm["y"].to_numpy(), dfm["z"].to_numpy()])
50
+
51
+ pinit["kappa"] = self.init_kappa(dfm)
52
+ self.work.shots[name_shot] = self.space_resection_shot(name_shot, obs_img,
53
+ obs_world, pinit)
54
+ self.work.set_param_shot(self.work.approxeucli)
55
+
56
+ if pt3d["type"].iloc[0]:
57
+ type_pt = "gcp2d"
58
+ else:
59
+ type_pt = "co_points"
60
+ self.work.set_point_image_dataframe(pt2d, type_pt)
61
+ self.work.set_point_world_dataframe(pt3d, type_pt)
62
+
63
+ def space_resection_on_worksite(self, add_pixel: tuple = (0, 0)) -> None:
64
+ """
65
+ Recalculates the shot's 6 external orientation parameters,
66
+ the 3 angles omega, phi, kappa and its position x, y, z.
67
+ For all shot with a variation pixel.
68
+
69
+ Args:
70
+ add_pixel (tuple): Factor (column, line) added on observable point.
71
+ """
72
+ for key_shot, item_shot in self.work.shots.items():
73
+ self.work.shots[key_shot] = self.space_resection_gap(item_shot, add_pixel)
74
+
75
+ def space_resection_shot(self, name_shot: str, pt_img: np.ndarray,
76
+ pt_world: np.ndarray, pinit: dict) -> Shot:
77
+ """
78
+ Calculates the shot's 6 external orientation parameters,
79
+ the 3 angles omega, phi, kappa and its position x, y, z.
80
+
81
+ Args:
82
+ name_shot (str): Name of shot to calculte externa parameters.
83
+ pt_img (np.array): Coordonnees image of points.
84
+ pt_world (np.array): Coordonnees world of points.
85
+ pinit (dict): Dictionnary with 3d coordinate for initialization and kappa.
86
+
87
+ Returns:
88
+ Shot: Adjusted shot.
89
+ """
90
+ # Initialization of adjusted shot
91
+ cam = self.work.cameras[list(self.work.cameras.keys())[0]]
92
+ shot_adjust = Shot(name_shot, pinit["coor_init"], np.array([0, 0, pinit["kappa"]]),
93
+ cam.name_camera, "degree", False, "opk")
94
+ shot_adjust.set_param_eucli_shot(self.work.approxeucli)
95
+ z_nadir = ImageWorldShot(shot_adjust, cam).image_to_world(np.array([cam.ppax, cam.ppay]),
96
+ self.work.type_z_shot,
97
+ self.work.type_z_shot, False)[2]
98
+ shot_adjust.set_z_nadir(z_nadir)
99
+
100
+ # Calculate euclidean position
101
+ pt_eucli = shot_adjust.projeucli.world_to_eucli(pt_world)
102
+
103
+ # Least-square methode
104
+ shot_adjust = self.least_square_shot(shot_adjust, pt_img, pt_eucli, pt_world)
105
+
106
+ return shot_adjust
107
+
108
+ def init_kappa(self, dfpt: pd.DataFrame) -> float:
109
+ """
110
+ Calculates a kappa angle for acquisition initialization.
111
+
112
+ Args:
113
+ dfpt (pd.DataFrame): Coordonnees of points in image and world.
114
+
115
+ Returns:
116
+ float: kappa angle.
117
+ """
118
+ pt_img = np.array([dfpt["column"].to_numpy(), dfpt["line"].to_numpy()])
119
+ pt_mini, arg_min = min_max_pt(pt_img, "min")
120
+ pt_maxi, arg_max = min_max_pt(pt_img, "max")
121
+ pt_minw = dfpt.iloc[arg_min].get(["x", "y"]).to_numpy()
122
+ pt_maxw = dfpt.iloc[arg_max].get(["x", "y"]).to_numpy()
123
+ vect_i = pt_maxi - pt_mini
124
+ vect_w = pt_maxw - pt_minw
125
+ norm_vi = normalize(vect_i)[0]
126
+ norm_vw = normalize(vect_w)[0]
127
+ kappa = angle_degree_2vect(norm_vi, norm_vw)
128
+ return np.floor(kappa)
129
+
130
+ def space_resection_gap(self, shot: Shot, add_pixel: tuple = (0, 0)) -> Shot:
131
+ """
132
+ Recalculates the shot's 6 external orientation parameters,
133
+ the 3 angles omega, phi, kappa and its position x, y, z.
134
+
135
+ Args:
136
+ shot (Shot): Shot to recalculte externa parameters.
137
+ add_pixel (tuble): Pixel to be added to change marker.
138
+
139
+ Returns:
140
+ Shot: Adjusted shot.
141
+ """
142
+ # Initialization observation point
143
+ obs, pt_world = self.take_obs(shot)
144
+
145
+ # Calculate euclidean position
146
+ pt_eucli = shot.projeucli.world_to_eucli(pt_world)
147
+
148
+ # Add factor
149
+ obs[0] += add_pixel[0]
150
+ obs[1] += add_pixel[1]
151
+
152
+ # Initialization of adjusted shot
153
+ shot_adjust = Shot(shot.name_shot, shot.pos_shot, shot.ori_shot,
154
+ shot.name_cam, shot.unit_angle, shot.linear_alteration, shot.order_axe)
155
+ shot_adjust.set_param_eucli_shot(shot.approxeucli)
156
+ shot_adjust.set_z_nadir(shot.z_nadir)
157
+
158
+ # Least-square methode
159
+ shot_adjust = self.least_square_shot(shot_adjust, obs, pt_eucli, pt_world)
160
+
161
+ shot_adjust.co_points = shot.co_points
162
+ shot_adjust.gcp2d = shot.gcp2d
163
+ shot_adjust.gcp3d = shot.gcp3d
164
+
165
+ return shot_adjust
166
+
167
+ def least_square_shot(self, shot_adjust: Shot, obs: np.ndarray,
168
+ pt_eucli: np.ndarray, pt_world: np.ndarray) -> Shot:
169
+ """
170
+ Least-square methode to calcule the shot's 6 external orientation parameters.
171
+
172
+ Args:
173
+ shot_adjust (Shot): The shot to adjust.
174
+ obs (np.array): Observation of point in image [c_obs, l_obs].
175
+ pt_eucli (np.array): Observation of world point in euclidean system [X, Y, Z].
176
+ pt_world (np.array): Observation of world point [X, Y, Z].
177
+
178
+ Returns:
179
+ Shot: Adjusted shot.
180
+ """
181
+ bool_iter = True
182
+ count_iter = 0
183
+ while bool_iter:
184
+ count_iter += 1
185
+
186
+ dx = self.func_least_square(shot_adjust, obs, pt_eucli, pt_world)
187
+
188
+ # Calculate new x = x0 + dx for position and rotation matrix
189
+ new_pos_eucli = np.array([shot_adjust.pos_shot[0] + dx[0],
190
+ shot_adjust.pos_shot[1] + dx[1],
191
+ shot_adjust.pos_shot[2] + dx[2]])
192
+ new_mat_eucli = shot_adjust.mat_rot_eucli @ R.from_rotvec(dx[3:]).as_matrix()
193
+
194
+ # Creation of new shot with new parameter
195
+ imc_new_adjust = Shot.from_param_euclidean(shot_adjust.name_shot, new_pos_eucli,
196
+ new_mat_eucli, shot_adjust.name_cam,
197
+ shot_adjust.unit_angle,
198
+ shot_adjust.linear_alteration,
199
+ shot_adjust.order_axe,
200
+ shot_adjust.approxeucli)
201
+ imc_new_adjust.set_z_nadir(shot_adjust.z_nadir)
202
+
203
+ # Look difference to know if you want to stop the calculation
204
+ diff_coord = np.array([imc_new_adjust.pos_shot]) - np.array([shot_adjust.pos_shot])
205
+ diff_opk = np.array([imc_new_adjust.ori_shot]) - np.array([shot_adjust.ori_shot])
206
+
207
+ if (np.all(diff_coord < 10 ** -3) and np.all(diff_opk < 10 ** -6)) or count_iter > 100:
208
+ # print(count_iter)
209
+ bool_iter = False
210
+
211
+ # Replace adjusted place
212
+ shot_adjust = imc_new_adjust
213
+
214
+ return shot_adjust
215
+
216
+ def func_least_square(self, shot_adjust: Shot, obs: np.ndarray,
217
+ pt_eucli: np.ndarray, pt_world: np.ndarray) -> np.ndarray:
218
+ """
219
+ Calculate the least-squares equation dx = (A.T @ A)**-1 @ A.T @ B
220
+
221
+ Args:
222
+ shot_adjust (Shot): The shot to adjust.
223
+ obs (np.array): Observation of point in image [c_obs, l_obs].
224
+ pt_eucli (np.array): Observation of world point in euclidean system [X, Y, Z].
225
+ pt_world (np.array): Observation of world point [X, Y, Z].
226
+
227
+ Returns:
228
+ np.array: dx.
229
+ """
230
+ cam = self.work.cameras[shot_adjust.name_cam]
231
+ # Calculate position column and line with new shot f(x0)
232
+ f0 = WorldImageShot(shot_adjust,
233
+ cam
234
+ ).world_to_image(pt_world,
235
+ self.work.type_z_data,
236
+ self.work.type_z_shot)
237
+
238
+ # Calculate residual vector B
239
+ v_res = np.c_[obs[0] - f0[0], obs[1] - f0[1]].reshape(2 * len(pt_eucli[0]))
240
+
241
+ # Creation of A with mat_obs_axia
242
+ # Calculate dx = (A.T @ A)**-1 @ A.T @ B
243
+ return np.squeeze(np.linalg.lstsq(self.mat_obs_axia(pt_eucli, shot_adjust),
244
+ v_res, rcond=None)[0])
245
+
246
+ def mat_obs_axia(self, pt_eucli: np.ndarray, imc_adjust: Shot) -> np.ndarray:
247
+ """
248
+ Setting up the mat_a matrix to solve the system by axiator.
249
+
250
+ Args:
251
+ pt_eucli (np.array): Coordinate [X, Y, Z] euclidean.
252
+ imc_adjust (Shot): adjusted shot.
253
+
254
+ Returns:
255
+ np.array: Matrix A.
256
+ """
257
+ vect_a, vect_u, mat_v = set_param_bundle_diff(imc_adjust, pt_eucli)
258
+
259
+ # Axiator of vect_a
260
+ a_axiator = np.zeros((3 * len(vect_a[0]), 3))
261
+ a_axiator[0::3, 1] = -vect_a[2]
262
+ a_axiator[0::3, 2] = vect_a[1]
263
+ a_axiator[1::3, 0] = vect_a[2]
264
+ a_axiator[1::3, 2] = -vect_a[0]
265
+ a_axiator[2::3, 0] = -vect_a[1]
266
+ a_axiator[2::3, 1] = vect_a[0]
267
+
268
+ cam = self.work.cameras[imc_adjust.name_cam]
269
+ mat_a = -np.tile(np.repeat(cam.focal / vect_u[2] ** 2, 2), (6, 1)).T
270
+ mat_a[:, :3] *= (mat_v @ imc_adjust.mat_rot_eucli)
271
+
272
+ mat_a[:, 3:] *= np.einsum('lij, ljk->lik',
273
+ (mat_v @ imc_adjust.mat_rot_eucli).reshape(-1, 2, 3),
274
+ # pylint: disable-next=too-many-function-args
275
+ a_axiator.reshape(-1, 3, 3)).reshape(-1, 3)
276
+
277
+ return mat_a
278
+
279
+ def take_obs(self, shot: Shot) -> tuple:
280
+ """
281
+ Check co point on the shot to use in observation.
282
+ If there aren't enough points, add 20 random observation points.
283
+
284
+ Args:
285
+ shot (Shot): The shot to adjust.
286
+
287
+ Returns:
288
+ tuple: np.array(obs_image), np.array(pt_world).
289
+ """
290
+ add_pt = True
291
+ pt_world_random = 0
292
+ if shot.co_points:
293
+ if not self.work.co_pts_world:
294
+ WorldIntersection(self.work).calculate_image_world_by_intersection("co_points")
295
+ obs, pt_world = self.work.get_coor_pt_img_and_world(shot.name_shot, "co_points")
296
+ if obs.shape[1] >= 7:
297
+ add_pt = False
298
+
299
+ if add_pt:
300
+ cam = self.work.cameras[shot.name_cam]
301
+ # Initialization of 20 points for shooting position
302
+ obs_random, z_world = self.seed_20_point(cam)
303
+ # Calculate world position
304
+ x_world, y_world, _ = ImageWorldShot(shot,
305
+ cam
306
+ ).image_z_to_world(obs_random,
307
+ self.work.type_z_shot,
308
+ z_world)
309
+ pt_world_random = np.array([x_world, y_world, z_world])
310
+
311
+ if add_pt and shot.co_points:
312
+ obs = np.concatenate((obs, obs_random), axis=1)
313
+ pt_world[2] += 350 # 350 is the mean of z_obs in seed_20_point
314
+ pt_world = np.concatenate((pt_world, pt_world_random), axis=1)
315
+ add_pt = False
316
+
317
+ if add_pt:
318
+ obs = obs_random
319
+ pt_world = pt_world_random
320
+
321
+ return obs, pt_world
322
+
323
+ def seed_20_point(self, cam: Camera) -> tuple:
324
+ """
325
+ Positioning of 20 points on an image by percentage position on width and height.
326
+ The z-world position is given by fixed values.
327
+
328
+ Args:
329
+ cam (Camera): Camera of the shot.
330
+
331
+ Returns:
332
+ np.array: Tuple of 3 elements (position column obs, position line obs, z world).
333
+ """
334
+ pourcent_x = np.array([6.25, 12.5, 18.75, 25, 31.25, 37.5, 37.5, 43.75, 50, 56.25,
335
+ 62.5, 62.5, 68.75, 68.75, 75, 81.25, 87.5, 87.5, 93.75, 93.75]) / 100
336
+ pourcent_y = np.array([80, 20, 60, 90, 30, 10, 50, 70, 20, 40,
337
+ 60, 90, 20, 80, 50, 20, 10, 70, 40, 90]) / 100
338
+ z_obs = np.array([200, 320, 250, 240, 330, 335, 340, 330, 350, 360,
339
+ 360, 350, 370, 355, 380, 400, 450, 400, 500, 300])
340
+ c_obs = pourcent_x * cam.width
341
+ l_obs = pourcent_y * cam.height
342
+
343
+ return np.array([c_obs, l_obs]), z_obs
@@ -0,0 +1,43 @@
1
+ """
2
+ World image transformation module for Worksite
3
+ """
4
+ from dataclasses import dataclass
5
+ from borea.worksite.worksite import Worksite
6
+ from borea.transform_world_image.transform_shot.world_image_shot import WorldImageShot
7
+
8
+
9
+ @dataclass
10
+ class WorldImageWork:
11
+ """
12
+ Class to calculate world coordinate to image coordinate in worksite.
13
+
14
+ Args:
15
+ name (str): Name of the worksite.
16
+ """
17
+ work: Worksite
18
+
19
+ def calculate_world_to_image(self, lcode: list) -> None:
20
+ """
21
+ Calculates the position of gcp3d which corresponds to the data code
22
+ in the images they appear in.
23
+
24
+ Args:
25
+ lcode (list): gcp code.
26
+ """
27
+ if self.work.gcp3d and self.work.gcp2d:
28
+ for name_gcp, gcp in self.work.gcp3d.items():
29
+ if gcp.code in lcode or lcode == []:
30
+ try:
31
+ list_shots = self.work.gcp2d[name_gcp]
32
+ for name_shot in list_shots:
33
+ shot = self.work.shots[name_shot]
34
+ cam = self.work.cameras[shot.name_cam]
35
+ coor_img = WorldImageShot(shot,
36
+ cam).world_to_image(gcp.coor,
37
+ self.work.type_z_data,
38
+ self.work.type_z_shot)
39
+ self.work.shots[name_shot].gcp3d[name_gcp] = coor_img
40
+ except KeyError:
41
+ print(f"Warning: id point {name_gcp} is present "
42
+ "in gcp3d but not in gcp2d.")
43
+ continue
File without changes
File without changes
@@ -0,0 +1,59 @@
1
+ """
2
+ A script for verification header str in manage reader.
3
+ """
4
+ from borea.utils.check.check_header import check_head, check_h_z, get_type_z_and_header
5
+
6
+
7
+ def check_args_opk(args: dict) -> tuple:
8
+ """
9
+ Check args for reading an oopk.
10
+
11
+ Args:
12
+ args (dict): Information for reading an opk file.
13
+ keys:
14
+ "interval" (list): Interval of lines taken into account,
15
+ [i, j] if i or j is None = :. e.g. [1, None] = [1:].
16
+ "header" (list): List of column type file.
17
+ "unit_angle" (str): Unit of angle 'degrees' or 'radian'.
18
+ "linear_alteration" (bool): True if data corrected by linear alteration.
19
+
20
+ Return:
21
+ tuple: args, header and type of z shot.
22
+ """
23
+ if args["unit_angle"] not in ["degree", "radian"]:
24
+ raise ValueError(f"Unit angles is incorrect {args['unit_angle']},"
25
+ "correct writing is degree or radian.")
26
+
27
+ if args["interval"][0] is not None and args["interval"][0] > 0:
28
+ args["interval"][0] -= 1
29
+ if args["interval"][1] is not None:
30
+ args["interval"][1] -= 1
31
+
32
+ header, type_z = check_header_file(args["header"])
33
+
34
+ return args, header, type_z
35
+
36
+
37
+ def check_header_file(header: list) -> tuple:
38
+ """
39
+ Check if the header of the file is good.
40
+
41
+ Args:
42
+ header (list): List of column type file.
43
+
44
+ Returns:
45
+ tuple: Header without type, type of z, type of angle.
46
+ """
47
+ list_letter = ['S', 'N', 'X', 'Y', 'Z', 'H', 'O', 'P', 'K', 'C']
48
+
49
+ bad_head, ms_error_letter, head, symbol = check_head(header, list_letter)
50
+
51
+ misss = set(list_letter[1:]) - symbol
52
+ bad_head, ms_error_letter = check_h_z(bad_head, misss, ms_error_letter)
53
+
54
+ ms_error = "Your header is not correct.\n"
55
+ ms_error += ms_error_letter
56
+ if bad_head:
57
+ raise ValueError(ms_error)
58
+
59
+ return get_type_z_and_header(head)
@@ -0,0 +1,44 @@
1
+ """
2
+ A script for verification args to read a point file.
3
+ """
4
+ from borea.utils.check.check_header import check_head, check_h_z, get_type_z_and_header
5
+
6
+
7
+ def check_header_file(header: list, type_pt: str) -> tuple:
8
+ """
9
+ Check if the header of the file is good.
10
+
11
+ Args:
12
+ header (list): List of column type file.
13
+ type_pt (str): Type of point is reading (co_point, gcp2d, gcp3d).
14
+
15
+ Returns:
16
+ tuple: header, type_z
17
+ """
18
+ type_z = None
19
+ list_letter = ['S', 'N', 'X', 'Y', 'Z', 'H', 'P', 'T']
20
+
21
+ bad_head, ms_error_letter, _, symbol = check_head(header, list_letter)
22
+
23
+ if type_pt in ["gcp3d", "pt3d"]:
24
+ ll_type = ['P', 'T', 'X', 'Y', 'Z', 'H']
25
+ misss = set(ll_type) - symbol
26
+ if misss != set() and misss != set("T"):
27
+ if "T" in misss:
28
+ misss.remove("T")
29
+ bad_head, ms_error_letter = check_h_z(bad_head, misss, ms_error_letter)
30
+ if not bad_head:
31
+ header, type_z = get_type_z_and_header(header)
32
+ else:
33
+ ll_type = ['P', 'N', 'X', 'Y']
34
+ misss = set(ll_type) - symbol
35
+ if misss != set():
36
+ bad_head = True
37
+ ms_error_letter += f"The letters {misss} are missing.\n"
38
+
39
+ ms_error = "Your header is not correct.\n"
40
+ ms_error += ms_error_letter
41
+ if bad_head:
42
+ raise ValueError(ms_error)
43
+
44
+ return header, type_z
@@ -0,0 +1,56 @@
1
+ """
2
+ A script combining various data conversion and format verification functions.
3
+ """
4
+ from typing import Union
5
+ import numpy as np
6
+
7
+
8
+ def check_array_transfo(x: Union[np.ndarray, float],
9
+ y: Union[np.ndarray, float],
10
+ z: Union[np.ndarray, float] = None) -> tuple:
11
+ """
12
+ Checks the dimension of vectors if dim = 1 outputs the element in a variable.
13
+ x,y,z are in the same dimension and they must be vectors if array
14
+ possible dimension if array : (1,), (n,), (n,1) or (1,n).
15
+
16
+ Args:
17
+ x (Union[np.array, float]): Vector x.
18
+ y (Union[np.array, float]): Vector y.
19
+ z (Union[np.array, float]): Vector z.
20
+
21
+ Returns:
22
+ tuple: x, y, z.
23
+ """
24
+ if isinstance(x, np.ndarray): # look if is an array
25
+ t = np.shape(x)
26
+ if len(t) == 1: # look dim == (n,)
27
+ if t[0] == 1: # look n == 1
28
+ # takes out the elements
29
+ x = float(x[0])
30
+ y = float(y[0])
31
+ if z is not None:
32
+ z = float(z[0])
33
+
34
+ if len(t) == 2: # look dim == (n,m)
35
+ if t[0] != 1: # look n != 1
36
+ # conversion dim (n,1) to (1,n)
37
+ x = x.T
38
+ y = y.T
39
+ if z is not None:
40
+ z = z.T
41
+ # conversion dim (1,n) to (n,)
42
+ x = np.squeeze(x, axis=0)
43
+ y = np.squeeze(y, axis=0)
44
+ if z is not None:
45
+ z = np.squeeze(z, axis=0)
46
+
47
+ # same checkup to the top
48
+ t2 = np.shape(x)
49
+ if len(t2) == 1:
50
+ if t2[0] == 1:
51
+ x = float(x[0])
52
+ y = float(y[0])
53
+ if z is not None:
54
+ z = float(z[0])
55
+
56
+ return x, y, z
@@ -0,0 +1,90 @@
1
+ """
2
+ A script for verification header list str.
3
+ """
4
+
5
+
6
+ def check_head(header: list, check_letter: list) -> tuple:
7
+ """
8
+ Checks that the header contains all the letters in the check letter.
9
+
10
+ Args:
11
+ header (list): List of column type file.
12
+ check_letter (list): List of good letter in header.
13
+
14
+ Returns:
15
+ tuple: bad_head, ms_error, head, symbol
16
+ """
17
+ bad_head = False
18
+ ms_error_letter = ""
19
+ symbol = set()
20
+ head = []
21
+
22
+ for l_type in header:
23
+ if l_type in check_letter:
24
+ if l_type not in head or l_type == "S":
25
+ head.append(l_type)
26
+ if l_type != "S":
27
+ symbol.add(l_type)
28
+ else:
29
+ bad_head = True
30
+ ms_error_letter += f"Symbol {l_type} appears several times, "
31
+ ms_error_letter += "this is incorrect, must appear only once\n"
32
+ else:
33
+ bad_head = True
34
+ ms_error_letter += f"Symbol {l_type} is not recognized, "
35
+ ms_error_letter += f"list of symbol recognized {check_letter}\n"
36
+
37
+ return bad_head, ms_error_letter, head, symbol
38
+
39
+
40
+ def check_h_z(bad_head: bool, misss: set, ms_error_letter: str) -> tuple:
41
+ """
42
+ Check letter H and Z in header.
43
+
44
+ Args:
45
+ bad_head (bool): boolean if the header is false.
46
+ misss (set): Diff between list_letter and symbol.
47
+ ms_error_letter (str): Error message.
48
+
49
+ Returns:
50
+ tuple: bad_head, ms_error_letter
51
+ """
52
+ if misss != {"Z"} and misss != {"H"} and misss != set():
53
+ bad_head = True
54
+ if "Z" in misss and "H" in misss:
55
+ miss = misss.copy()
56
+ miss.remove("Z")
57
+ miss.remove("H")
58
+ ms_error_letter += f"The letters 'Z' or 'H' are missing and lettres {miss}.\n"
59
+ elif "Z" in misss:
60
+ misss.remove("Z")
61
+ ms_error_letter += f"The letters {misss} are missing.\n"
62
+ else:
63
+ misss.remove("H")
64
+ ms_error_letter += f"The letters {misss} are missing.\n"
65
+
66
+ if misss == set():
67
+ bad_head = True
68
+ ms_error_letter += "The letters Z and H cannot be in the same string.\n"
69
+
70
+ return bad_head, ms_error_letter
71
+
72
+
73
+ def get_type_z_and_header(header: list) -> tuple:
74
+ """
75
+ Return type of z, height if H and altitude if Z
76
+ and header with the H replaced by a Z.
77
+
78
+ Args:
79
+ header (list): List of column type file.
80
+
81
+ Returns:
82
+ tuple: Header and type_z.
83
+ """
84
+ if "H" in header:
85
+ type_z = "height"
86
+ header[header.index('H')] = "Z"
87
+ else:
88
+ type_z = "altitude"
89
+
90
+ return header, type_z
@@ -0,0 +1,50 @@
1
+ """
2
+ Script to check order axe and return axe for scipy
3
+ """
4
+ from borea.utils.check.check_header import check_head
5
+
6
+
7
+ def check_order_axe(order_axe: str) -> str:
8
+ """
9
+ Check if code is good and convert for scipy rotation.
10
+
11
+ Args:
12
+ order_axe (str): Order of rotation matrix axes.
13
+
14
+ Retuns:
15
+ str: Order of ratation matrix axe for scipy.
16
+ """
17
+ list_val = ['o', 'p', 'k']
18
+
19
+ bad_head, ms_error_letter, _, symbol = check_head(list(order_axe), list_val)
20
+
21
+ misss = set(list_val) - symbol
22
+ if misss != set():
23
+ bad_head = True
24
+ ms_error_letter += f"The letters {misss} are missing.\n"
25
+
26
+ ms_error = "Your order axe is not correct.\n"
27
+ ms_error += ms_error_letter
28
+ if bad_head:
29
+ raise ValueError(ms_error)
30
+
31
+ return convert_opk_to_xyz(order_axe)
32
+
33
+
34
+ def convert_opk_to_xyz(order_axe: str) -> str:
35
+ """
36
+ Convert for scipy formalism.
37
+
38
+ Args:
39
+ order_axe (str): Order of rotation matrix axes.
40
+
41
+ Retuns:
42
+ str: Order of ratation matrix axe for scipy.
43
+ """
44
+ letter_conv = {"o": "x", "p": "y", "k": "z"}
45
+
46
+ form_xyz = ""
47
+ for i in order_axe:
48
+ form_xyz += letter_conv[i]
49
+
50
+ return form_xyz