geopic-tag-reader 1.2.0__tar.gz → 1.3.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 (39) hide show
  1. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/CHANGELOG.md +15 -1
  2. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/Makefile +1 -1
  3. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/PKG-INFO +6 -6
  4. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/README.md +5 -5
  5. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/__init__.py +1 -1
  6. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/main.py +4 -1
  7. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/reader.py +73 -31
  8. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/sequence.py +45 -4
  9. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  10. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/de/LC_MESSAGES/geopic_tag_reader.po +5 -1
  11. geopic_tag_reader-1.3.0/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  12. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.po +39 -27
  13. geopic_tag_reader-1.3.0/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  14. geopic_tag_reader-1.2.0/geopic_tag_reader/translations/geopic_tag_reader.pot → geopic_tag_reader-1.3.0/geopic_tag_reader/translations/fi/LC_MESSAGES/geopic_tag_reader.po +4 -5
  15. geopic_tag_reader-1.3.0/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  16. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.po +5 -1
  17. geopic_tag_reader-1.3.0/geopic_tag_reader/translations/geopic_tag_reader.pot +159 -0
  18. geopic_tag_reader-1.2.0/geopic_tag_reader/translations/en/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  19. geopic_tag_reader-1.2.0/geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  20. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/.gitignore +0 -0
  21. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/.gitlab-ci.yml +0 -0
  22. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/.pre-commit-config.yaml +0 -0
  23. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/CODE_OF_CONDUCT.md +0 -0
  24. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/LICENSE +0 -0
  25. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/docs/develop.md +0 -0
  26. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/docs/index.md +0 -0
  27. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/docs/install.md +0 -0
  28. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/docs/tech/api_reference.md +0 -0
  29. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/docs/tech/cli.md +0 -0
  30. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/camera.py +0 -0
  31. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/i18n.py +0 -0
  32. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/model.py +0 -0
  33. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/py.typed +0 -0
  34. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.mo +0 -0
  35. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/translations/es/LC_MESSAGES/geopic_tag_reader.po +0 -0
  36. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/geopic_tag_reader/writer.py +0 -0
  37. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/mkdocs.yml +0 -0
  38. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.0}/pyproject.toml +0 -0
  39. {geopic_tag_reader-1.2.0 → geopic_tag_reader-1.3.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.3.0] - 2024-09-30
11
+
12
+ ### Changed
13
+
14
+ - Reader offers a new property `ts_by_source` to distinguish read datetime from GPS and camera.
15
+ - Sequence sorting uses same timestamp source through all pictures (GPS if available, camera else, fallback with other value if two timestamps are identical).
16
+ - Sequence splits uses whenever possible same timestamp source.
17
+
18
+ ### Fixed
19
+
20
+ - Documentation links were not up-to-date in README file.
21
+ - Sub-seconds values and time offset are applied only if part of the same EXIF group.
22
+
10
23
  ## [1.2.0] - 2024-07-30
11
24
 
12
25
  ### Added
@@ -212,7 +225,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
212
225
 
213
226
  - EXIF tag reading methods extracted from [GeoVisio API](https://gitlab.com/panoramax/server/api)
214
227
 
215
- [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.2.0...main
228
+ [Unreleased]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.3.0...main
229
+ [1.3.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.2.0...1.3.0
216
230
  [1.2.0]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.1.5...1.2.0
217
231
  [1.1.5]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.1.4...1.1.5
218
232
  [1.1.4]: https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/compare/1.1.3...1.1.4
@@ -11,7 +11,7 @@ type-check: ## Check all python types
11
11
  mypy geopic_tag_reader/
12
12
 
13
13
  fmt: ## Format code
14
- black --fast .
14
+ python -m black --fast .
15
15
 
16
16
  ci: type-check fmt test ## Run all check like the ci
17
17
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geopic-tag-reader
3
- Version: 1.2.0
3
+ Version: 1.3.0
4
4
  Summary: GeoPicTagReader
5
5
  Author-email: Adrien PAVIE <panieravide@riseup.net>
6
6
  Requires-Python: >=3.8
@@ -31,7 +31,7 @@ Provides-Extra: write-exif
31
31
 
32
32
  # ![Panoramax](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Panoramax.svg/40px-Panoramax.svg.png) Panoramax
33
33
 
34
- __Panoramax__ is a digital resource for sharing and exploiting 📍📷 field photos. Anyone can take photographs of places visible from the public streets and contribute them to the Panoramax database. This data is then freely accessible and reusable by all. More information available at [gitlab.com/panoramax](https://gitlab.com/panoramax) and [panoramax.fr](https://panoramax.fr/).
34
+ __Panoramax__ is a digital resource for sharing and using 📍📷 field photos. Anyone can take photographs of places visible from the public streets and contribute them to the Panoramax database. This data is then freely accessible and reusable by all. More information available at [gitlab.com/panoramax](https://gitlab.com/panoramax) and [panoramax.fr](https://panoramax.fr/).
35
35
 
36
36
 
37
37
  # 📷 GeoPic Tag Reader
@@ -56,7 +56,7 @@ pip install geopic_tag_reader
56
56
  geopic-tag-reader --help
57
57
  ```
58
58
 
59
- To know more about install and other options, see [install documentation](./docs/Install.md).
59
+ To know more about install and other options, see [install documentation](./docs/install.md).
60
60
 
61
61
  If at some point you're lost or need help, you can contact us through [issues](https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/issues) or by [email](mailto:panieravide@riseup.net).
62
62
 
@@ -88,7 +88,7 @@ geopic-tag-reader write \
88
88
  --output /path/to/edited_image.jpg
89
89
  ```
90
90
 
91
- [Full documentation is also available here](./docs/CLI_USAGE.md).
91
+ [Full documentation is also available here](./docs/index.md).
92
92
 
93
93
  ### As Python library
94
94
 
@@ -119,14 +119,14 @@ editedImg.write(editedImgBytes)
119
119
  editedImg.close()
120
120
  ```
121
121
 
122
- [Full documentation is also available here](./docs/API_USAGE.md).
122
+ [Full documentation is also available here](./docs/tech/api_reference.md).
123
123
 
124
124
 
125
125
  ## Contributing
126
126
 
127
127
  Pull requests are welcome. For major changes, please open an [issue](https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/issues) first to discuss what you would like to change.
128
128
 
129
- More information about developing is available in [documentation](./docs/Develop.md).
129
+ More information about developing is available in [documentation](./docs/develop.md).
130
130
 
131
131
 
132
132
  ## ⚖️ License
@@ -1,6 +1,6 @@
1
1
  # ![Panoramax](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Panoramax.svg/40px-Panoramax.svg.png) Panoramax
2
2
 
3
- __Panoramax__ is a digital resource for sharing and exploiting 📍📷 field photos. Anyone can take photographs of places visible from the public streets and contribute them to the Panoramax database. This data is then freely accessible and reusable by all. More information available at [gitlab.com/panoramax](https://gitlab.com/panoramax) and [panoramax.fr](https://panoramax.fr/).
3
+ __Panoramax__ is a digital resource for sharing and using 📍📷 field photos. Anyone can take photographs of places visible from the public streets and contribute them to the Panoramax database. This data is then freely accessible and reusable by all. More information available at [gitlab.com/panoramax](https://gitlab.com/panoramax) and [panoramax.fr](https://panoramax.fr/).
4
4
 
5
5
 
6
6
  # 📷 GeoPic Tag Reader
@@ -25,7 +25,7 @@ pip install geopic_tag_reader
25
25
  geopic-tag-reader --help
26
26
  ```
27
27
 
28
- To know more about install and other options, see [install documentation](./docs/Install.md).
28
+ To know more about install and other options, see [install documentation](./docs/install.md).
29
29
 
30
30
  If at some point you're lost or need help, you can contact us through [issues](https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/issues) or by [email](mailto:panieravide@riseup.net).
31
31
 
@@ -57,7 +57,7 @@ geopic-tag-reader write \
57
57
  --output /path/to/edited_image.jpg
58
58
  ```
59
59
 
60
- [Full documentation is also available here](./docs/CLI_USAGE.md).
60
+ [Full documentation is also available here](./docs/index.md).
61
61
 
62
62
  ### As Python library
63
63
 
@@ -88,14 +88,14 @@ editedImg.write(editedImgBytes)
88
88
  editedImg.close()
89
89
  ```
90
90
 
91
- [Full documentation is also available here](./docs/API_USAGE.md).
91
+ [Full documentation is also available here](./docs/tech/api_reference.md).
92
92
 
93
93
 
94
94
  ## Contributing
95
95
 
96
96
  Pull requests are welcome. For major changes, please open an [issue](https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/issues) first to discuss what you would like to change.
97
97
 
98
- More information about developing is available in [documentation](./docs/Develop.md).
98
+ More information about developing is available in [documentation](./docs/develop.md).
99
99
 
100
100
 
101
101
  ## ⚖️ License
@@ -2,4 +2,4 @@
2
2
  GeoPicTagReader
3
3
  """
4
4
 
5
- __version__ = "1.2.0"
5
+ __version__ = "1.3.0"
@@ -25,7 +25,10 @@ def read(
25
25
  _ = i18n_init(lang)
26
26
  print(_("Latitude:"), metadata.lat)
27
27
  print(_("Longitude:"), metadata.lon)
28
- print(_("Timestamp:"), metadata.ts.isoformat())
28
+ print(_("Timestamp:"), metadata.ts)
29
+ if metadata.ts_by_source is not None:
30
+ print(" -", (metadata.ts_by_source.gps or _("not set")), _("(GPS)"))
31
+ print(" -", (metadata.ts_by_source.camera or _("not set")), _("(Camera)"))
29
32
  print(_("Heading:"), metadata.heading)
30
33
  print(_("Type:"), metadata.type)
31
34
  print(_("Make:"), metadata.make)
@@ -39,6 +39,32 @@ class CropValues:
39
39
  top: int
40
40
 
41
41
 
42
+ @dataclass
43
+ class TimeBySource:
44
+ """All datetimes read from available sources
45
+
46
+ Attributes:
47
+ gps (datetime): Time read from GPS clock
48
+ camera (datetime): Time read from camera clock (DateTimeOriginal)
49
+ """
50
+
51
+ gps: Optional[datetime.datetime] = None
52
+ camera: Optional[datetime.datetime] = None
53
+
54
+ def getBest(self) -> Optional[datetime.datetime]:
55
+ """Get the best available datetime to use"""
56
+ if self.gps is not None and self.camera is None:
57
+ return self.gps
58
+ elif self.gps is None and self.camera is not None:
59
+ return self.camera
60
+ elif self.gps is None and self.camera is None:
61
+ return None
62
+ elif self.camera.microsecond > 0 and self.gps.microsecond == 0: # type: ignore
63
+ return self.camera
64
+ else:
65
+ return self.gps
66
+
67
+
42
68
  @dataclass
43
69
  class GeoPicTags:
44
70
  """Tags associated to a geolocated picture
@@ -59,6 +85,7 @@ class GeoPicTags:
59
85
  pitch (float): Picture pitch angle, compared to horizon (in degrees, bottom = -90°, horizon = 0°, top = 90°)
60
86
  roll (float): Picture roll angle, on a right/left axis (in degrees, left-arm down = -90°, flat = 0°, right-arm down = 90°)
61
87
  yaw (float): Picture yaw angle, on a vertical axis (in degrees, front = 0°, right = 90°, rear = 180°, left = 270°). This offsets the center image from GPS direction for a correct 360° sphere correction
88
+ ts_by_source (TimeBySource): all read timestamps from image, for finer processing.
62
89
 
63
90
 
64
91
  Implementation note: this needs to be sync with the PartialGeoPicTags structure
@@ -79,6 +106,7 @@ class GeoPicTags:
79
106
  pitch: Optional[float] = None
80
107
  roll: Optional[float] = None
81
108
  yaw: Optional[float] = None
109
+ ts_by_source: Optional[TimeBySource] = None
82
110
 
83
111
 
84
112
  class InvalidExifException(Exception):
@@ -114,6 +142,7 @@ class PartialGeoPicTags:
114
142
  pitch: Optional[float] = None
115
143
  roll: Optional[float] = None
116
144
  yaw: Optional[float] = None
145
+ ts_by_source: Optional[TimeBySource] = None
117
146
 
118
147
 
119
148
  class PartialExifException(Exception):
@@ -187,35 +216,21 @@ def readPictureMetadata(picture: bytes, lang_code: str = "en") -> GeoPicTags:
187
216
  if lon is not None and (lon < -180 or lon > 180):
188
217
  raise InvalidExifException(_("Read longitude is out of WGS84 bounds (should be in [-180, 180])"))
189
218
 
190
- # Parse date/time
191
- d, llw = decodeGPSDateTime(data, "Exif.GPSInfo", _, lat, lon)
219
+ # Parse GPS date/time
220
+ gpsTs, llw = decodeGPSDateTime(data, "Exif.GPSInfo", _, lat, lon)
192
221
 
193
222
  if len(llw) > 0:
194
223
  warnings.extend(llw)
195
224
 
196
- if d is None:
197
- d, llw = decodeGPSDateTime(data, "Xmp.exif", _, lat, lon)
225
+ if gpsTs is None:
226
+ gpsTs, llw = decodeGPSDateTime(data, "Xmp.exif", _, lat, lon)
198
227
  if len(llw) > 0:
199
228
  warnings.extend(llw)
200
229
 
201
- for exifField in [
202
- "Exif.Image.DateTimeOriginal",
203
- "Exif.Photo.DateTimeOriginal",
204
- "Exif.Image.DateTime",
205
- "Xmp.GPano.SourceImageCreateTime",
206
- ]:
207
- if d is None:
208
- d, llw = decodeDateTimeOriginal(data, exifField, _, lat, lon)
209
- if len(llw) > 0:
210
- warnings.extend(llw)
211
-
212
- if d is not None:
213
- break
214
-
215
- if d is None and isExifTagUsable(data, "MAPGpsTime"):
230
+ if gpsTs is None and isExifTagUsable(data, "MAPGpsTime"):
216
231
  try:
217
232
  year, month, day, hour, minutes, seconds, milliseconds = [int(dp) for dp in data["MAPGpsTime"].split("_")]
218
- d = datetime.datetime(
233
+ gpsTs = datetime.datetime(
219
234
  year,
220
235
  month,
221
236
  day,
@@ -229,6 +244,25 @@ def readPictureMetadata(picture: bytes, lang_code: str = "en") -> GeoPicTags:
229
244
  except Exception as e:
230
245
  warnings.append(_("Skipping Mapillary date/time as it was not recognized: {v}").format(v=data["MAPGpsTime"]))
231
246
 
247
+ # Parse camera date/time
248
+ cameraTs = None
249
+ for exifGroup, dtField, subsecField in [
250
+ ("Exif.Photo", "DateTimeOriginal", "SubSecTimeOriginal"),
251
+ ("Exif.Image", "DateTimeOriginal", "SubSecTimeOriginal"),
252
+ ("Exif.Image", "DateTime", "SubSecTimeOriginal"),
253
+ ("Xmp.GPano", "SourceImageCreateTime", "SubSecTimeOriginal"),
254
+ ("Xmp.exif", "DateTimeOriginal", "SubsecTimeOriginal"), # Case matters
255
+ ]:
256
+ if cameraTs is None:
257
+ cameraTs, llw = decodeDateTimeOriginal(data, exifGroup, dtField, subsecField, _, lat, lon)
258
+ if len(llw) > 0:
259
+ warnings.extend(llw)
260
+
261
+ if cameraTs is not None:
262
+ break
263
+ tsSources = TimeBySource(gps=gpsTs, camera=cameraTs) if gpsTs or cameraTs else None
264
+ d = tsSources.getBest() if tsSources is not None else None
265
+
232
266
  # GPS Heading
233
267
  heading = None
234
268
  if isExifTagUsable(data, "Exif.GPSInfo.GPSImgDirection", Fraction):
@@ -370,6 +404,7 @@ def readPictureMetadata(picture: bytes, lang_code: str = "en") -> GeoPicTags:
370
404
  pitch=pitch,
371
405
  roll=roll,
372
406
  yaw=yaw,
407
+ ts_by_source=tsSources,
373
408
  ),
374
409
  )
375
410
 
@@ -390,6 +425,7 @@ def readPictureMetadata(picture: bytes, lang_code: str = "en") -> GeoPicTags:
390
425
  pitch=pitch,
391
426
  roll=roll,
392
427
  yaw=yaw,
428
+ ts_by_source=tsSources,
393
429
  )
394
430
 
395
431
 
@@ -480,20 +516,28 @@ def decodeLatLon(data: dict, group: str, _: Callable[[str], str]) -> Tuple[Optio
480
516
 
481
517
 
482
518
  def decodeDateTimeOriginal(
483
- data: dict, datetimeField: str, _: Callable[[str], str], lat: Optional[float] = None, lon: Optional[float] = None
519
+ data: dict,
520
+ exifGroup: str,
521
+ datetimeField: str,
522
+ subsecField: str,
523
+ _: Callable[[str], str],
524
+ lat: Optional[float] = None,
525
+ lon: Optional[float] = None,
484
526
  ) -> Tuple[Optional[datetime.datetime], List[str]]:
485
527
  d = None
486
528
  warnings = []
529
+ dtField = f"{exifGroup}.{datetimeField}"
530
+ ssField = f"{exifGroup}.{subsecField}"
487
531
 
488
- if d is None and isExifTagUsable(data, datetimeField):
532
+ if d is None and isExifTagUsable(data, dtField):
489
533
  try:
490
- dateRaw = data[datetimeField][:10].replace(":", "-")
491
- timeRaw = data[datetimeField][11:].split(":")
534
+ dateRaw = data[dtField][:10].replace(":", "-")
535
+ timeRaw = data[dtField][11:].split(":")
492
536
  hourRaw = int(timeRaw[0])
493
537
  minutesRaw = int(timeRaw[1])
494
538
  secondsRaw, microsecondsRaw, msw = decodeSecondsAndMicroSeconds(
495
- timeRaw[2],
496
- data["Exif.Photo.SubSecTimeOriginal"] if isExifTagUsable(data, "Exif.Photo.SubSecTimeOriginal", float) else "0",
539
+ timeRaw[2] if len(timeRaw) >= 3 else "0",
540
+ data[ssField] if isExifTagUsable(data, ssField, float) else "0",
497
541
  _,
498
542
  )
499
543
  warnings += msw
@@ -510,7 +554,7 @@ def decodeDateTimeOriginal(
510
554
 
511
555
  # Timezone handling
512
556
  # Try to read from EXIF
513
- tz = decodeTimeOffset(data, f"Exif.Photo.OffsetTime{'Original' if 'DateTimeOriginal' in datetimeField else ''}")
557
+ tz = decodeTimeOffset(data, f"{exifGroup}.OffsetTime{'Original' if 'DateTimeOriginal' in dtField else ''}")
514
558
  if tz is not None:
515
559
  d = d.replace(tzinfo=tz)
516
560
 
@@ -531,9 +575,7 @@ def decodeDateTimeOriginal(
531
575
 
532
576
  except ValueError as e:
533
577
  warnings.append(
534
- _("Skipping original date/time (from {datefield}) as it was not recognized: {v}").format(
535
- datefield=datetimeField, v=data[datetimeField]
536
- )
578
+ _("Skipping original date/time (from {datefield}) as it was not recognized: {v}").format(datefield=dtField, v=data[dtField])
537
579
  )
538
580
 
539
581
  return (d, warnings)
@@ -571,7 +613,7 @@ def decodeGPSDateTime(
571
613
  if timeRaw:
572
614
  seconds, microseconds, msw = decodeSecondsAndMicroSeconds(
573
615
  str(float(timeRaw[2])),
574
- data["Exif.Photo.SubSecTimeOriginal"] if isExifTagUsable(data, "Exif.Photo.SubSecTimeOriginal", float) else "0",
616
+ "0", # No SubSecTimeOriginal, it's only for DateTimeOriginal
575
617
  _,
576
618
  )
577
619
 
@@ -143,7 +143,35 @@ def sort_pictures(pictures: List[Picture], method: Optional[SortMethod] = SortMe
143
143
 
144
144
  # Sort based on picture ts
145
145
  elif strat == "time":
146
- pictures.sort(key=lambda p: p.metadata.ts.isoformat() if p.metadata is not None else "0000-00-00T00:00:00Z")
146
+ # Check if all pictures have GPS ts set
147
+ missingGpsTs = next(
148
+ (p for p in pictures if p.metadata is None or p.metadata.ts_by_source is None or p.metadata.ts_by_source.gps is None), None
149
+ )
150
+ if missingGpsTs:
151
+ # Check if all pictures have camera ts set
152
+ missingCamTs = next(
153
+ (p for p in pictures if p.metadata is None or p.metadata.ts_by_source is None or p.metadata.ts_by_source.camera is None),
154
+ None,
155
+ )
156
+ # Sort by best ts available
157
+ if missingCamTs:
158
+ pictures.sort(key=lambda p: p.metadata.ts.isoformat() if p.metadata is not None else "0000-00-00T00:00:00Z")
159
+ # Sort by camera ts
160
+ else:
161
+ pictures.sort(
162
+ key=lambda p: (
163
+ p.metadata.ts_by_source.camera.isoformat(), # type: ignore
164
+ p.metadata.ts_by_source.gps.isoformat() if p.metadata.ts_by_source.gps else "0000-00-00T00:00:00Z", # type: ignore
165
+ )
166
+ )
167
+ # Sort by GPS ts
168
+ else:
169
+ pictures.sort(
170
+ key=lambda p: (
171
+ p.metadata.ts_by_source.gps.isoformat(), # type: ignore
172
+ p.metadata.ts_by_source.camera.isoformat() if p.metadata.ts_by_source.camera else "0000-00-00T00:00:00Z", # type: ignore
173
+ )
174
+ )
147
175
 
148
176
  if order == "desc":
149
177
  pictures.reverse()
@@ -244,9 +272,22 @@ def split_in_sequences(pictures: List[Picture], splitParams: Optional[SplitParam
244
272
  continue
245
273
 
246
274
  # Time delta
247
- timeOutOfDelta = (
248
- False if splitParams.maxTime is None else (abs(lastPic.metadata.ts - pic.metadata.ts)).total_seconds() > splitParams.maxTime
249
- )
275
+ timeDelta = lastPic.metadata.ts - pic.metadata.ts
276
+ if (
277
+ lastPic.metadata.ts_by_source
278
+ and lastPic.metadata.ts_by_source.gps
279
+ and pic.metadata.ts_by_source
280
+ and pic.metadata.ts_by_source.gps
281
+ ):
282
+ timeDelta = lastPic.metadata.ts_by_source.gps - pic.metadata.ts_by_source.gps
283
+ elif (
284
+ lastPic.metadata.ts_by_source
285
+ and lastPic.metadata.ts_by_source.camera
286
+ and pic.metadata.ts_by_source
287
+ and pic.metadata.ts_by_source.camera
288
+ ):
289
+ timeDelta = lastPic.metadata.ts_by_source.camera - pic.metadata.ts_by_source.camera
290
+ timeOutOfDelta = False if splitParams.maxTime is None else (abs(timeDelta)).total_seconds() > splitParams.maxTime
250
291
 
251
292
  # Distance delta
252
293
  distance = lastPic.distance_to(pic)
@@ -8,7 +8,7 @@ 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: 2024-07-13 20:42+0000\n"
11
+ "PO-Revision-Date: 2024-08-07 13:42+0000\n"
12
12
  "Last-Translator: Bastian Greshake Tzovaras <bastian@gedankenstuecke.de>\n"
13
13
  "Language-Team: German <http://weblate.panoramax.xyz/projects/panoramax/"
14
14
  "tag-reader/de/>\n"
@@ -163,3 +163,7 @@ msgstr ""
163
163
  #, python-brace-format
164
164
  msgid "Unsupported key in additional tags ({k})"
165
165
  msgstr "Nicht unterstützter Schlüssel in den zusätzlichen Attributen ({k})"
166
+
167
+ #: geopic_tag_reader/main.py:37
168
+ msgid "Yaw:"
169
+ msgstr "Gierwinkel:"
@@ -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: 2024-07-30 16:49+0200\n"
11
- "PO-Revision-Date: 2024-07-30 16:49+0200\n"
10
+ "POT-Creation-Date: 2024-09-30 11:02+0200\n"
11
+ "PO-Revision-Date: 2024-09-30 11:02+0200\n"
12
12
  "Last-Translator: Automatically generated\n"
13
13
  "Language-Team: none\n"
14
14
  "Language: en\n"
@@ -29,88 +29,100 @@ msgstr "Longitude:"
29
29
  msgid "Timestamp:"
30
30
  msgstr "Timestamp:"
31
31
 
32
- #: geopic_tag_reader/main.py:29
32
+ #: geopic_tag_reader/main.py:30 geopic_tag_reader/main.py:31
33
+ msgid "not set"
34
+ msgstr "not set"
35
+
36
+ #: geopic_tag_reader/main.py:30
37
+ msgid "(GPS)"
38
+ msgstr "(GPS)"
39
+
40
+ #: geopic_tag_reader/main.py:31
41
+ msgid "(Camera)"
42
+ msgstr "(Camera)"
43
+
44
+ #: geopic_tag_reader/main.py:32
33
45
  msgid "Heading:"
34
46
  msgstr "Heading:"
35
47
 
36
- #: geopic_tag_reader/main.py:30
48
+ #: geopic_tag_reader/main.py:33
37
49
  msgid "Type:"
38
50
  msgstr "Type:"
39
51
 
40
- #: geopic_tag_reader/main.py:31
52
+ #: geopic_tag_reader/main.py:34
41
53
  msgid "Make:"
42
54
  msgstr "Make:"
43
55
 
44
- #: geopic_tag_reader/main.py:32
56
+ #: geopic_tag_reader/main.py:35
45
57
  msgid "Model:"
46
58
  msgstr "Model:"
47
59
 
48
- #: geopic_tag_reader/main.py:33
60
+ #: geopic_tag_reader/main.py:36
49
61
  msgid "Focal length:"
50
62
  msgstr "Focal length:"
51
63
 
52
- #: geopic_tag_reader/main.py:34
64
+ #: geopic_tag_reader/main.py:37
53
65
  msgid "Crop parameters:"
54
66
  msgstr "Crop parameters:"
55
67
 
56
- #: geopic_tag_reader/main.py:35
68
+ #: geopic_tag_reader/main.py:38
57
69
  msgid "Pitch:"
58
70
  msgstr "Pitch:"
59
71
 
60
- #: geopic_tag_reader/main.py:36
72
+ #: geopic_tag_reader/main.py:39
61
73
  msgid "Roll:"
62
74
  msgstr "Roll:"
63
75
 
64
- #: geopic_tag_reader/main.py:37
76
+ #: geopic_tag_reader/main.py:40
65
77
  msgid "Yaw:"
66
78
  msgstr "Yaw:"
67
79
 
68
- #: geopic_tag_reader/main.py:40
80
+ #: geopic_tag_reader/main.py:43
69
81
  msgid "Warnings raised by reader:"
70
82
  msgstr "Warnings raised by reader:"
71
83
 
72
- #: geopic_tag_reader/reader.py:186
84
+ #: geopic_tag_reader/reader.py:215
73
85
  msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
74
86
  msgstr "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
75
87
 
76
- #: geopic_tag_reader/reader.py:188
88
+ #: geopic_tag_reader/reader.py:217
77
89
  msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
78
90
  msgstr "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
79
91
 
80
- #: geopic_tag_reader/reader.py:230
92
+ #: geopic_tag_reader/reader.py:245
81
93
  #, python-brace-format
82
94
  msgid "Skipping Mapillary date/time as it was not recognized: {v}"
83
95
  msgstr "Skipping Mapillary date/time as it was not recognized: {v}"
84
96
 
85
- #: geopic_tag_reader/reader.py:337
97
+ #: geopic_tag_reader/reader.py:371
86
98
  msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
87
99
  msgstr "No GPS coordinates or broken coordinates in picture EXIF tags"
88
100
 
89
- #: geopic_tag_reader/reader.py:343
101
+ #: geopic_tag_reader/reader.py:377
90
102
  msgid "No valid date in picture EXIF tags"
91
103
  msgstr "No valid date in picture EXIF tags"
92
104
 
93
- #: geopic_tag_reader/reader.py:348
105
+ #: geopic_tag_reader/reader.py:382
94
106
  msgid "The picture is missing mandatory metadata:"
95
107
  msgstr "The picture is missing mandatory metadata:"
96
108
 
97
- #: geopic_tag_reader/reader.py:437 geopic_tag_reader/reader.py:466
109
+ #: geopic_tag_reader/reader.py:473 geopic_tag_reader/reader.py:502
98
110
  msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
99
111
  msgstr "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
100
112
 
101
- #: geopic_tag_reader/reader.py:445
113
+ #: geopic_tag_reader/reader.py:481
102
114
  msgid "Broken GPS coordinates in picture EXIF tags"
103
115
  msgstr "Broken GPS coordinates in picture EXIF tags"
104
116
 
105
- #: geopic_tag_reader/reader.py:448 geopic_tag_reader/reader.py:472
117
+ #: geopic_tag_reader/reader.py:484 geopic_tag_reader/reader.py:508
106
118
  msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
107
119
  msgstr "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
108
120
 
109
- #: geopic_tag_reader/reader.py:525
121
+ #: geopic_tag_reader/reader.py:569
110
122
  msgid "Precise timezone information not found, fallback to UTC"
111
123
  msgstr "Precise timezone information not found, fallback to UTC"
112
124
 
113
- #: geopic_tag_reader/reader.py:530
125
+ #: geopic_tag_reader/reader.py:574
114
126
  msgid ""
115
127
  "Precise timezone information not found (and no GPS coordinates to help), "
116
128
  "fallback to UTC"
@@ -118,14 +130,14 @@ msgstr ""
118
130
  "Precise timezone information not found (and no GPS coordinates to help), "
119
131
  "fallback to UTC"
120
132
 
121
- #: geopic_tag_reader/reader.py:534
133
+ #: geopic_tag_reader/reader.py:578
122
134
  #, python-brace-format
123
135
  msgid ""
124
136
  "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
125
137
  msgstr ""
126
138
  "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
127
139
 
128
- #: geopic_tag_reader/reader.py:568
140
+ #: geopic_tag_reader/reader.py:610
129
141
  #, python-brace-format
130
142
  msgid ""
131
143
  "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
@@ -134,12 +146,12 @@ msgstr ""
134
146
  "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
135
147
  "group)"
136
148
 
137
- #: geopic_tag_reader/reader.py:599
149
+ #: geopic_tag_reader/reader.py:641
138
150
  #, python-brace-format
139
151
  msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
140
152
  msgstr "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
141
153
 
142
- #: geopic_tag_reader/reader.py:625
154
+ #: geopic_tag_reader/reader.py:667
143
155
  #, python-brace-format
144
156
  msgid ""
145
157
  "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
@@ -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: 2024-07-30 16:49+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: fi\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,7 +8,7 @@ msgstr ""
8
8
  "Project-Id-Version: PACKAGE VERSION\n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
10
  "POT-Creation-Date: 2024-06-18 09:12+0200\n"
11
- "PO-Revision-Date: 2024-07-10 08:29+0000\n"
11
+ "PO-Revision-Date: 2024-08-29 10:07+0000\n"
12
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"
@@ -188,3 +188,7 @@ msgid ""
188
188
  msgstr ""
189
189
  "Date/heure originales (venant de {datefield}) ignorées car non-reconnues : "
190
190
  "{v}"
191
+
192
+ #: geopic_tag_reader/main.py:37
193
+ msgid "Yaw:"
194
+ msgstr "Lacet :"
@@ -0,0 +1,159 @@
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: 2024-09-30 11:02+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 "Timestamp:"
30
+ msgstr ""
31
+
32
+ #: geopic_tag_reader/main.py:30 geopic_tag_reader/main.py:31
33
+ msgid "not set"
34
+ msgstr ""
35
+
36
+ #: geopic_tag_reader/main.py:30
37
+ msgid "(GPS)"
38
+ msgstr ""
39
+
40
+ #: geopic_tag_reader/main.py:31
41
+ msgid "(Camera)"
42
+ msgstr ""
43
+
44
+ #: geopic_tag_reader/main.py:32
45
+ msgid "Heading:"
46
+ msgstr ""
47
+
48
+ #: geopic_tag_reader/main.py:33
49
+ msgid "Type:"
50
+ msgstr ""
51
+
52
+ #: geopic_tag_reader/main.py:34
53
+ msgid "Make:"
54
+ msgstr ""
55
+
56
+ #: geopic_tag_reader/main.py:35
57
+ msgid "Model:"
58
+ msgstr ""
59
+
60
+ #: geopic_tag_reader/main.py:36
61
+ msgid "Focal length:"
62
+ msgstr ""
63
+
64
+ #: geopic_tag_reader/main.py:37
65
+ msgid "Crop parameters:"
66
+ msgstr ""
67
+
68
+ #: geopic_tag_reader/main.py:38
69
+ msgid "Pitch:"
70
+ msgstr ""
71
+
72
+ #: geopic_tag_reader/main.py:39
73
+ msgid "Roll:"
74
+ msgstr ""
75
+
76
+ #: geopic_tag_reader/main.py:40
77
+ msgid "Yaw:"
78
+ msgstr ""
79
+
80
+ #: geopic_tag_reader/main.py:43
81
+ msgid "Warnings raised by reader:"
82
+ msgstr ""
83
+
84
+ #: geopic_tag_reader/reader.py:215
85
+ msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
86
+ msgstr ""
87
+
88
+ #: geopic_tag_reader/reader.py:217
89
+ msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
90
+ msgstr ""
91
+
92
+ #: geopic_tag_reader/reader.py:245
93
+ #, python-brace-format
94
+ msgid "Skipping Mapillary date/time as it was not recognized: {v}"
95
+ msgstr ""
96
+
97
+ #: geopic_tag_reader/reader.py:371
98
+ msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
99
+ msgstr ""
100
+
101
+ #: geopic_tag_reader/reader.py:377
102
+ msgid "No valid date in picture EXIF tags"
103
+ msgstr ""
104
+
105
+ #: geopic_tag_reader/reader.py:382
106
+ msgid "The picture is missing mandatory metadata:"
107
+ msgstr ""
108
+
109
+ #: geopic_tag_reader/reader.py:473 geopic_tag_reader/reader.py:502
110
+ msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
111
+ msgstr ""
112
+
113
+ #: geopic_tag_reader/reader.py:481
114
+ msgid "Broken GPS coordinates in picture EXIF tags"
115
+ msgstr ""
116
+
117
+ #: geopic_tag_reader/reader.py:484 geopic_tag_reader/reader.py:508
118
+ msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
119
+ msgstr ""
120
+
121
+ #: geopic_tag_reader/reader.py:569
122
+ msgid "Precise timezone information not found, fallback to UTC"
123
+ msgstr ""
124
+
125
+ #: geopic_tag_reader/reader.py:574
126
+ msgid ""
127
+ "Precise timezone information not found (and no GPS coordinates to help), "
128
+ "fallback to UTC"
129
+ msgstr ""
130
+
131
+ #: geopic_tag_reader/reader.py:578
132
+ #, python-brace-format
133
+ msgid ""
134
+ "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
135
+ msgstr ""
136
+
137
+ #: geopic_tag_reader/reader.py:610
138
+ #, python-brace-format
139
+ msgid ""
140
+ "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
141
+ "group)"
142
+ msgstr ""
143
+
144
+ #: geopic_tag_reader/reader.py:641
145
+ #, python-brace-format
146
+ msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
147
+ msgstr ""
148
+
149
+ #: geopic_tag_reader/reader.py:667
150
+ #, python-brace-format
151
+ msgid ""
152
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
153
+ "not matching value from EXIF field ({microseconds}). Max value will be kept."
154
+ msgstr ""
155
+
156
+ #: geopic_tag_reader/writer.py:132
157
+ #, python-brace-format
158
+ msgid "Unsupported key in additional tags ({k})"
159
+ msgstr ""