geopic-tag-reader 1.6.0__tar.gz → 1.8.0__tar.gz

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 (70) hide show
  1. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/CHANGELOG.md +16 -1
  2. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/PKG-INFO +2 -1
  3. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/__init__.py +1 -1
  4. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/reader.py +23 -4
  5. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/sequence.py +87 -33
  6. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/ar/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  7. geopic_tag_reader-1.6.0/geopic_tag_reader/translations/geopic_tag_reader.pot → geopic_tag_reader-1.8.0/geopic_tag_reader/translations/ar/LC_MESSAGES/geopic_tag_reader.po +4 -5
  8. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  9. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.po +3 -3
  10. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  11. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.po +17 -17
  12. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  13. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.po +2 -3
  14. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/geopic_tag_reader.pot +197 -0
  15. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/nl/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  16. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/nl/LC_MESSAGES/geopic_tag_reader.po +13 -14
  17. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/ti/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  18. geopic_tag_reader-1.8.0/geopic_tag_reader/translations/ti/LC_MESSAGES/geopic_tag_reader.po +196 -0
  19. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/pyproject.toml +1 -0
  20. geopic_tag_reader-1.6.0/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  21. geopic_tag_reader-1.6.0/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  22. geopic_tag_reader-1.6.0/geopic_tag_reader/translations/nl/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  23. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/.gitignore +0 -0
  24. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/.gitlab-ci.yml +0 -0
  25. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/.pre-commit-config.yaml +0 -0
  26. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/CODE_OF_CONDUCT.md +0 -0
  27. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/LICENSE +0 -0
  28. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/Makefile +0 -0
  29. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/README.md +0 -0
  30. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/develop.md +0 -0
  31. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/images/quality_score.png +0 -0
  32. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/images/quality_score_viewer.png +0 -0
  33. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/index.md +0 -0
  34. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/install.md +0 -0
  35. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/quality_score.md +0 -0
  36. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/tech/api_reference.md +0 -0
  37. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/docs/tech/cli.md +0 -0
  38. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/camera.py +0 -0
  39. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/cameras.csv +0 -0
  40. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/i18n.py +0 -0
  41. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/main.py +0 -0
  42. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/model.py +0 -0
  43. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/py.typed +0 -0
  44. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/br/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  45. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/br/LC_MESSAGES/geopic_tag_reader.po +0 -0
  46. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  47. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/da/LC_MESSAGES/geopic_tag_reader.po +0 -0
  48. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  49. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/eo/LC_MESSAGES/geopic_tag_reader.po +0 -0
  50. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  51. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.po +0 -0
  52. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  53. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.po +0 -0
  54. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/hu/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  55. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/hu/LC_MESSAGES/geopic_tag_reader.po +0 -0
  56. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  57. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/it/LC_MESSAGES/geopic_tag_reader.po +0 -0
  58. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/ja/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  59. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/ja/LC_MESSAGES/geopic_tag_reader.po +0 -0
  60. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/ko/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  61. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/ko/LC_MESSAGES/geopic_tag_reader.po +0 -0
  62. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/pl/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  63. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/pl/LC_MESSAGES/geopic_tag_reader.po +0 -0
  64. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/sv/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  65. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/sv/LC_MESSAGES/geopic_tag_reader.po +0 -0
  66. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/zh_Hant/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  67. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/translations/zh_Hant/LC_MESSAGES/geopic_tag_reader.po +0 -0
  68. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/geopic_tag_reader/writer.py +0 -0
  69. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/mkdocs.yml +0 -0
  70. {geopic_tag_reader-1.6.0 → geopic_tag_reader-1.8.0}/pytest.ini +0 -0
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.8.0] - 2025-05-25
11
+
12
+ ### Changed
13
+
14
+ - Improved duplicates detection, now the whole sequence is deduplicated, not only the consecutive pictures
15
+ - **Breaking Change** Now the `duplicate_pictures` in the report is a list of `Duplicate` (instead of `Picture`), so we can get more information about the duplicate.
16
+
17
+ ## [1.7.0] - 2025-05-25
18
+
19
+ ### Changed
20
+
21
+ - Better support of cylindrical images.
22
+
10
23
  ## [1.6.0] - 2025-05-09
11
24
 
12
25
  ### Added
@@ -285,7 +298,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
285
298
 
286
299
  - EXIF tag reading methods extracted from [Panoramax/GeoVisio API](https://gitlab.com/panoramax/server/api)
287
300
 
288
- [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.6.0...main
301
+ [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.8.0...main
302
+ [1.8.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.7.0...1.8.0
303
+ [1.7.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.6.0...1.7.0
289
304
  [1.6.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.5.0...1.6.0
290
305
  [1.5.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.4.2...1.5.0
291
306
  [1.4.2]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.4.1...1.4.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geopic-tag-reader
3
- Version: 1.6.0
3
+ Version: 1.8.0
4
4
  Summary: GeoPicTagReader
5
5
  Author-email: Adrien PAVIE <panieravide@riseup.net>
6
6
  Requires-Python: >=3.9
@@ -14,6 +14,7 @@ Requires-Dist: timezonefinder == 6.5.9
14
14
  Requires-Dist: pytz ~= 2025.2
15
15
  Requires-Dist: types-pytz ~= 2025.2.0
16
16
  Requires-Dist: types-python-dateutil ~= 2.9.0
17
+ Requires-Dist: rtree ~= 1.4.0
17
18
  Requires-Dist: flit ~= 3.8.0 ; extra == "build"
18
19
  Requires-Dist: black ~= 25.1.0 ; extra == "dev"
19
20
  Requires-Dist: mypy ~= 1.15.0 ; extra == "dev"
@@ -2,4 +2,4 @@
2
2
  GeoPicTagReader
3
3
  """
4
4
 
5
- __version__ = "1.6.0"
5
+ __version__ = "1.8.0"
@@ -353,20 +353,35 @@ def getPictureMetadata(data: dict, width: int, height: int, lang_code: str = "en
353
353
  # Cropped pano data
354
354
  crop = None
355
355
  if (
356
- isExifTagUsable(data, "Xmp.GPano.FullPanoWidthPixels", int)
357
- and isExifTagUsable(data, "Xmp.GPano.FullPanoHeightPixels", int)
356
+ (isExifTagUsable(data, "Xmp.GPano.FullPanoWidthPixels", int) or isExifTagUsable(data, "Xmp.GPano.FullPanoHeightPixels", int))
358
357
  and isExifTagUsable(data, "Xmp.GPano.CroppedAreaImageWidthPixels", int)
359
358
  and isExifTagUsable(data, "Xmp.GPano.CroppedAreaImageHeightPixels", int)
360
359
  and isExifTagUsable(data, "Xmp.GPano.CroppedAreaLeftPixels", int)
361
360
  and isExifTagUsable(data, "Xmp.GPano.CroppedAreaTopPixels", int)
362
361
  ):
363
- fw = int(data["Xmp.GPano.FullPanoWidthPixels"])
364
- fh = int(data["Xmp.GPano.FullPanoHeightPixels"])
365
362
  w = int(data["Xmp.GPano.CroppedAreaImageWidthPixels"])
366
363
  h = int(data["Xmp.GPano.CroppedAreaImageHeightPixels"])
367
364
  l = int(data["Xmp.GPano.CroppedAreaLeftPixels"])
368
365
  t = int(data["Xmp.GPano.CroppedAreaTopPixels"])
369
366
 
367
+ # Compute full width & height, even if one is missing
368
+ fw = None
369
+ fh = None
370
+ if isExifTagUsable(data, "Xmp.GPano.FullPanoWidthPixels", int):
371
+ fw = int(data["Xmp.GPano.FullPanoWidthPixels"])
372
+ if isExifTagUsable(data, "Xmp.GPano.FullPanoHeightPixels", int):
373
+ fh = int(data["Xmp.GPano.FullPanoHeightPixels"])
374
+ if fw is None:
375
+ fw = fh * 2 # type: ignore
376
+ # Center image horizontally if full weight is guessed
377
+ if fw > w and l == 0:
378
+ l = math.floor((fw - w) / 2)
379
+ if fh is None:
380
+ fh = math.floor(fw / 2) # type: ignore
381
+ # Center image vertically if full height is guessed
382
+ if fh > h and t == 0:
383
+ t = math.floor((fh - h) / 2)
384
+
370
385
  if fw > w or fh > h:
371
386
  crop = CropValues(fw, fh, w, h, l, t)
372
387
 
@@ -383,6 +398,10 @@ def getPictureMetadata(data: dict, width: int, height: int, lang_code: str = "en
383
398
  # 360° based on GPano EXIF tag
384
399
  if isExifTagUsable(data, "Xmp.GPano.ProjectionType"):
385
400
  pic_type = data["Xmp.GPano.ProjectionType"]
401
+
402
+ # Check for cylindrical
403
+ if pic_type == "cylindrical" and crop is not None:
404
+ pic_type = "equirectangular"
386
405
  # 360° based on known models
387
406
  elif camera.is_360(make, model, width, height):
388
407
  pic_type = "equirectangular"
@@ -1,9 +1,10 @@
1
- from dataclasses import dataclass
1
+ from dataclasses import dataclass, field
2
2
  from enum import Enum
3
3
  from typing import Optional, List, Tuple
4
4
  from pathlib import PurePath
5
5
  from geopic_tag_reader.reader import GeoPicTags
6
6
  import datetime
7
+ from rtree import index
7
8
  import math
8
9
 
9
10
 
@@ -37,6 +38,7 @@ class SplitParams:
37
38
  class Picture:
38
39
  filename: str
39
40
  metadata: GeoPicTags
41
+ heading_computed: bool = False
40
42
 
41
43
  def distance_to(self, other) -> float:
42
44
  """Computes distance in meters based on Haversine formula"""
@@ -50,7 +52,7 @@ class Picture:
50
52
  distance = R * c
51
53
  return distance
52
54
 
53
- def rotation_angle(self, other) -> Optional[float]:
55
+ def rotation_angle(self, other) -> Optional[int]:
54
56
  return rotation_angle(self.metadata.heading, other.metadata.heading)
55
57
 
56
58
 
@@ -116,11 +118,19 @@ class Sequence:
116
118
  return (otherSeq.from_ts() - self.to_ts(), otherSeq.pictures[0].distance_to(self.pictures[-1])) # type: ignore
117
119
 
118
120
 
121
+ @dataclass
122
+ class Duplicate:
123
+ picture: Picture
124
+ duplicate_of: Picture
125
+ distance: float
126
+ angle: Optional[int]
127
+
128
+
119
129
  @dataclass
120
130
  class DispatchReport:
121
131
  sequences: List[Sequence]
122
- duplicate_pictures: Optional[List[Picture]] = None
123
- sequences_splits: Optional[List[Split]] = None
132
+ duplicate_pictures: List[Duplicate] = field(default_factory=list)
133
+ sequences_splits: List[Split] = field(default_factory=list)
124
134
 
125
135
 
126
136
  def sort_pictures(pictures: List[Picture], method: Optional[SortMethod] = SortMethod.time_asc) -> List[Picture]:
@@ -202,7 +212,38 @@ def sort_pictures(pictures: List[Picture], method: Optional[SortMethod] = SortMe
202
212
  return pictures
203
213
 
204
214
 
205
- def find_duplicates(pictures: List[Picture], params: Optional[MergeParams] = None) -> Tuple[List[Picture], List[Picture]]:
215
+ def are_duplicates(a: Picture, b: Picture, params: MergeParams) -> Optional[Tuple[float, Optional[int]]]:
216
+ """
217
+ Check if 2 pictures are too similar and should be considered duplicates
218
+
219
+ They are duplicates if they are close to each other, and for non 360 pictures, if they are roughly in the same direction.
220
+
221
+ Note that we only consider the direction (also called heading) if it is provided by the camera (and not computed with the sequences geometries)
222
+ since GPS can drift a bit resulting in erratic direction when waiting at a traffic light cf https://gitlab.com/panoramax/server/api/-/issues/231#note_2329723526
223
+
224
+ Return None if not duplicates, or the distance/angle if they are
225
+ """
226
+ dist = a.distance_to(b)
227
+
228
+ if params.maxDistance is None or dist > params.maxDistance:
229
+ return None
230
+
231
+ # Compare angle (if available on both images)
232
+ angle = a.rotation_angle(b)
233
+ # if one of the heading has been computed, we cannot rely on this angle being correct, so we don't consider it for the deduplication
234
+ # it's especially important when stopped and the GPS drift a bit, cf https://gitlab.com/panoramax/server/api/-/issues/231#note_2329723526
235
+ angle_computed = b.heading_computed or a.heading_computed
236
+ if angle is None or angle_computed or params.maxRotationAngle is None:
237
+ return (dist, None)
238
+ if angle <= params.maxRotationAngle:
239
+ return (dist, angle)
240
+ return None
241
+
242
+
243
+ APPROX_DEGREE_TO_METER = 0.00001 # this is roughly 1m
244
+
245
+
246
+ def find_duplicates(pictures: List[Picture], params: Optional[MergeParams] = None) -> Tuple[List[Picture], List[Duplicate]]:
206
247
  """
207
248
  Finds too similar pictures.
208
249
  Note that input list should be properly sorted.
@@ -217,42 +258,51 @@ def find_duplicates(pictures: List[Picture], params: Optional[MergeParams] = Non
217
258
  (Non-duplicates pictures, Duplicates pictures)
218
259
  """
219
260
 
220
- if params is None or not params.is_merge_needed():
261
+ if params is None or not params.is_merge_needed() or not pictures:
221
262
  return (pictures, [])
263
+ assert params.maxDistance
222
264
 
223
265
  nonDups: List[Picture] = []
224
- dups: List[Picture] = []
225
- lastNonDuplicatedPicId = 0
266
+ duplicates = []
267
+ duplicates_idx = set()
226
268
 
227
- for i, currentPic in enumerate(pictures):
228
- if i == 0:
229
- nonDups.append(currentPic)
230
- continue
269
+ rtree_index = index.Index(((i, (p.metadata.lon, p.metadata.lat, p.metadata.lon, p.metadata.lat), None) for i, p in enumerate(pictures)))
231
270
 
232
- prevPic = pictures[lastNonDuplicatedPicId]
271
+ # the rtree will give us all the neighbors in an approximated bounding box,
272
+ # and will check, for all those pictures if some pictures are really closed, using a real haversine distance
273
+ # we do a rough conversion between the maxDistance (in m) to degree, since it's only for the initial bounding box
274
+ # and we use a bbox bigger than necessary (could be half by direction) to not miss duplicates due to the degree to meter approximation
233
275
 
234
- if prevPic.metadata is None or currentPic.metadata is None:
235
- nonDups.append(currentPic)
276
+ bounding_box_tolerance_approx = params.maxDistance * APPROX_DEGREE_TO_METER
277
+ for i, currentPic in enumerate(pictures):
278
+ if i in duplicates_idx:
279
+ # the picture has already been flagged as duplicate by one of its neighbor, we can skip it
236
280
  continue
237
281
 
238
- is_duplicate = False
282
+ bounding_box = (
283
+ currentPic.metadata.lon - bounding_box_tolerance_approx,
284
+ currentPic.metadata.lat - bounding_box_tolerance_approx,
285
+ currentPic.metadata.lon + bounding_box_tolerance_approx,
286
+ currentPic.metadata.lat + bounding_box_tolerance_approx,
287
+ )
239
288
 
240
- # Compare distance
241
- dist = prevPic.distance_to(currentPic)
289
+ near_pics_idx = rtree_index.nearest(bounding_box, num_results=100, objects=False)
242
290
 
243
- if params.maxDistance is not None and dist <= params.maxDistance:
244
- # Compare angle (if available on both images)
245
- angle = prevPic.rotation_angle(currentPic)
246
- if angle is None or params.maxRotationAngle is None or angle <= params.maxRotationAngle:
247
- is_duplicate = True
291
+ for neighbor_idx in near_pics_idx:
292
+ if neighbor_idx == i:
293
+ continue
294
+ if neighbor_idx in duplicates_idx:
295
+ continue
296
+ neighbor = pictures[neighbor_idx]
297
+ duplicate_details = are_duplicates(currentPic, neighbor, params)
298
+ if duplicate_details:
299
+ distance, angle = duplicate_details
300
+ duplicates_idx.add(neighbor_idx)
301
+ duplicates.append(Duplicate(picture=neighbor, duplicate_of=currentPic, distance=round(distance, 2), angle=angle))
248
302
 
249
- if is_duplicate:
250
- dups.append(currentPic)
251
- else:
252
- lastNonDuplicatedPicId = i
253
- nonDups.append(currentPic)
303
+ nonDups.append(currentPic)
254
304
 
255
- return (nonDups, dups)
305
+ return (nonDups, duplicates)
256
306
 
257
307
 
258
308
  def split_in_sequences(pictures: List[Picture], splitParams: Optional[SplitParams] = SplitParams()) -> Tuple[List[Sequence], List[Split]]:
@@ -354,10 +404,14 @@ def dispatch_pictures(
354
404
  # Sort
355
405
  myPics = sort_pictures(pictures, sortMethod)
356
406
 
357
- # De-duplicate
358
- (myPics, dupsPics) = find_duplicates(myPics, mergeParams)
359
-
360
407
  # Split in sequences
361
408
  (mySeqs, splits) = split_in_sequences(myPics, splitParams)
362
409
 
363
- return DispatchReport(mySeqs, dupsPics if len(dupsPics) > 0 else None, splits if len(splits) > 0 else None)
410
+ # De-duplicate inside each sequences
411
+ dups_pics = []
412
+ for s in mySeqs:
413
+ (myPics, dups) = find_duplicates(s.pictures, mergeParams)
414
+ s.pictures = myPics
415
+ dups_pics.extend(dups)
416
+
417
+ return DispatchReport(sequences=mySeqs, duplicate_pictures=dups_pics, sequences_splits=splits)
@@ -3,18 +3,17 @@
3
3
  # This file is distributed under the same license as the PACKAGE package.
4
4
  # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
5
  #
6
- #, fuzzy
7
6
  msgid ""
8
7
  msgstr ""
9
8
  "Project-Id-Version: PACKAGE VERSION\n"
10
9
  "Report-Msgid-Bugs-To: \n"
11
10
  "POT-Creation-Date: 2025-05-09 12:12+0200\n"
12
11
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
- "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
- "Language-Team: LANGUAGE <LL@li.org>\n"
15
- "Language: \n"
12
+ "Last-Translator: Automatically generated\n"
13
+ "Language-Team: none\n"
14
+ "Language: ar\n"
16
15
  "MIME-Version: 1.0\n"
17
- "Content-Type: text/plain; charset=CHARSET\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
18
17
  "Content-Transfer-Encoding: 8bit\n"
19
18
 
20
19
  #: geopic_tag_reader/main.py:26
@@ -8,8 +8,9 @@ msgstr ""
8
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
10
  "POT-Creation-Date: 2024-07-10 13:05+0200\n"
11
- "PO-Revision-Date: 2025-04-15 07:48+0000\n"
12
- "Last-Translator: Anonymous <noreply@weblate.org>\n"
11
+ "PO-Revision-Date: 2025-07-31 16:22+0000\n"
12
+ "Last-Translator: osmmapper222 <osmmapper222@users.noreply.weblate.panoramax."
13
+ "xyz>\n"
13
14
  "Language-Team: German <http://weblate.panoramax.xyz/projects/panoramax/"
14
15
  "tag-reader/de/>\n"
15
16
  "Language: de\n"
@@ -40,7 +41,6 @@ msgid "Type:"
40
41
  msgstr "Typ:"
41
42
 
42
43
  #: geopic_tag_reader/main.py:31
43
- #, fuzzy
44
44
  msgid "Make:"
45
45
  msgstr "Hersteller:"
46
46
 
@@ -7,8 +7,8 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2025-05-09 12:12+0200\n"
11
- "PO-Revision-Date: 2025-05-09 12:12+0200\n"
10
+ "POT-Creation-Date: 2025-08-25 15:35+0200\n"
11
+ "PO-Revision-Date: 2025-08-25 15:35+0200\n"
12
12
  "Last-Translator: Automatically generated\n"
13
13
  "Language-Team: none\n"
14
14
  "Language: en\n"
@@ -119,17 +119,17 @@ msgstr ""
119
119
  "No make and model value found, no assumption on focal length or GPS "
120
120
  "precision can be made"
121
121
 
122
- #: geopic_tag_reader/reader.py:399
122
+ #: geopic_tag_reader/reader.py:418
123
123
  msgid ""
124
124
  "No focal length value was found, this prevents calculating field of view"
125
125
  msgstr ""
126
126
  "No focal length value was found, this prevents calculating field of view"
127
127
 
128
- #: geopic_tag_reader/reader.py:451
128
+ #: geopic_tag_reader/reader.py:470
129
129
  msgid "No GPS accuracy value found, this prevents computing a quality score"
130
130
  msgstr "No GPS accuracy value found, this prevents computing a quality score"
131
131
 
132
- #: geopic_tag_reader/reader.py:453
132
+ #: geopic_tag_reader/reader.py:472
133
133
  msgid ""
134
134
  "No GPS horizontal positioning error value found, GPS accuracy can only be "
135
135
  "estimated"
@@ -137,35 +137,35 @@ msgstr ""
137
137
  "No GPS horizontal positioning error value found, GPS accuracy can only be "
138
138
  "estimated"
139
139
 
140
- #: geopic_tag_reader/reader.py:460
140
+ #: geopic_tag_reader/reader.py:479
141
141
  msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
142
142
  msgstr "No GPS coordinates or broken coordinates in picture EXIF tags"
143
143
 
144
- #: geopic_tag_reader/reader.py:466
144
+ #: geopic_tag_reader/reader.py:485
145
145
  msgid "No valid date in picture EXIF tags"
146
146
  msgstr "No valid date in picture EXIF tags"
147
147
 
148
- #: geopic_tag_reader/reader.py:471
148
+ #: geopic_tag_reader/reader.py:490
149
149
  msgid "The picture is missing mandatory metadata:"
150
150
  msgstr "The picture is missing mandatory metadata:"
151
151
 
152
- #: geopic_tag_reader/reader.py:585 geopic_tag_reader/reader.py:614
152
+ #: geopic_tag_reader/reader.py:604 geopic_tag_reader/reader.py:633
153
153
  msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
154
154
  msgstr "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
155
155
 
156
- #: geopic_tag_reader/reader.py:593
156
+ #: geopic_tag_reader/reader.py:612
157
157
  msgid "Broken GPS coordinates in picture EXIF tags"
158
158
  msgstr "Broken GPS coordinates in picture EXIF tags"
159
159
 
160
- #: geopic_tag_reader/reader.py:596 geopic_tag_reader/reader.py:620
160
+ #: geopic_tag_reader/reader.py:615 geopic_tag_reader/reader.py:639
161
161
  msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
162
162
  msgstr "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
163
163
 
164
- #: geopic_tag_reader/reader.py:681
164
+ #: geopic_tag_reader/reader.py:700
165
165
  msgid "Precise timezone information not found, fallback to UTC"
166
166
  msgstr "Precise timezone information not found, fallback to UTC"
167
167
 
168
- #: geopic_tag_reader/reader.py:686
168
+ #: geopic_tag_reader/reader.py:705
169
169
  msgid ""
170
170
  "Precise timezone information not found (and no GPS coordinates to help), "
171
171
  "fallback to UTC"
@@ -173,14 +173,14 @@ msgstr ""
173
173
  "Precise timezone information not found (and no GPS coordinates to help), "
174
174
  "fallback to UTC"
175
175
 
176
- #: geopic_tag_reader/reader.py:690
176
+ #: geopic_tag_reader/reader.py:709
177
177
  #, python-brace-format
178
178
  msgid ""
179
179
  "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
180
180
  msgstr ""
181
181
  "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
182
182
 
183
- #: geopic_tag_reader/reader.py:722
183
+ #: geopic_tag_reader/reader.py:741
184
184
  #, python-brace-format
185
185
  msgid ""
186
186
  "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
@@ -189,12 +189,12 @@ msgstr ""
189
189
  "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
190
190
  "group)"
191
191
 
192
- #: geopic_tag_reader/reader.py:753
192
+ #: geopic_tag_reader/reader.py:772
193
193
  #, python-brace-format
194
194
  msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
195
195
  msgstr "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
196
196
 
197
- #: geopic_tag_reader/reader.py:779
197
+ #: geopic_tag_reader/reader.py:798
198
198
  #, python-brace-format
199
199
  msgid ""
200
200
  "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
@@ -8,8 +8,8 @@ msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
10
  "POT-Creation-Date: 2024-10-28 11:05+0100\n"
11
- "PO-Revision-Date: 2025-04-15 07:48+0000\n"
12
- "Last-Translator: Anonymous <noreply@weblate.org>\n"
11
+ "PO-Revision-Date: 2025-07-07 06:24+0000\n"
12
+ "Last-Translator: PanierAvide <adrien@pavie.info>\n"
13
13
  "Language-Team: French <http://weblate.panoramax.xyz/projects/panoramax/"
14
14
  "tag-reader/fr/>\n"
15
15
  "Language: fr\n"
@@ -52,7 +52,6 @@ msgid "Type:"
52
52
  msgstr "Type :"
53
53
 
54
54
  #: geopic_tag_reader/main.py:34
55
- #, fuzzy
56
55
  msgid "Make:"
57
56
  msgstr "Fabriquant :"
58
57
 
@@ -0,0 +1,197 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: PACKAGE VERSION\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2025-08-25 15:35+0200\n"
12
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
+ "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=CHARSET\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+
20
+ #: geopic_tag_reader/main.py:26
21
+ msgid "Latitude:"
22
+ msgstr ""
23
+
24
+ #: geopic_tag_reader/main.py:27
25
+ msgid "Longitude:"
26
+ msgstr ""
27
+
28
+ #: geopic_tag_reader/main.py:28
29
+ msgid "GPS accuracy:"
30
+ msgstr ""
31
+
32
+ #: geopic_tag_reader/main.py:28 geopic_tag_reader/main.py:31
33
+ #: geopic_tag_reader/main.py:32
34
+ msgid "not set"
35
+ msgstr ""
36
+
37
+ #: geopic_tag_reader/main.py:29
38
+ msgid "Timestamp:"
39
+ msgstr ""
40
+
41
+ #: geopic_tag_reader/main.py:31
42
+ msgid "(GPS)"
43
+ msgstr ""
44
+
45
+ #: geopic_tag_reader/main.py:32
46
+ msgid "(Camera)"
47
+ msgstr ""
48
+
49
+ #: geopic_tag_reader/main.py:33
50
+ msgid "Heading:"
51
+ msgstr ""
52
+
53
+ #: geopic_tag_reader/main.py:34
54
+ msgid "Type:"
55
+ msgstr ""
56
+
57
+ #: geopic_tag_reader/main.py:35
58
+ msgid "Make:"
59
+ msgstr ""
60
+
61
+ #: geopic_tag_reader/main.py:36
62
+ msgid "Model:"
63
+ msgstr ""
64
+
65
+ #: geopic_tag_reader/main.py:37
66
+ msgid "Focal length:"
67
+ msgstr ""
68
+
69
+ #: geopic_tag_reader/main.py:38
70
+ msgid "Field of view:"
71
+ msgstr ""
72
+
73
+ #: geopic_tag_reader/main.py:39
74
+ msgid "Sensor width:"
75
+ msgstr ""
76
+
77
+ #: geopic_tag_reader/main.py:40
78
+ msgid "Crop parameters:"
79
+ msgstr ""
80
+
81
+ #: geopic_tag_reader/main.py:41
82
+ msgid "Pitch:"
83
+ msgstr ""
84
+
85
+ #: geopic_tag_reader/main.py:42
86
+ msgid "Roll:"
87
+ msgstr ""
88
+
89
+ #: geopic_tag_reader/main.py:43
90
+ msgid "Yaw:"
91
+ msgstr ""
92
+
93
+ #: geopic_tag_reader/main.py:46
94
+ msgid "Warnings raised by reader:"
95
+ msgstr ""
96
+
97
+ #: geopic_tag_reader/reader.py:246
98
+ msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
99
+ msgstr ""
100
+
101
+ #: geopic_tag_reader/reader.py:248
102
+ msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
103
+ msgstr ""
104
+
105
+ #: geopic_tag_reader/reader.py:276
106
+ #, python-brace-format
107
+ msgid "Skipping Mapillary date/time as it was not recognized: {v}"
108
+ msgstr ""
109
+
110
+ #: geopic_tag_reader/reader.py:306
111
+ msgid "No heading value was found, this reduces usability of picture"
112
+ msgstr ""
113
+
114
+ #: geopic_tag_reader/reader.py:349
115
+ msgid ""
116
+ "No make and model value found, no assumption on focal length or GPS "
117
+ "precision can be made"
118
+ msgstr ""
119
+
120
+ #: geopic_tag_reader/reader.py:418
121
+ msgid ""
122
+ "No focal length value was found, this prevents calculating field of view"
123
+ msgstr ""
124
+
125
+ #: geopic_tag_reader/reader.py:470
126
+ msgid "No GPS accuracy value found, this prevents computing a quality score"
127
+ msgstr ""
128
+
129
+ #: geopic_tag_reader/reader.py:472
130
+ msgid ""
131
+ "No GPS horizontal positioning error value found, GPS accuracy can only be "
132
+ "estimated"
133
+ msgstr ""
134
+
135
+ #: geopic_tag_reader/reader.py:479
136
+ msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
137
+ msgstr ""
138
+
139
+ #: geopic_tag_reader/reader.py:485
140
+ msgid "No valid date in picture EXIF tags"
141
+ msgstr ""
142
+
143
+ #: geopic_tag_reader/reader.py:490
144
+ msgid "The picture is missing mandatory metadata:"
145
+ msgstr ""
146
+
147
+ #: geopic_tag_reader/reader.py:604 geopic_tag_reader/reader.py:633
148
+ msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
149
+ msgstr ""
150
+
151
+ #: geopic_tag_reader/reader.py:612
152
+ msgid "Broken GPS coordinates in picture EXIF tags"
153
+ msgstr ""
154
+
155
+ #: geopic_tag_reader/reader.py:615 geopic_tag_reader/reader.py:639
156
+ msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
157
+ msgstr ""
158
+
159
+ #: geopic_tag_reader/reader.py:700
160
+ msgid "Precise timezone information not found, fallback to UTC"
161
+ msgstr ""
162
+
163
+ #: geopic_tag_reader/reader.py:705
164
+ msgid ""
165
+ "Precise timezone information not found (and no GPS coordinates to help), "
166
+ "fallback to UTC"
167
+ msgstr ""
168
+
169
+ #: geopic_tag_reader/reader.py:709
170
+ #, python-brace-format
171
+ msgid ""
172
+ "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
173
+ msgstr ""
174
+
175
+ #: geopic_tag_reader/reader.py:741
176
+ #, python-brace-format
177
+ msgid ""
178
+ "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
179
+ "group)"
180
+ msgstr ""
181
+
182
+ #: geopic_tag_reader/reader.py:772
183
+ #, python-brace-format
184
+ msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
185
+ msgstr ""
186
+
187
+ #: geopic_tag_reader/reader.py:798
188
+ #, python-brace-format
189
+ msgid ""
190
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
191
+ "not matching value from EXIF field ({microseconds}). Max value will be kept."
192
+ msgstr ""
193
+
194
+ #: geopic_tag_reader/writer.py:132
195
+ #, python-brace-format
196
+ msgid "Unsupported key in additional tags ({k})"
197
+ msgstr ""
@@ -8,8 +8,8 @@ msgstr ""
8
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
10
  "POT-Creation-Date: 2024-10-22 15:05+0200\n"
11
- "PO-Revision-Date: 2025-04-15 07:48+0000\n"
12
- "Last-Translator: Anonymous <noreply@weblate.org>\n"
11
+ "PO-Revision-Date: 2025-06-03 19:06+0000\n"
12
+ "Last-Translator: GeoArchief <panoramax@geoarchief.nl>\n"
13
13
  "Language-Team: Dutch <http://weblate.panoramax.xyz/projects/panoramax/"
14
14
  "tag-reader/nl/>\n"
15
15
  "Language: nl\n"
@@ -45,16 +45,15 @@ msgstr "(Camera)"
45
45
 
46
46
  #: geopic_tag_reader/main.py:32
47
47
  msgid "Heading:"
48
- msgstr "Titel:"
48
+ msgstr "Richtingshoek:"
49
49
 
50
50
  #: geopic_tag_reader/main.py:33
51
51
  msgid "Type:"
52
- msgstr "Soort:"
52
+ msgstr "Type:"
53
53
 
54
54
  #: geopic_tag_reader/main.py:34
55
- #, fuzzy
56
55
  msgid "Make:"
57
- msgstr "Maker:"
56
+ msgstr "Merk:"
58
57
 
59
58
  #: geopic_tag_reader/main.py:35
60
59
  msgid "Model:"
@@ -66,19 +65,19 @@ msgstr "Brandpuntsafstand:"
66
65
 
67
66
  #: geopic_tag_reader/main.py:37
68
67
  msgid "Crop parameters:"
69
- msgstr "Crop parameters:"
68
+ msgstr "Bijsnijd parameters:"
70
69
 
71
70
  #: geopic_tag_reader/main.py:38
72
71
  msgid "Pitch:"
73
- msgstr "Standplaats:"
72
+ msgstr "Pitch:"
74
73
 
75
74
  #: geopic_tag_reader/main.py:39
76
75
  msgid "Roll:"
77
- msgstr "Draaien:"
76
+ msgstr "Roll:"
78
77
 
79
78
  #: geopic_tag_reader/main.py:40
80
79
  msgid "Yaw:"
81
- msgstr "Gier (yaw):"
80
+ msgstr "Yaw:"
82
81
 
83
82
  #: geopic_tag_reader/main.py:43
84
83
  msgid "Warnings raised by reader:"
@@ -87,12 +86,12 @@ msgstr "Waarschuwingen van lezer:"
87
86
  #: geopic_tag_reader/reader.py:218
88
87
  msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
89
88
  msgstr ""
90
- "Gelezen breedtegraad is buiten WGS84-grenzen (zou in [-90, 90] moeten zijn)"
89
+ "Gelezen breedtegraad is buiten WGS84-grenzen (moet binnen [-90, 90] liggen)"
91
90
 
92
91
  #: geopic_tag_reader/reader.py:220
93
92
  msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
94
93
  msgstr ""
95
- "Gelezen lengtegraad is buiten WGS84-grenzen (zou in [-180, 180] moeten zijn)"
94
+ "Gelezen lengtegraad is buiten WGS84-grenzen (moet binnen [-180, 180] liggen)"
96
95
 
97
96
  #: geopic_tag_reader/reader.py:248
98
97
  #, python-brace-format
@@ -119,7 +118,7 @@ msgstr ""
119
118
 
120
119
  #: geopic_tag_reader/reader.py:484
121
120
  msgid "Broken GPS coordinates in picture EXIF tags"
122
- msgstr "Ontbrekende GPS-coördinaten in EXIF-tags de van foto"
121
+ msgstr "Ontbrekende GPS-coördinaten in EXIF-tags de van afbeelding"
123
122
 
124
123
  #: geopic_tag_reader/reader.py:487 geopic_tag_reader/reader.py:511
125
124
  msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
@@ -180,7 +179,7 @@ msgstr "Niet-ondersteunde sleutel in bijhorende tags ({k})"
180
179
  #: geopic_tag_reader/reader.py:278
181
180
  msgid "No heading value was found, this reduces usability of picture"
182
181
  msgstr ""
183
- "Er is geen koerswaarde gevonden, dit vermindert de bruikbaarheid van de "
182
+ "Er is geen richtingshoek gevonden, dit vermindert de bruikbaarheid van de "
184
183
  "afbeelding"
185
184
 
186
185
  #: geopic_tag_reader/reader.py:321
@@ -0,0 +1,196 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: PACKAGE VERSION\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2025-05-09 12:12+0200\n"
11
+ "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
12
+ "Last-Translator: Automatically generated\n"
13
+ "Language-Team: none\n"
14
+ "Language: ti\n"
15
+ "MIME-Version: 1.0\n"
16
+ "Content-Type: text/plain; charset=UTF-8\n"
17
+ "Content-Transfer-Encoding: 8bit\n"
18
+
19
+ #: geopic_tag_reader/main.py:26
20
+ msgid "Latitude:"
21
+ msgstr ""
22
+
23
+ #: geopic_tag_reader/main.py:27
24
+ msgid "Longitude:"
25
+ msgstr ""
26
+
27
+ #: geopic_tag_reader/main.py:28
28
+ msgid "GPS accuracy:"
29
+ msgstr ""
30
+
31
+ #: geopic_tag_reader/main.py:28 geopic_tag_reader/main.py:31
32
+ #: geopic_tag_reader/main.py:32
33
+ msgid "not set"
34
+ msgstr ""
35
+
36
+ #: geopic_tag_reader/main.py:29
37
+ msgid "Timestamp:"
38
+ msgstr ""
39
+
40
+ #: geopic_tag_reader/main.py:31
41
+ msgid "(GPS)"
42
+ msgstr ""
43
+
44
+ #: geopic_tag_reader/main.py:32
45
+ msgid "(Camera)"
46
+ msgstr ""
47
+
48
+ #: geopic_tag_reader/main.py:33
49
+ msgid "Heading:"
50
+ msgstr ""
51
+
52
+ #: geopic_tag_reader/main.py:34
53
+ msgid "Type:"
54
+ msgstr ""
55
+
56
+ #: geopic_tag_reader/main.py:35
57
+ msgid "Make:"
58
+ msgstr ""
59
+
60
+ #: geopic_tag_reader/main.py:36
61
+ msgid "Model:"
62
+ msgstr ""
63
+
64
+ #: geopic_tag_reader/main.py:37
65
+ msgid "Focal length:"
66
+ msgstr ""
67
+
68
+ #: geopic_tag_reader/main.py:38
69
+ msgid "Field of view:"
70
+ msgstr ""
71
+
72
+ #: geopic_tag_reader/main.py:39
73
+ msgid "Sensor width:"
74
+ msgstr ""
75
+
76
+ #: geopic_tag_reader/main.py:40
77
+ msgid "Crop parameters:"
78
+ msgstr ""
79
+
80
+ #: geopic_tag_reader/main.py:41
81
+ msgid "Pitch:"
82
+ msgstr ""
83
+
84
+ #: geopic_tag_reader/main.py:42
85
+ msgid "Roll:"
86
+ msgstr ""
87
+
88
+ #: geopic_tag_reader/main.py:43
89
+ msgid "Yaw:"
90
+ msgstr ""
91
+
92
+ #: geopic_tag_reader/main.py:46
93
+ msgid "Warnings raised by reader:"
94
+ msgstr ""
95
+
96
+ #: geopic_tag_reader/reader.py:246
97
+ msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
98
+ msgstr ""
99
+
100
+ #: geopic_tag_reader/reader.py:248
101
+ msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
102
+ msgstr ""
103
+
104
+ #: geopic_tag_reader/reader.py:276
105
+ #, python-brace-format
106
+ msgid "Skipping Mapillary date/time as it was not recognized: {v}"
107
+ msgstr ""
108
+
109
+ #: geopic_tag_reader/reader.py:306
110
+ msgid "No heading value was found, this reduces usability of picture"
111
+ msgstr ""
112
+
113
+ #: geopic_tag_reader/reader.py:349
114
+ msgid ""
115
+ "No make and model value found, no assumption on focal length or GPS "
116
+ "precision can be made"
117
+ msgstr ""
118
+
119
+ #: geopic_tag_reader/reader.py:399
120
+ msgid ""
121
+ "No focal length value was found, this prevents calculating field of view"
122
+ msgstr ""
123
+
124
+ #: geopic_tag_reader/reader.py:451
125
+ msgid "No GPS accuracy value found, this prevents computing a quality score"
126
+ msgstr ""
127
+
128
+ #: geopic_tag_reader/reader.py:453
129
+ msgid ""
130
+ "No GPS horizontal positioning error value found, GPS accuracy can only be "
131
+ "estimated"
132
+ msgstr ""
133
+
134
+ #: geopic_tag_reader/reader.py:460
135
+ msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
136
+ msgstr ""
137
+
138
+ #: geopic_tag_reader/reader.py:466
139
+ msgid "No valid date in picture EXIF tags"
140
+ msgstr ""
141
+
142
+ #: geopic_tag_reader/reader.py:471
143
+ msgid "The picture is missing mandatory metadata:"
144
+ msgstr ""
145
+
146
+ #: geopic_tag_reader/reader.py:585 geopic_tag_reader/reader.py:614
147
+ msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
148
+ msgstr ""
149
+
150
+ #: geopic_tag_reader/reader.py:593
151
+ msgid "Broken GPS coordinates in picture EXIF tags"
152
+ msgstr ""
153
+
154
+ #: geopic_tag_reader/reader.py:596 geopic_tag_reader/reader.py:620
155
+ msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
156
+ msgstr ""
157
+
158
+ #: geopic_tag_reader/reader.py:681
159
+ msgid "Precise timezone information not found, fallback to UTC"
160
+ msgstr ""
161
+
162
+ #: geopic_tag_reader/reader.py:686
163
+ msgid ""
164
+ "Precise timezone information not found (and no GPS coordinates to help), "
165
+ "fallback to UTC"
166
+ msgstr ""
167
+
168
+ #: geopic_tag_reader/reader.py:690
169
+ #, python-brace-format
170
+ msgid ""
171
+ "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
172
+ msgstr ""
173
+
174
+ #: geopic_tag_reader/reader.py:722
175
+ #, python-brace-format
176
+ msgid ""
177
+ "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
178
+ "group)"
179
+ msgstr ""
180
+
181
+ #: geopic_tag_reader/reader.py:753
182
+ #, python-brace-format
183
+ msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
184
+ msgstr ""
185
+
186
+ #: geopic_tag_reader/reader.py:779
187
+ #, python-brace-format
188
+ msgid ""
189
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
190
+ "not matching value from EXIF field ({microseconds}). Max value will be kept."
191
+ msgstr ""
192
+
193
+ #: geopic_tag_reader/writer.py:132
194
+ #, python-brace-format
195
+ msgid "Unsupported key in additional tags ({k})"
196
+ msgstr ""
@@ -15,6 +15,7 @@ dependencies = [
15
15
  "pytz ~= 2025.2",
16
16
  "types-pytz ~= 2025.2.0",
17
17
  "types-python-dateutil ~= 2.9.0",
18
+ "rtree ~= 1.4.0",
18
19
  ]
19
20
  requires-python = ">=3.9"
20
21