mapillary-tools 0.13.3__py3-none-any.whl → 0.14.0__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.
- mapillary_tools/__init__.py +1 -1
- mapillary_tools/api_v4.py +198 -55
- mapillary_tools/authenticate.py +326 -64
- mapillary_tools/blackvue_parser.py +195 -0
- mapillary_tools/camm/camm_builder.py +55 -97
- mapillary_tools/camm/camm_parser.py +429 -181
- mapillary_tools/commands/__main__.py +10 -6
- mapillary_tools/commands/authenticate.py +8 -1
- mapillary_tools/commands/process.py +27 -51
- mapillary_tools/commands/process_and_upload.py +18 -5
- mapillary_tools/commands/sample_video.py +2 -3
- mapillary_tools/commands/upload.py +44 -13
- mapillary_tools/commands/video_process_and_upload.py +19 -5
- mapillary_tools/config.py +65 -26
- mapillary_tools/constants.py +141 -18
- mapillary_tools/exceptions.py +37 -34
- mapillary_tools/exif_read.py +221 -116
- mapillary_tools/exif_write.py +10 -8
- mapillary_tools/exiftool_read.py +33 -42
- mapillary_tools/exiftool_read_video.py +97 -47
- mapillary_tools/exiftool_runner.py +57 -0
- mapillary_tools/ffmpeg.py +417 -242
- mapillary_tools/geo.py +158 -118
- mapillary_tools/geotag/__init__.py +0 -1
- mapillary_tools/geotag/base.py +147 -0
- mapillary_tools/geotag/factory.py +307 -0
- mapillary_tools/geotag/geotag_images_from_exif.py +14 -131
- mapillary_tools/geotag/geotag_images_from_exiftool.py +136 -85
- mapillary_tools/geotag/geotag_images_from_gpx.py +60 -124
- mapillary_tools/geotag/geotag_images_from_gpx_file.py +13 -126
- mapillary_tools/geotag/geotag_images_from_nmea_file.py +4 -5
- mapillary_tools/geotag/geotag_images_from_video.py +88 -51
- mapillary_tools/geotag/geotag_videos_from_exiftool.py +123 -0
- mapillary_tools/geotag/geotag_videos_from_gpx.py +52 -0
- mapillary_tools/geotag/geotag_videos_from_video.py +20 -185
- mapillary_tools/geotag/image_extractors/base.py +18 -0
- mapillary_tools/geotag/image_extractors/exif.py +60 -0
- mapillary_tools/geotag/image_extractors/exiftool.py +18 -0
- mapillary_tools/geotag/options.py +182 -0
- mapillary_tools/geotag/utils.py +52 -16
- mapillary_tools/geotag/video_extractors/base.py +18 -0
- mapillary_tools/geotag/video_extractors/exiftool.py +70 -0
- mapillary_tools/geotag/video_extractors/gpx.py +116 -0
- mapillary_tools/geotag/video_extractors/native.py +160 -0
- mapillary_tools/{geotag → gpmf}/gpmf_parser.py +205 -182
- mapillary_tools/{geotag → gpmf}/gps_filter.py +5 -3
- mapillary_tools/history.py +134 -20
- mapillary_tools/mp4/construct_mp4_parser.py +17 -10
- mapillary_tools/mp4/io_utils.py +0 -1
- mapillary_tools/mp4/mp4_sample_parser.py +36 -28
- mapillary_tools/mp4/simple_mp4_builder.py +10 -9
- mapillary_tools/mp4/simple_mp4_parser.py +13 -22
- mapillary_tools/process_geotag_properties.py +184 -414
- mapillary_tools/process_sequence_properties.py +594 -225
- mapillary_tools/sample_video.py +20 -26
- mapillary_tools/serializer/description.py +587 -0
- mapillary_tools/serializer/gpx.py +132 -0
- mapillary_tools/telemetry.py +26 -13
- mapillary_tools/types.py +98 -611
- mapillary_tools/upload.py +411 -387
- mapillary_tools/upload_api_v4.py +167 -142
- mapillary_tools/uploader.py +804 -284
- mapillary_tools/utils.py +49 -18
- {mapillary_tools-0.13.3.dist-info → mapillary_tools-0.14.0.dist-info}/METADATA +93 -35
- mapillary_tools-0.14.0.dist-info/RECORD +75 -0
- {mapillary_tools-0.13.3.dist-info → mapillary_tools-0.14.0.dist-info}/WHEEL +1 -1
- mapillary_tools/geotag/blackvue_parser.py +0 -118
- mapillary_tools/geotag/geotag_from_generic.py +0 -22
- mapillary_tools/geotag/geotag_images_from_exiftool_both_image_and_video.py +0 -93
- mapillary_tools/geotag/geotag_videos_from_exiftool_video.py +0 -145
- mapillary_tools/video_data_extraction/cli_options.py +0 -22
- mapillary_tools/video_data_extraction/extract_video_data.py +0 -176
- mapillary_tools/video_data_extraction/extractors/base_parser.py +0 -75
- mapillary_tools/video_data_extraction/extractors/blackvue_parser.py +0 -34
- mapillary_tools/video_data_extraction/extractors/camm_parser.py +0 -38
- mapillary_tools/video_data_extraction/extractors/exiftool_runtime_parser.py +0 -71
- mapillary_tools/video_data_extraction/extractors/exiftool_xml_parser.py +0 -53
- mapillary_tools/video_data_extraction/extractors/generic_video_parser.py +0 -52
- mapillary_tools/video_data_extraction/extractors/gopro_parser.py +0 -43
- mapillary_tools/video_data_extraction/extractors/gpx_parser.py +0 -108
- mapillary_tools/video_data_extraction/extractors/nmea_parser.py +0 -24
- mapillary_tools/video_data_extraction/video_data_parser_factory.py +0 -39
- mapillary_tools-0.13.3.dist-info/RECORD +0 -75
- /mapillary_tools/{geotag → gpmf}/gpmf_gps_filter.py +0 -0
- {mapillary_tools-0.13.3.dist-info → mapillary_tools-0.14.0.dist-info}/entry_points.txt +0 -0
- {mapillary_tools-0.13.3.dist-info → mapillary_tools-0.14.0.dist-info/licenses}/LICENSE +0 -0
- {mapillary_tools-0.13.3.dist-info → mapillary_tools-0.14.0.dist-info}/top_level.txt +0 -0
mapillary_tools/constants.py
CHANGED
|
@@ -1,19 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import functools
|
|
1
4
|
import os
|
|
2
|
-
import
|
|
5
|
+
import tempfile
|
|
3
6
|
|
|
4
7
|
import appdirs
|
|
5
8
|
|
|
6
9
|
_ENV_PREFIX = "MAPILLARY_TOOLS_"
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
|
|
12
|
+
def _yes_or_no(val: str) -> bool:
|
|
13
|
+
return val.strip().upper() in ["1", "TRUE", "YES"]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _parse_scaled_integers(
|
|
17
|
+
value: str, scale: dict[str, int] | None = None
|
|
18
|
+
) -> int | None:
|
|
19
|
+
"""
|
|
20
|
+
>>> scale = {"": 1, "b": 1, "K": 1024, "M": 1024 * 1024, "G": 1024 * 1024 * 1024}
|
|
21
|
+
>>> _parse_scaled_integers("0", scale=scale)
|
|
22
|
+
0
|
|
23
|
+
>>> _parse_scaled_integers("10", scale=scale)
|
|
24
|
+
10
|
|
25
|
+
>>> _parse_scaled_integers("100B", scale=scale)
|
|
26
|
+
100
|
|
27
|
+
>>> _parse_scaled_integers("100k", scale=scale)
|
|
28
|
+
102400
|
|
29
|
+
>>> _parse_scaled_integers("100t", scale=scale)
|
|
30
|
+
Traceback (most recent call last):
|
|
31
|
+
ValueError: Expect valid integer ends with , b, K, M, G, but got 100T
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
if scale is None:
|
|
35
|
+
scale = {"": 1}
|
|
36
|
+
|
|
37
|
+
value = value.strip().upper()
|
|
38
|
+
|
|
39
|
+
if value in ["INF", "INFINITY"]:
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
try:
|
|
43
|
+
for k, v in scale.items():
|
|
44
|
+
k = k.upper()
|
|
45
|
+
if k and value.endswith(k):
|
|
46
|
+
return int(value[: -len(k)]) * v
|
|
47
|
+
|
|
48
|
+
if "" in scale:
|
|
49
|
+
return int(value) * scale[""]
|
|
50
|
+
except ValueError:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Expect valid integer ends with {', '.join(scale.keys())}, but got {value}"
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_parse_pixels = functools.partial(
|
|
59
|
+
_parse_scaled_integers,
|
|
60
|
+
scale={
|
|
61
|
+
"": 1,
|
|
62
|
+
"K": 1000,
|
|
63
|
+
"M": 1000 * 1000,
|
|
64
|
+
"MP": 1000 * 1000,
|
|
65
|
+
"G": 1000 * 1000 * 1000,
|
|
66
|
+
"GP": 1000 * 1000 * 1000,
|
|
67
|
+
},
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
_parse_filesize = functools.partial(
|
|
71
|
+
_parse_scaled_integers,
|
|
72
|
+
scale={"B": 1, "K": 1024, "M": 1024 * 1024, "G": 1024 * 1024 * 1024},
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
###################
|
|
76
|
+
##### GENERAL #####
|
|
77
|
+
###################
|
|
78
|
+
USER_DATA_DIR = appdirs.user_data_dir(appname="mapillary_tools", appauthor="Mapillary")
|
|
79
|
+
PROMPT_DISABLED: bool = _yes_or_no(os.getenv(_ENV_PREFIX + "PROMPT_DISABLED", "NO"))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
############################
|
|
83
|
+
##### VIDEO PROCESSING #####
|
|
84
|
+
############################
|
|
85
|
+
# In seconds
|
|
15
86
|
VIDEO_SAMPLE_INTERVAL = float(os.getenv(_ENV_PREFIX + "VIDEO_SAMPLE_INTERVAL", -1))
|
|
16
|
-
#
|
|
87
|
+
# In meters
|
|
17
88
|
VIDEO_SAMPLE_DISTANCE = float(os.getenv(_ENV_PREFIX + "VIDEO_SAMPLE_DISTANCE", 3))
|
|
18
89
|
VIDEO_DURATION_RATIO = float(os.getenv(_ENV_PREFIX + "VIDEO_DURATION_RATIO", 1))
|
|
19
90
|
FFPROBE_PATH: str = os.getenv(_ENV_PREFIX + "FFPROBE_PATH", "ffprobe")
|
|
@@ -25,26 +96,78 @@ IMAGE_DESCRIPTION_FILENAME = os.getenv(
|
|
|
25
96
|
SAMPLED_VIDEO_FRAMES_FILENAME = os.getenv(
|
|
26
97
|
_ENV_PREFIX + "SAMPLED_VIDEO_FRAMES_FILENAME", "mapillary_sampled_video_frames"
|
|
27
98
|
)
|
|
28
|
-
USER_DATA_DIR = appdirs.user_data_dir(appname="mapillary_tools", appauthor="Mapillary")
|
|
29
|
-
UPLOAD_CHUNK_SIZE_MB = float(os.getenv(_ENV_PREFIX + "UPLOAD_CHUNK_SIZE_MB", 16))
|
|
30
|
-
|
|
31
99
|
# DoP value, the lower the better
|
|
32
100
|
# See https://github.com/gopro/gpmf-parser#hero5-black-with-gps-enabled-adds
|
|
33
101
|
# It is used to filter out noisy points
|
|
34
102
|
GOPRO_MAX_DOP100 = int(os.getenv(_ENV_PREFIX + "GOPRO_MAX_DOP100", 1000))
|
|
35
103
|
# Within the GPS stream: 0 - no lock, 2 or 3 - 2D or 3D Lock
|
|
36
|
-
GOPRO_GPS_FIXES:
|
|
104
|
+
GOPRO_GPS_FIXES: set[int] = set(
|
|
37
105
|
int(fix) for fix in os.getenv(_ENV_PREFIX + "GOPRO_GPS_FIXES", "2,3").split(",")
|
|
38
106
|
)
|
|
39
|
-
MAX_UPLOAD_RETRIES: int = int(os.getenv(_ENV_PREFIX + "MAX_UPLOAD_RETRIES", 200))
|
|
40
|
-
|
|
41
107
|
# GPS precision, in meters, is used to filter outliers
|
|
42
108
|
GOPRO_GPS_PRECISION = float(os.getenv(_ENV_PREFIX + "GOPRO_GPS_PRECISION", 15))
|
|
109
|
+
MAPILLARY__EXPERIMENTAL_ENABLE_IMU: bool = _yes_or_no(
|
|
110
|
+
os.getenv("MAPILLARY__EXPERIMENTAL_ENABLE_IMU", "NO")
|
|
111
|
+
)
|
|
112
|
+
|
|
43
113
|
|
|
114
|
+
#################################
|
|
115
|
+
###### SEQUENCE PROCESSING ######
|
|
116
|
+
#################################
|
|
117
|
+
# In meters
|
|
118
|
+
CUTOFF_DISTANCE = float(os.getenv(_ENV_PREFIX + "CUTOFF_DISTANCE", 600))
|
|
119
|
+
# In seconds
|
|
120
|
+
CUTOFF_TIME = float(os.getenv(_ENV_PREFIX + "CUTOFF_TIME", 60))
|
|
121
|
+
DUPLICATE_DISTANCE = float(os.getenv(_ENV_PREFIX + "DUPLICATE_DISTANCE", 0.1))
|
|
122
|
+
DUPLICATE_ANGLE = float(os.getenv(_ENV_PREFIX + "DUPLICATE_ANGLE", 5))
|
|
123
|
+
MAX_CAPTURE_SPEED_KMH = float(
|
|
124
|
+
os.getenv(_ENV_PREFIX + "MAX_CAPTURE_SPEED_KMH", 400)
|
|
125
|
+
) # 400 KM/h
|
|
44
126
|
# WARNING: Changing the following envvars might result in failed uploads
|
|
45
127
|
# Max number of images per sequence
|
|
46
|
-
MAX_SEQUENCE_LENGTH
|
|
128
|
+
MAX_SEQUENCE_LENGTH: int | None = _parse_scaled_integers(
|
|
129
|
+
os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_LENGTH", "1000")
|
|
130
|
+
)
|
|
47
131
|
# Max file size per sequence (sum of image filesizes in the sequence)
|
|
48
|
-
MAX_SEQUENCE_FILESIZE:
|
|
132
|
+
MAX_SEQUENCE_FILESIZE: int | None = _parse_filesize(
|
|
133
|
+
os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_FILESIZE", "110G")
|
|
134
|
+
)
|
|
49
135
|
# Max number of pixels per sequence (sum of image pixels in the sequence)
|
|
50
|
-
MAX_SEQUENCE_PIXELS:
|
|
136
|
+
MAX_SEQUENCE_PIXELS: int | None = _parse_pixels(
|
|
137
|
+
os.getenv(_ENV_PREFIX + "MAX_SEQUENCE_PIXELS", "6G")
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
##################
|
|
142
|
+
##### UPLOAD #####
|
|
143
|
+
##################
|
|
144
|
+
MAPILLARY_DISABLE_API_LOGGING: bool = _yes_or_no(
|
|
145
|
+
os.getenv("MAPILLARY_DISABLE_API_LOGGING", "NO")
|
|
146
|
+
)
|
|
147
|
+
MAPILLARY_UPLOAD_HISTORY_PATH: str = os.getenv(
|
|
148
|
+
"MAPILLARY_UPLOAD_HISTORY_PATH", os.path.join(USER_DATA_DIR, "upload_history")
|
|
149
|
+
)
|
|
150
|
+
UPLOAD_CACHE_DIR: str = os.getenv(
|
|
151
|
+
_ENV_PREFIX + "UPLOAD_CACHE_DIR",
|
|
152
|
+
os.path.join(tempfile.gettempdir(), "mapillary_tools", "upload_cache"),
|
|
153
|
+
)
|
|
154
|
+
# The minimal upload speed is used to calculate the read timeout to avoid upload hanging:
|
|
155
|
+
# timeout = upload_size / MIN_UPLOAD_SPEED
|
|
156
|
+
MIN_UPLOAD_SPEED: int | None = _parse_filesize(
|
|
157
|
+
os.getenv(_ENV_PREFIX + "MIN_UPLOAD_SPEED", "50K") # 50 KiB/s
|
|
158
|
+
)
|
|
159
|
+
MAX_IMAGE_UPLOAD_WORKERS: int = int(
|
|
160
|
+
os.getenv(_ENV_PREFIX + "MAX_IMAGE_UPLOAD_WORKERS", 64)
|
|
161
|
+
)
|
|
162
|
+
# The chunk size in MB (see chunked transfer encoding https://en.wikipedia.org/wiki/Chunked_transfer_encoding)
|
|
163
|
+
# for uploading data to MLY upload service.
|
|
164
|
+
# Changing this size does not change the number of requests nor affect upload performance,
|
|
165
|
+
# but it affects the responsiveness of the upload progress bar
|
|
166
|
+
UPLOAD_CHUNK_SIZE_MB: float = float(os.getenv(_ENV_PREFIX + "UPLOAD_CHUNK_SIZE_MB", 1))
|
|
167
|
+
MAX_UPLOAD_RETRIES: int = int(os.getenv(_ENV_PREFIX + "MAX_UPLOAD_RETRIES", 200))
|
|
168
|
+
MAPILLARY__ENABLE_UPLOAD_HISTORY_FOR_DRY_RUN: bool = _yes_or_no(
|
|
169
|
+
os.getenv("MAPILLARY__ENABLE_UPLOAD_HISTORY_FOR_DRY_RUN", "NO")
|
|
170
|
+
)
|
|
171
|
+
_AUTH_VERIFICATION_DISABLED: bool = _yes_or_no(
|
|
172
|
+
os.getenv(_ENV_PREFIX + "_AUTH_VERIFICATION_DISABLED", "NO")
|
|
173
|
+
)
|
mapillary_tools/exceptions.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import typing as T
|
|
2
4
|
|
|
3
5
|
|
|
@@ -5,6 +7,18 @@ class MapillaryUserError(Exception):
|
|
|
5
7
|
exit_code: int
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
class MapillaryProcessError(MapillaryUserError):
|
|
11
|
+
"""
|
|
12
|
+
Base exception for process specific errors
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
exit_code = 6
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class MapillaryDescriptionError(Exception):
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
8
22
|
class MapillaryBadParameterError(MapillaryUserError):
|
|
9
23
|
exit_code = 2
|
|
10
24
|
|
|
@@ -17,47 +31,42 @@ class MapillaryInvalidDescriptionFile(MapillaryUserError):
|
|
|
17
31
|
exit_code = 4
|
|
18
32
|
|
|
19
33
|
|
|
20
|
-
class MapillaryUnknownFileTypeError(MapillaryUserError):
|
|
21
|
-
exit_code = 5
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
class MapillaryProcessError(MapillaryUserError):
|
|
25
|
-
exit_code = 6
|
|
26
|
-
|
|
27
|
-
|
|
28
34
|
class MapillaryVideoError(MapillaryUserError):
|
|
29
35
|
exit_code = 7
|
|
30
36
|
|
|
31
37
|
|
|
32
38
|
class MapillaryFFmpegNotFoundError(MapillaryUserError):
|
|
33
39
|
exit_code = 8
|
|
34
|
-
help = "https://github.com/mapillary/mapillary_tools#video-support"
|
|
35
40
|
|
|
36
41
|
|
|
37
42
|
class MapillaryExiftoolNotFoundError(MapillaryUserError):
|
|
38
43
|
exit_code = 8
|
|
39
44
|
|
|
40
45
|
|
|
41
|
-
class MapillaryDescriptionError
|
|
46
|
+
class MapillaryGeoTaggingError(MapillaryDescriptionError):
|
|
42
47
|
pass
|
|
43
48
|
|
|
44
49
|
|
|
45
|
-
class
|
|
50
|
+
class MapillaryVideoGPSNotFoundError(MapillaryDescriptionError):
|
|
46
51
|
pass
|
|
47
52
|
|
|
48
53
|
|
|
49
|
-
class
|
|
50
|
-
|
|
54
|
+
class MapillaryInvalidVideoError(MapillaryDescriptionError):
|
|
55
|
+
pass
|
|
51
56
|
|
|
52
57
|
|
|
53
|
-
class
|
|
54
|
-
|
|
58
|
+
class MapillaryGPXEmptyError(MapillaryDescriptionError):
|
|
59
|
+
pass
|
|
55
60
|
|
|
56
61
|
|
|
57
62
|
class MapillaryGPSNoiseError(MapillaryDescriptionError):
|
|
58
63
|
pass
|
|
59
64
|
|
|
60
65
|
|
|
66
|
+
class MapillaryStationaryVideoError(MapillaryDescriptionError):
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
|
|
61
70
|
class MapillaryOutsideGPXTrackError(MapillaryDescriptionError):
|
|
62
71
|
def __init__(
|
|
63
72
|
self, message: str, image_time: str, gpx_start_time: str, gpx_end_time: str
|
|
@@ -68,21 +77,13 @@ class MapillaryOutsideGPXTrackError(MapillaryDescriptionError):
|
|
|
68
77
|
self.gpx_end_time = gpx_end_time
|
|
69
78
|
|
|
70
79
|
|
|
71
|
-
class MapillaryStationaryVideoError(MapillaryDescriptionError, MapillaryUserError):
|
|
72
|
-
exit_code = 10
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
class MapillaryInvalidBlackVueVideoError(MapillaryDescriptionError, MapillaryUserError):
|
|
76
|
-
exit_code = 11
|
|
77
|
-
|
|
78
|
-
|
|
79
80
|
class MapillaryDuplicationError(MapillaryDescriptionError):
|
|
80
81
|
def __init__(
|
|
81
82
|
self,
|
|
82
83
|
message: str,
|
|
83
84
|
desc: T.Mapping[str, T.Any],
|
|
84
85
|
distance: float,
|
|
85
|
-
angle_diff:
|
|
86
|
+
angle_diff: float | None,
|
|
86
87
|
) -> None:
|
|
87
88
|
super().__init__(message)
|
|
88
89
|
self.desc = desc
|
|
@@ -90,17 +91,19 @@ class MapillaryDuplicationError(MapillaryDescriptionError):
|
|
|
90
91
|
self.angle_diff = angle_diff
|
|
91
92
|
|
|
92
93
|
|
|
93
|
-
class
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
94
|
+
class MapillaryExifToolXMLNotFoundError(MapillaryDescriptionError):
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class MapillaryFileTooLargeError(MapillaryDescriptionError):
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class MapillaryCaptureSpeedTooFastError(MapillaryDescriptionError):
|
|
103
|
+
pass
|
|
101
104
|
|
|
102
105
|
|
|
103
|
-
class
|
|
106
|
+
class MapillaryNullIslandError(MapillaryDescriptionError):
|
|
104
107
|
pass
|
|
105
108
|
|
|
106
109
|
|
|
@@ -116,5 +119,5 @@ class MapillaryUploadUnauthorizedError(MapillaryUserError):
|
|
|
116
119
|
exit_code = 14
|
|
117
120
|
|
|
118
121
|
|
|
119
|
-
class MapillaryMetadataValidationError(MapillaryUserError
|
|
122
|
+
class MapillaryMetadataValidationError(MapillaryUserError):
|
|
120
123
|
exit_code = 15
|