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.
- borea/__init__.py +0 -0
- borea/datastruct/__init__.py +0 -0
- borea/datastruct/camera.py +25 -0
- borea/datastruct/dtm.py +119 -0
- borea/datastruct/gcp.py +22 -0
- borea/datastruct/shot.py +222 -0
- borea/datastruct/workdata.py +220 -0
- borea/format/__init__.py +0 -0
- borea/format/conl.py +143 -0
- borea/format/rpc.py +244 -0
- borea/geodesy/__init__.py +0 -0
- borea/geodesy/approx_euclidean_proj.py +91 -0
- borea/geodesy/euclidean_proj.py +25 -0
- borea/geodesy/local_euclidean_proj.py +127 -0
- borea/geodesy/proj_engine.py +70 -0
- borea/geodesy/projectionlist/__init__.py +0 -0
- borea/geodesy/projectionlist/search_proj.py +60 -0
- borea/geodesy/transform_geodesy.py +114 -0
- borea/process/__init__.py +0 -0
- borea/process/p_add_data/__init__.py +0 -0
- borea/process/p_add_data/p_add_shot.py +63 -0
- borea/process/p_add_data/p_file_gcp2d.py +55 -0
- borea/process/p_add_data/p_file_gcp3d.py +53 -0
- borea/process/p_add_data/p_gen_param.py +76 -0
- borea/process/p_add_data/p_pt2d.py +48 -0
- borea/process/p_add_data/p_pt3d.py +48 -0
- borea/process/p_add_data/p_unit_shot.py +48 -0
- borea/process/p_add_data/p_write.py +23 -0
- borea/process/p_format/__init__.py +0 -0
- borea/process/p_format/p_read_opk.py +78 -0
- borea/process/p_format/p_write_con.py +36 -0
- borea/process/p_format/p_write_opk.py +64 -0
- borea/process/p_format/p_write_rpc.py +48 -0
- borea/process/p_func/__init__.py +0 -0
- borea/process/p_func/p_control.py +67 -0
- borea/process/p_func/p_image_world.py +48 -0
- borea/process/p_func/p_spaceresection.py +51 -0
- borea/process/p_func/p_world_image.py +49 -0
- borea/reader/__init__.py +0 -0
- borea/reader/orientation/__init__.py +0 -0
- borea/reader/orientation/manage_reader.py +33 -0
- borea/reader/orientation/reader_opk.py +58 -0
- borea/reader/reader_camera.py +52 -0
- borea/reader/reader_point.py +113 -0
- borea/stat/__init__.py +0 -0
- borea/stat/statistics.py +215 -0
- borea/transform_world_image/__init__.py +0 -0
- borea/transform_world_image/transform_dtm/__init__.py +0 -0
- borea/transform_world_image/transform_dtm/world_image_dtm.py +47 -0
- borea/transform_world_image/transform_shot/__init__.py +0 -0
- borea/transform_world_image/transform_shot/conversion_coor_shot.py +58 -0
- borea/transform_world_image/transform_shot/image_world_shot.py +153 -0
- borea/transform_world_image/transform_shot/world_image_shot.py +117 -0
- borea/transform_world_image/transform_worksite/__init__.py +0 -0
- borea/transform_world_image/transform_worksite/image_world_intersection.py +154 -0
- borea/transform_world_image/transform_worksite/image_world_least_square.py +184 -0
- borea/transform_world_image/transform_worksite/image_world_work.py +49 -0
- borea/transform_world_image/transform_worksite/space_resection.py +343 -0
- borea/transform_world_image/transform_worksite/world_image_work.py +43 -0
- borea/utils/__init__.py +0 -0
- borea/utils/check/__init__.py +0 -0
- borea/utils/check/check_args_opk.py +59 -0
- borea/utils/check/check_args_reader_pt.py +44 -0
- borea/utils/check/check_array.py +56 -0
- borea/utils/check/check_header.py +90 -0
- borea/utils/check/check_order_axe.py +50 -0
- borea/utils/miscellaneous/__init__.py +0 -0
- borea/utils/miscellaneous/miscellaneous.py +83 -0
- borea/utils/miscellaneous/param_bundle.py +36 -0
- borea/utils/miscellaneous/sparse.py +31 -0
- borea/utils/singleton/__init__.py +0 -0
- borea/utils/singleton/singleton.py +23 -0
- borea/utils/xml/__init__.py +0 -0
- borea/utils/xml/xml.py +63 -0
- borea/worksite/__init__.py +0 -0
- borea/worksite/worksite.py +240 -0
- borea/writer/__init__.py +0 -0
- borea/writer/manage_writer.py +23 -0
- borea/writer/writer_con.py +29 -0
- borea/writer/writer_df_to_txt.py +32 -0
- borea/writer/writer_opk.py +70 -0
- borea/writer/writer_rpc.py +55 -0
- borea_tools/__init__.py +0 -0
- borea_tools/opk_control.py +33 -0
- borea_tools/opk_to_conl.py +33 -0
- borea_tools/opk_to_opk.py +33 -0
- borea_tools/opk_to_rpc.py +33 -0
- borea_tools/pt_image_to_world.py +32 -0
- borea_tools/pt_world_to_image.py +32 -0
- borea_tools/ptfile_image_to_world.py +32 -0
- borea_tools/ptfile_world_to_image.py +32 -0
- borea_tools/spaceresection_opk.py +34 -0
- ign_borea-0.1.5.dist-info/LICENSE +21 -0
- ign_borea-0.1.5.dist-info/METADATA +274 -0
- ign_borea-0.1.5.dist-info/RECORD +98 -0
- ign_borea-0.1.5.dist-info/WHEEL +5 -0
- ign_borea-0.1.5.dist-info/entry_points.txt +10 -0
- ign_borea-0.1.5.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Photogrammetry worksite to writing in rpc.
|
|
3
|
+
"""
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path, PureWindowsPath
|
|
6
|
+
from borea.format.rpc import Rpc
|
|
7
|
+
from borea.worksite.worksite import Worksite
|
|
8
|
+
from borea.datastruct.dtm import Dtm
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def write(name: str, folder_rpc: str, param_rpc: dict, work: Worksite) -> None:
|
|
12
|
+
"""
|
|
13
|
+
Converte Worksite in RPC class and save it in txt.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
name (str): Name of file begin.
|
|
17
|
+
folder_rpc (str): Path of folder to registration file .txt.
|
|
18
|
+
param_rpc (dict): Dictionary of parameters for rpc calculation.
|
|
19
|
+
key;
|
|
20
|
+
"size_grid"; size of the grip to calcule rpc.
|
|
21
|
+
"order"; order of the polynome of the rpc.
|
|
22
|
+
"fact_rpc"; rpc factor for world coordinate when src is not WGS84.
|
|
23
|
+
work (Worksite): The site to be recorded.
|
|
24
|
+
"""
|
|
25
|
+
_ = name
|
|
26
|
+
keys = ["ERR_BIAS", "ERR_RAND", "LINE_OFF", "SAMP_OFF",
|
|
27
|
+
"LAT_OFF", "LONG_OFF", "HEIGHT_OFF", "LINE_SCALE",
|
|
28
|
+
"SAMP_SCALE", "LAT_SCALE", "LONG_SCALE",
|
|
29
|
+
"HEIGHT_SCALE"]
|
|
30
|
+
|
|
31
|
+
work.set_unit_shot(type_z=Dtm().type_dtm)
|
|
32
|
+
|
|
33
|
+
for name_shot, shot in work.shots.items():
|
|
34
|
+
cam = work.cameras[shot.name_cam]
|
|
35
|
+
|
|
36
|
+
rpc = Rpc.from_shot(shot, cam, param_rpc,
|
|
37
|
+
{"unit_z_data": work.type_z_data, "unit_z_shot": work.type_z_shot})
|
|
38
|
+
|
|
39
|
+
list_txt_rpc = [f"{key}: {rpc.param_rpc[key]}" for key in keys]
|
|
40
|
+
|
|
41
|
+
for idx, val in enumerate(rpc.param_rpc["LINE_NUM_COEFF"]):
|
|
42
|
+
list_txt_rpc += [f"LINE_NUM_COEFF_{idx + 1}: {val}"]
|
|
43
|
+
|
|
44
|
+
for idx, val in enumerate(rpc.param_rpc["LINE_DEN_COEFF"]):
|
|
45
|
+
list_txt_rpc += [f"LINE_DEN_COEFF_{idx + 1}: {val}"]
|
|
46
|
+
|
|
47
|
+
for idx, val in enumerate(rpc.param_rpc["SAMP_NUM_COEFF"]):
|
|
48
|
+
list_txt_rpc += [f"SAMP_NUM_COEFF_{idx + 1}: {val}"]
|
|
49
|
+
|
|
50
|
+
for idx, val in enumerate(rpc.param_rpc["SAMP_DEN_COEFF"]):
|
|
51
|
+
list_txt_rpc += [f"SAMP_DEN_COEFF_{idx + 1}: {val}"]
|
|
52
|
+
|
|
53
|
+
path_rpc = os.path.join(Path(PureWindowsPath(folder_rpc)),
|
|
54
|
+
f"{name_shot}_RPC.TXT")
|
|
55
|
+
Path(path_rpc).write_text("\n".join(list_txt_rpc), encoding="UTF-8")
|
borea_tools/__init__.py
ADDED
|
File without changes
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to control opk file
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_func.p_control import args_control, process_args_control # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def opk_control():
|
|
15
|
+
"""
|
|
16
|
+
Controls the accuracy of an OPK file with support points.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Photogrammetric site control opk file')
|
|
19
|
+
|
|
20
|
+
# Args for implement control opk
|
|
21
|
+
parser = args_reading_opk(parser)
|
|
22
|
+
parser = args_control(parser)
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
# Process to read data
|
|
27
|
+
work = process_args_read_opk(args)
|
|
28
|
+
# Process to control data
|
|
29
|
+
process_args_control(args, work)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
opk_control()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to convert opk file to an light conical file (it's an xml file for GEOVIEW IGN France)
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_format.p_write_con import args_write_con, process_args_write_con # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def opk_to_conl():
|
|
15
|
+
"""
|
|
16
|
+
Converts an OPK file into an light conical (IGN format) file.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Photogrammetric site conversion '
|
|
19
|
+
'opk to conical file.')
|
|
20
|
+
# Args for implement opk to conl
|
|
21
|
+
parser = args_reading_opk(parser)
|
|
22
|
+
parser = args_write_con(parser)
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
# Process to read data
|
|
27
|
+
work = process_args_read_opk(args)
|
|
28
|
+
# Process to write conl
|
|
29
|
+
process_args_write_con(args, work)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
opk_to_conl()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to convert opk file to an other opk file
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position, line-too-long
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_format.p_write_opk import args_writing_opk, process_args_write_opk # noqa: E402, E501
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def opk_to_opk():
|
|
15
|
+
"""
|
|
16
|
+
Converts an OPK file into an OPK file.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Photogrammetric site conversion'
|
|
19
|
+
' and manipulation software opk to opk.')
|
|
20
|
+
# Args for implement opk to opk
|
|
21
|
+
parser = args_reading_opk(parser)
|
|
22
|
+
parser = args_writing_opk(parser)
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
# Process to read data
|
|
27
|
+
work = process_args_read_opk(args)
|
|
28
|
+
# Process to write opk
|
|
29
|
+
process_args_write_opk(args, work)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
opk_to_opk()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to convert opk file to Rpc
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position, line-too-long
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_format.p_write_rpc import args_writing_rpc, process_args_write_rpc # noqa: E402, E501
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def opk_to_rpc():
|
|
15
|
+
"""
|
|
16
|
+
Converts an OPK file into an RPC file.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Photogrammetric site conversion'
|
|
19
|
+
' and manipulation software opk to rpc.')
|
|
20
|
+
# Args for implement opk to rpc
|
|
21
|
+
parser = args_reading_opk(parser)
|
|
22
|
+
parser = args_writing_rpc(parser)
|
|
23
|
+
|
|
24
|
+
args = parser.parse_args()
|
|
25
|
+
|
|
26
|
+
# Process to read data
|
|
27
|
+
work = process_args_read_opk(args)
|
|
28
|
+
# Process to write rpc
|
|
29
|
+
process_args_write_rpc(args, work)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == "__main__":
|
|
33
|
+
opk_to_rpc()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to calculate image coodinate of the ground point.
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_add_data.p_add_shot import args_add_shot, process_add_shot # noqa: E402
|
|
11
|
+
from borea.process.p_add_data.p_pt2d import args_add_pt2d, process_image_world # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def pt_image_to_world():
|
|
15
|
+
"""
|
|
16
|
+
Converts a image point into an terrain point.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Calculate image coodinate of the ground point.')
|
|
19
|
+
# Args for implement pt image to world
|
|
20
|
+
parser = args_add_shot(parser)
|
|
21
|
+
parser = args_add_pt2d(parser)
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
|
|
25
|
+
# Process to read data
|
|
26
|
+
work = process_add_shot(args)
|
|
27
|
+
# Process to transform image to world
|
|
28
|
+
process_image_world(args, work)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
pt_image_to_world()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to calculate world coodinate of the image point.
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_add_data.p_add_shot import args_add_shot, process_add_shot # noqa: E402
|
|
11
|
+
from borea.process.p_add_data.p_pt3d import args_add_pt3d, process_world_image # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def pt_world_to_image():
|
|
15
|
+
"""
|
|
16
|
+
Converts a terrain point into an image point.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Calculate world coodinate of the image point.')
|
|
19
|
+
# Args for implement pt world to image
|
|
20
|
+
parser = args_add_shot(parser)
|
|
21
|
+
parser = args_add_pt3d(parser)
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
|
|
25
|
+
# Process to read data
|
|
26
|
+
work = process_add_shot(args)
|
|
27
|
+
# Process to transform world to image
|
|
28
|
+
process_world_image(args, work)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
pt_world_to_image()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to calculate world coodinate with opk.
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_func.p_image_world import args_image_world, process_image_world # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ptfile_image_to_world():
|
|
15
|
+
"""
|
|
16
|
+
Converts a image point file into an terrain point file.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Calculate ground coodinate of the image point.')
|
|
19
|
+
# Args for implement ptfile image to world
|
|
20
|
+
parser = args_reading_opk(parser)
|
|
21
|
+
parser = args_image_world(parser)
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
|
|
25
|
+
# Process to read data
|
|
26
|
+
work = process_args_read_opk(args)
|
|
27
|
+
# Process to transform image to world
|
|
28
|
+
process_image_world(args, work)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
ptfile_image_to_world()
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to calculate image coodinate with opk.
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_format.p_read_opk import args_reading_opk, process_args_read_opk # noqa: E402
|
|
11
|
+
from borea.process.p_func.p_world_image import args_world_image, process_world_image # noqa: E402
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def ptfile_world_to_image():
|
|
15
|
+
"""
|
|
16
|
+
Converts a terrain point file into an image point file.
|
|
17
|
+
"""
|
|
18
|
+
parser = argparse.ArgumentParser(description='Calculate image coodinate of the ground point.')
|
|
19
|
+
# Args for implement ptfile world to image
|
|
20
|
+
parser = args_reading_opk(parser)
|
|
21
|
+
parser = args_world_image(parser)
|
|
22
|
+
|
|
23
|
+
args = parser.parse_args()
|
|
24
|
+
|
|
25
|
+
# Process to read data
|
|
26
|
+
work = process_args_read_opk(args)
|
|
27
|
+
# Process to transform world to image
|
|
28
|
+
process_world_image(args, work)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
if __name__ == "__main__":
|
|
32
|
+
ptfile_world_to_image()
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Main to calculate 6 externals parameters of shots and save in opk file.
|
|
3
|
+
"""
|
|
4
|
+
# pylint: disable=import-error, wrong-import-position, line-too-long
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
sys.path.append(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))
|
|
10
|
+
from borea.process.p_func.p_spaceresection import args_space_resection, process_space_resection # noqa: E402, E501
|
|
11
|
+
from borea.process.p_format.p_write_opk import args_writing_opk, process_args_write_opk # noqa: E402, E501
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def spaceresection_opk():
|
|
15
|
+
"""
|
|
16
|
+
Permet d'obtenir un fichier OPK à partir de point de liaison sur les images et de leur
|
|
17
|
+
coordonnées terrain (calcule les 6 paramètres externes pour chaque image qui dispose d'un point)
|
|
18
|
+
"""
|
|
19
|
+
parser = argparse.ArgumentParser(description='Space resection of point file image and world'
|
|
20
|
+
' to calculate 6 externals parameters of shots')
|
|
21
|
+
# Args for implement space resection( opk
|
|
22
|
+
parser = args_space_resection(parser)
|
|
23
|
+
parser = args_writing_opk(parser)
|
|
24
|
+
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
# Process to read data
|
|
28
|
+
work = process_space_resection(args)
|
|
29
|
+
# Process to space resection
|
|
30
|
+
process_args_write_opk(args, work)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == "__main__":
|
|
34
|
+
spaceresection_opk()
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 Institut National de l'Information Géographique et Forestière
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ign-borea
|
|
3
|
+
Version: 0.1.5
|
|
4
|
+
Summary: A package to manipulate orientation files
|
|
5
|
+
Author-email: Antoine Cornu <antoine.cornu@ign.fr>, Nicolas Laurain <nicolas.laurain@ign.fr>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) 2024 Institut National de l'Information Géographique et Forestière
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Github, https://github.com/ACornuIGN/Pink_Lady
|
|
29
|
+
Keywords: ign-borea,borea,aero,ign
|
|
30
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
31
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
32
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
33
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
34
|
+
Classifier: Operating System :: Unix
|
|
35
|
+
Classifier: Operating System :: MacOS
|
|
36
|
+
Requires-Python: >=3.9
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
39
|
+
Requires-Dist: numpy <=1.26.4
|
|
40
|
+
Requires-Dist: dataclasses
|
|
41
|
+
Requires-Dist: pyproj
|
|
42
|
+
Requires-Dist: scipy
|
|
43
|
+
Requires-Dist: pandas
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: numpy <=1.26.4 ; extra == 'dev'
|
|
46
|
+
Requires-Dist: pylint ; extra == 'dev'
|
|
47
|
+
Requires-Dist: pytest ; extra == 'dev'
|
|
48
|
+
Requires-Dist: pytest-shutil ; extra == 'dev'
|
|
49
|
+
Requires-Dist: flake8 ; extra == 'dev'
|
|
50
|
+
Requires-Dist: sphinx ; extra == 'dev'
|
|
51
|
+
Requires-Dist: furo ; extra == 'dev'
|
|
52
|
+
Requires-Dist: dataclasses ; extra == 'dev'
|
|
53
|
+
Requires-Dist: pyproj ; extra == 'dev'
|
|
54
|
+
Requires-Dist: scipy ; extra == 'dev'
|
|
55
|
+
Requires-Dist: pandas ; extra == 'dev'
|
|
56
|
+
Requires-Dist: build ; extra == 'dev'
|
|
57
|
+
Requires-Dist: twine ; extra == 'dev'
|
|
58
|
+
|
|
59
|
+
[](https://www.ign.fr/) [](
|
|
60
|
+
https://pypi.org/project/ign-borea/)
|
|
61
|
+
|
|
62
|
+
Borea is an open-source python package tools-box photogrammetric conversion format and transformation coordinate of image and terrain.
|
|
63
|
+
Why Borea? **B** for Box and **orea** is a back slang of aero.
|
|
64
|
+
|
|
65
|
+
## Installation
|
|
66
|
+
|
|
67
|
+
#### Pip
|
|
68
|
+
|
|
69
|
+
Due to different dependency used installation of the library require `GDAL>=3.3.2`, which is not included in the dependency.
|
|
70
|
+
```
|
|
71
|
+
pip install ign-borea
|
|
72
|
+
```
|
|
73
|
+
For GDAL installation you need `libgdal-dev`:
|
|
74
|
+
```
|
|
75
|
+
sudo apt-get install libgdal-dev
|
|
76
|
+
```
|
|
77
|
+
Please note that the `GDAL` version depends on the `libgdal-dev` version.
|
|
78
|
+
```
|
|
79
|
+
apt-cache show libgdal-dev
|
|
80
|
+
# or if you are ogr
|
|
81
|
+
ogrinfo --version
|
|
82
|
+
```
|
|
83
|
+
```
|
|
84
|
+
pip install GDAL==<GDAL VERSION FROM OGRINFO>
|
|
85
|
+
```
|
|
86
|
+
You can find more information on [mothergeo-py](https://mothergeo-py.readthedocs.io/en/latest/development/how-to/gdal-ubuntu-pkg.html) if you have problems installing GDAL.
|
|
87
|
+
|
|
88
|
+
#### In the QGIS environment
|
|
89
|
+
|
|
90
|
+
View the doc on [borea github docs/installation/In_QGIS.md](https://github.com/IGNF/Borea/tree/main/docs/installation/In_QGIS.md).
|
|
91
|
+
|
|
92
|
+
## Tools
|
|
93
|
+
|
|
94
|
+
Some tools are already implemented in the library:
|
|
95
|
+
* Conversion OPK to OPK: `opk-to-opk -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_opk_to_opk.md) (OPK = Omega Phi Kappa)
|
|
96
|
+
* Control OPK file: `opk-control -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_opk_control.md)
|
|
97
|
+
* Conversion OPK to RPC: `opk-to-rpc -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_opk_to_rpc.md)
|
|
98
|
+
* Conversion OPK to Conl: `opk-to-conl -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_opk_to_conl.md) (Conl = light conical file, IGN France format)
|
|
99
|
+
* Transforms coordinates terrain from image: `pt-image-to-world -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_pt_image_to_world.md)
|
|
100
|
+
* Transforms coordinates image from terrain: `pt-world-to-image -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_pt_world_to_image.md)
|
|
101
|
+
* Transforms coordinates file terrain from image: `ptfile-image-to-world -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_ptfile_image_to_world.md)
|
|
102
|
+
* Transforms coordinates file image from terrain: `ptfile-world-to-image -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_ptfile_world_to_image.md)
|
|
103
|
+
* Calculates opk by space resection: `spaceresection-opk -h` [doc](https://github.com/IGNF/Borea/tree/main/borea_tools/docs_tools/README_spaceresection_opk.md)
|
|
104
|
+
|
|
105
|
+
## Read data and instantiate worksite
|
|
106
|
+
|
|
107
|
+
Creation of a worksite object from a worksite file (.opk) to be read by `reader_orientation(pathfile, arg_dict)` with `from borea.reader.orientation.manage_reader import reader_orientation`.
|
|
108
|
+
`arg_dict` is a dictionary for different args:
|
|
109
|
+
* `"interval":[first_line, last_line]` is an list of int that specifies the number of lines you want to read. `first_line` allows you to skip the file header, which must not be taken into account when reading the file, as specified in the `header` variable. If `first_line = None` skips everything up to `last_line`, if `lastline = None` skips everything from `first_line` to the end, and if both are None reads the entire file.
|
|
110
|
+
* `"header":header` described in the section above, is a list of str e.g. `['N', 'X', 'Y', 'Z', 'O', 'P', 'K', 'C'] = list("NXYZOPKC")`, detail of letter at section [header of file opk](#header-of-file-opk) below.
|
|
111
|
+
* `"unit_angle": "degree"` degree or radian.
|
|
112
|
+
* `"linear_alteration": True` boolean saying True if z shots are corrected by linear alteration.
|
|
113
|
+
* `"order_axe: "opk"` string to define the order of angle to calculate rotation matrix.
|
|
114
|
+
|
|
115
|
+
Once the object has been created, you can add other data to it:
|
|
116
|
+
|
|
117
|
+
* Setup the projection of the worksite `work.set_proj(epsg, path_geoid)`, with:
|
|
118
|
+
* `epsg` the code epsg e.g. 2154
|
|
119
|
+
* `path_geoid` path to the file pyproj GeoTIFF of geoid
|
|
120
|
+
|
|
121
|
+
* The camera with `read_camera([filepath], worksite)` in `from borea.reader.reader_camera import read_camera`, this function only reads txt and xml files referencing camera data, and can take several camera files if there are several.
|
|
122
|
+
|
|
123
|
+
* Link points with `read_file_pt(filepath, header, type_point, worksite)` in `from borea.reader.reader_point import read_file_pt`. this function reads all .txt, .mes, .app and other file types, as long as the data structure in the file is column-based and delimited by spaces. The first args is the file path of one file. The second is the column type in the file detail of letter at section [header of point file](#header-of-point-file) below. The third is the type of point **'co_points'** for connecting points, **'gcp2d'** for coordinnate of gcp in images and **'gcp3d'** for gcp coordinate in the ground. And the last args is the worksite where data will be save.
|
|
124
|
+
|
|
125
|
+
* Add Dtm to your worksite `work.set_dtm(path_dtm, type_dtm)`, It converts z data between gcp and acquisition position if these are not in the same unit (one in altitude and one in height). `type_dtm` is the unit of the dtm 'altitude' or 'height'.
|
|
126
|
+
|
|
127
|
+
Examples in section [examples](#examples) below.
|
|
128
|
+
|
|
129
|
+
## Different process
|
|
130
|
+
|
|
131
|
+
* Set different parameters for shots (projection system of shot and z_nadir), mandatory if data is to be processed afterwards. `work.set_param_shot()`.
|
|
132
|
+
|
|
133
|
+
* Can calculate the position of image points in world with `ImageWorldWork(worksite).manage_image_world(type_point, type_process, type_control)` in `from borea.transform_world_image.transform_worksite.image_world_work import ImageWorldWork`.
|
|
134
|
+
* `type_point` is the type of point you want to calcule `co_points` or `gcp2d`.
|
|
135
|
+
* `type_process` is the type of process you want to use intersection with key `inter` or least square methode with key `square`.
|
|
136
|
+
* `type_control` egal None by default, is used if the type_point = gcp2d and if you want just one type code point, else None to process on all point.
|
|
137
|
+
|
|
138
|
+
The result can be found in `worksite.co_pts_world['name_point']` when type_point = co_points or `worksite.img_pts_world['name_point]` when type_point = gcp2d.
|
|
139
|
+
|
|
140
|
+
* Can calculate the position of terrain points in images with `WorldImageWork(work).calculate_world_to_image(type_control)` in `from borea.transform_world_image.transform_worksite.world_image_work import WorldImageWork`, with `type_control` egal None by default, is used if the type_point = gcp2d and if you want just one type code point, else None to process on all point. . The result can be found in `worksite.shots['name_shot'].gcps['name_gcp']` for each image and each gcps.
|
|
141
|
+
|
|
142
|
+
* Can calculate spatial resection for each shot in worksite with `SpaceResection(work).space_resection_on_worksite(add_pixel = (0,0))` in `from borea.transform_world_image.transform_worksite.space_resection import SpaceResection`. `add_pixel` is used to add a mainiation to the position of the points to modify the shot's 6 external parameters for data conversion.
|
|
143
|
+
|
|
144
|
+
* Can calculate spatial resection in poitn of shot for creating worksite with `SpaceResection(work).space_resection_to_worksite(pt2d, pt3d, pinit)` in `from borea.transform_world_image.transform_worksite.space_resection import SpaceResection`.
|
|
145
|
+
The DataFrame **pt2d** is a table with 4 column and n line. The id of column must be:
|
|
146
|
+
* `id_pt`: the id of the point
|
|
147
|
+
* `id_shot`: the name of the shot where the point is located
|
|
148
|
+
* `column`: column coordinate in pixel of the point in the image
|
|
149
|
+
* `line`: line coordinate in pixel of the point in the image
|
|
150
|
+
|
|
151
|
+
it can be created with the function `read_file_pt_dataframe(path_file_pt,header_file,"pt2d")`
|
|
152
|
+
The DataFrame **pt3d** is a table with 5 column and n line. The id of column must be:
|
|
153
|
+
* `id_pt`: the id of the point
|
|
154
|
+
* `type`: if point is gcp with type else None
|
|
155
|
+
* `x`: x coordinate in your projection system of the point
|
|
156
|
+
* `y`: y coordinate in your projection system of the point
|
|
157
|
+
* `z`: z coordinate in your projection system of the point
|
|
158
|
+
|
|
159
|
+
it can be created with the function `read_file_pt_dataframe(path_file_pt,header_file,"pt3d")`
|
|
160
|
+
The dictionary **pinit** which give the initialization point X, Y, Z. A point on the worksite with a z at an approximate flying height. The name of the key in the dictionary is `coor_init`.
|
|
161
|
+
Example at the end of explanation of function [file](https://github.com/IGNF/Borea/tree/main/docs/functions/Space_resection.md).
|
|
162
|
+
|
|
163
|
+
* You can calculate some control point statistics to see how accurate your site is `stat = Stat(work, pathreturn, control_type)` to init the object and run for all stat with `stat.main_stat_and_save()`. Make stat on function image to world and world to image, if there are data. And save result on *pathreturn/Stat_{Name_worksite}.txt*.
|
|
164
|
+
|
|
165
|
+
Examples in section [examples](#examples) below.
|
|
166
|
+
|
|
167
|
+
## Write data
|
|
168
|
+
|
|
169
|
+
* Can write worksite object as different format OPK, RPC, Conical for GEOVIEW. The function is `manager_reader(writer, name, pathreturn, args, work)` in `from borea.writer.manage_writer import manager_writer`:
|
|
170
|
+
* `writer` (str), is the type of output `"opk"`, `"rpc"`, `"con"`.
|
|
171
|
+
* `name` (str), name of file to save, just to save in opk, for other format this args isn't read.
|
|
172
|
+
* `pathreturn` (str), path of folder where you want to save data.
|
|
173
|
+
* `args` (dict), Dictionary with different args for the format to save, detail at setion [args for writing file](#args-for-writing-file) below.
|
|
174
|
+
* `work` (Worksite), the worksite to save.
|
|
175
|
+
|
|
176
|
+
Examples in section [examples](#examples) below.
|
|
177
|
+
|
|
178
|
+
## Examples
|
|
179
|
+
|
|
180
|
+
All examples are in [borea github ./examples/](https://github.com/IGNF/Borea/tree/main/examples):
|
|
181
|
+
* For build main class Worksite with file **eg_build_worksite_by_file.py** and with data **eg_build_worksite_by_data.py**.
|
|
182
|
+
* To make transformation image to world **eg_image_to_world.py**.
|
|
183
|
+
* To make transformation world to image **eg_world_to_image.py**.
|
|
184
|
+
* To make space resection on point to determine worksite **eg_space_resection.py**.
|
|
185
|
+
* To convert format opk to an other format opk rpc con **eg_opk_to_format.py**.
|
|
186
|
+
|
|
187
|
+
Examples of the different formats of file can be found in [borea github ./dataset/](https://github.com/IGNF/Borea/tree/main/dataset):
|
|
188
|
+
* An opk file **23FD1305_alt_test.OPK** with z unit is altitude.
|
|
189
|
+
* Cameras filesformat **Camera1.txt** and **Camera2.txt**.
|
|
190
|
+
* Geotiff of the French geoid for pyproj fr_ign_RAF20.tif** detail at section [info projection](#info-projection) below.
|
|
191
|
+
* Crops geotiff of the French DTM **MNT_France_25m_h_crop.tif** in height unit.
|
|
192
|
+
* Ground Control Point (GCP) in terrian **GCP_test.app** unit z in height.
|
|
193
|
+
* Ground Control Point (GCP) in image **terrain_test.mes**.
|
|
194
|
+
* Connecting points in image **liaisons_test.mes**.
|
|
195
|
+
* Image point to transform terrain coordinates to image coordinates to find out in which image the points are located **terrain_test0.mes**.
|
|
196
|
+
|
|
197
|
+
## Detail
|
|
198
|
+
|
|
199
|
+
### Header of file opk
|
|
200
|
+
`header` is used to describe the format of the opk file read. It provides information on what's in each column, and gives the data unit for Z and angles.
|
|
201
|
+
Type is:
|
|
202
|
+
| Symbol | Details |
|
|
203
|
+
| :----: | :------ |
|
|
204
|
+
| S | to ignore the column |
|
|
205
|
+
| N | name of shot |
|
|
206
|
+
| X | coordinate x of the shot position |
|
|
207
|
+
| Y | coordinate y of the shot position |
|
|
208
|
+
| Z | coordinate z altitude of the shot position |
|
|
209
|
+
| H | coordinate z height of the shot position |
|
|
210
|
+
| O | omega rotation angle |
|
|
211
|
+
| P | phi rotation angle |
|
|
212
|
+
| K | kappa rotation angle |
|
|
213
|
+
| C | name of the camera |
|
|
214
|
+
|
|
215
|
+
### Header of point file
|
|
216
|
+
|
|
217
|
+
`header` is used to describe the format of the point file read. It provides information on what's in each column.
|
|
218
|
+
Type is:
|
|
219
|
+
| Symbol | Details |
|
|
220
|
+
| :----: | :------ |
|
|
221
|
+
| S | to ignore the column |
|
|
222
|
+
| P | name of the point |
|
|
223
|
+
| N | name of shot |
|
|
224
|
+
| T | type of point |
|
|
225
|
+
| X | coordinate x of the shot position |
|
|
226
|
+
| Y | coordinate y of the shot position |
|
|
227
|
+
| Z | coordinate z altitude of the shot position |
|
|
228
|
+
|
|
229
|
+
### Camera file format
|
|
230
|
+
|
|
231
|
+
The camera file is a txt file, containing 6 pieces of information about the camera : its **name** (str), **ppax** (float), **ppay** (float), **focal** (float), image size: **width** (int) and **height** (int) in pixels.
|
|
232
|
+
**Ppax** and **ppay** are the main points of image deformation in x and y directions.
|
|
233
|
+
Each line of the file corresponds to a piece of information, starting with the **type = info**.
|
|
234
|
+
```
|
|
235
|
+
name = UCE-M3-f120-s06
|
|
236
|
+
ppax = 13210.00
|
|
237
|
+
ppay = 8502.00
|
|
238
|
+
focal = 30975.00
|
|
239
|
+
width = 26460
|
|
240
|
+
height = 17004
|
|
241
|
+
```
|
|
242
|
+
Only these 7 pieces of information will be read. You can add comments with a # in the first element of the line or other type = info, but they will not be read by the tool, unless the attribute has been added to the Camera class in *borea/datastruct/camera.py*.
|
|
243
|
+
An example file can be found in repository *./dataset/Camera1.txt*.
|
|
244
|
+
No camera-related distortion is taken into account (distortion-free camera).
|
|
245
|
+
|
|
246
|
+
### Info projection
|
|
247
|
+
|
|
248
|
+
This library can transform and process 3D data with a z in altitude or height. This is done by the pyproj library, which needs the geoid at site level to change units.
|
|
249
|
+
|
|
250
|
+
The varaible in example for adding a geoid is path_geoid, a list which contains paths of geoids, where you can enter the paths to the various geoids. If the file is stored in pyproj's native folder (pyproj.datadir.get_data_dir(), *usr/share/proj* or *env_name_folder/lib/python3.10/site-packages/pyproj/proj_dir/share/proj*) the file name is sufficient pyproj will find it on its own.
|
|
251
|
+
Geoids file can be found on pyproj's github (https://github.com/OSGeo/PROJ-data).
|
|
252
|
+
|
|
253
|
+
### Args for writing file
|
|
254
|
+
|
|
255
|
+
#### OPK
|
|
256
|
+
|
|
257
|
+
There are 4 keys in the dictionary:
|
|
258
|
+
* "order_axe" (str): **Order of rotation matrix axes**.
|
|
259
|
+
* "header" (list): **List of column type** file (same to read opk).
|
|
260
|
+
* "unit_angle" (str): Unit of angle **'degree' or 'radian'**.
|
|
261
|
+
* "linear_alteration" (bool): **True** if data corrected by linear alteration or else **False**.
|
|
262
|
+
|
|
263
|
+
#### RPC
|
|
264
|
+
|
|
265
|
+
There are 3 keys in the dictionary:
|
|
266
|
+
* "size_grid" (int): **size of the grip** to calcule rpc.
|
|
267
|
+
* "order" (int): order of the polynome of the rpc. **[1, 2, 3]**
|
|
268
|
+
* "fact_rpc" (float): rpc factor for world coordinate when not src, we recommend **None**.
|
|
269
|
+
|
|
270
|
+
#### CON
|
|
271
|
+
|
|
272
|
+
There is no need for an additional argument, you can set **None** to the argument.
|
|
273
|
+
|
|
274
|
+
 
|