geopic-tag-reader 1.1.0__py3-none-any.whl → 1.1.2__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.
@@ -2,4 +2,4 @@
2
2
  GeoPicTagReader
3
3
  """
4
4
 
5
- __version__ = "1.1.0"
5
+ __version__ = "1.1.2"
@@ -39,11 +39,22 @@ def is_360(make: Optional[str] = None, model: Optional[str] = None, width: Optio
39
39
  True
40
40
  >>> is_360("GoPro", "Max 360", "1024", "768")
41
41
  False
42
+ >>> is_360("RICOH", "THETA S", "5376", "2688")
43
+ True
42
44
  """
43
45
 
46
+ # Check make and model are defined
44
47
  if not make or not model:
45
48
  return False
46
49
 
47
- return any(model.startswith(potentialModel) for potentialModel in EQUIRECTANGULAR_MODELS.get(make, [])) and (
48
- (width is None or height is None) or int(width) == 2 * int(height)
49
- )
50
+ # Check width and height are equirectangular
51
+ if not ((width is None or height is None) or int(width) == 2 * int(height)):
52
+ return False
53
+
54
+ # Find make
55
+ matchMake = next((m for m in EQUIRECTANGULAR_MODELS.keys() if make.lower() == m.lower()), None)
56
+ if matchMake is None:
57
+ return False
58
+
59
+ # Find model
60
+ return any(model.lower().startswith(m.lower()) for m in EQUIRECTANGULAR_MODELS[matchMake])
@@ -0,0 +1,8 @@
1
+ import gettext
2
+ import os
3
+
4
+ lang_code = globals().get("LANG") or os.getenv("LANG") or "en"
5
+ localedir = os.path.join(os.path.abspath(os.path.dirname(__file__)), "translations")
6
+ lang = gettext.translation("geopic_tag_reader", localedir, languages=[lang_code], fallback=True)
7
+ lang.install()
8
+ _ = lang.gettext
geopic_tag_reader/main.py CHANGED
@@ -29,6 +29,8 @@ def read(
29
29
  print("Model:", metadata.model)
30
30
  print("Focal length:", metadata.focal_length)
31
31
  print("Crop parameters:", metadata.crop)
32
+ print("Pitch:", metadata.pitch)
33
+ print("Roll:", metadata.roll)
32
34
 
33
35
  if len(metadata.tagreader_warnings) > 0:
34
36
  print("Warnings raised by reader:")
@@ -9,6 +9,7 @@ from fractions import Fraction
9
9
  from geopic_tag_reader import camera
10
10
  import timezonefinder # type: ignore
11
11
  import pytz
12
+ from geopic_tag_reader.i18n import _
12
13
 
13
14
  # This is a fix for invalid MakerNotes leading to picture not read at all
14
15
  # https://github.com/LeoHsiao1/pyexiv2/issues/58
@@ -46,7 +47,7 @@ class GeoPicTags:
46
47
  lat (float): GPS Latitude (in WGS84)
47
48
  lon (float): GPS Longitude (in WGS84)
48
49
  ts (datetime): The capture date (date & time with timezone)
49
- heading (int): Picture heading (in degrees, North = 0°, East = 90°, South = 180°, West = 270°)
50
+ heading (int): Picture heading/yaw (in degrees, North = 0°, East = 90°, South = 180°, West = 270°)
50
51
  type (str): The kind of picture (flat, equirectangular)
51
52
  make (str): The camera manufacturer name
52
53
  model (str): The camera model name
@@ -55,6 +56,8 @@ class GeoPicTags:
55
56
  exif (dict[str, str]): Raw EXIF tags from picture (following Exiv2 naming scheme, see https://exiv2.org/metadata.html)
56
57
  tagreader_warnings (list[str]): List of thrown warnings during metadata reading
57
58
  altitude (float): altitude (in m) (optional)
59
+ pitch (float): Picture pitch angle, compared to horizon (in degrees, bottom = -90°, horizon = 0°, top = 90°)
60
+ roll (float): Picture roll angle, on a right/left axis (in degrees, left-arm down = -90°, flat = 0°, right-arm down = 90°)
58
61
 
59
62
 
60
63
  Implementation note: this needs to be sync with the PartialGeoPicTags structure
@@ -72,6 +75,8 @@ class GeoPicTags:
72
75
  exif: Dict[str, str] = field(default_factory=lambda: {})
73
76
  tagreader_warnings: List[str] = field(default_factory=lambda: [])
74
77
  altitude: Optional[float] = None
78
+ pitch: Optional[float] = None
79
+ roll: Optional[float] = None
75
80
 
76
81
 
77
82
  class InvalidExifException(Exception):
@@ -100,6 +105,8 @@ class PartialGeoPicTags:
100
105
  exif: Dict[str, str] = field(default_factory=lambda: {})
101
106
  tagreader_warnings: List[str] = field(default_factory=lambda: [])
102
107
  altitude: Optional[float] = None
108
+ pitch: Optional[float] = None
109
+ roll: Optional[float] = None
103
110
 
104
111
 
105
112
  class PartialExifException(Exception):
@@ -167,9 +174,9 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
167
174
 
168
175
  # Check coordinates validity
169
176
  if lat is not None and (lat < -90 or lat > 90):
170
- raise InvalidExifException("Read latitude is out of WGS84 bounds (should be in [-90, 90])")
177
+ raise InvalidExifException(_("Read latitude is out of WGS84 bounds (should be in [-90, 90])"))
171
178
  if lon is not None and (lon < -180 or lon > 180):
172
- raise InvalidExifException("Read longitude is out of WGS84 bounds (should be in [-180, 180])")
179
+ raise InvalidExifException(_("Read longitude is out of WGS84 bounds (should be in [-180, 180])"))
173
180
 
174
181
  # Parse date/time
175
182
  d, llw = decodeGPSDateTime(data, "Exif.GPSInfo", lat, lon)
@@ -211,9 +218,9 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
211
218
  )
212
219
 
213
220
  except Exception as e:
214
- warnings.append("Skipping Mapillary date/time as it was not recognized:\n\t" + str(e))
221
+ warnings.append(_("Skipping Mapillary date/time as it was not recognized: {v}").format(v=data["MAPGpsTime"]))
215
222
 
216
- # Heading
223
+ # Heading/Yaw
217
224
  heading = None
218
225
  if isExifTagUsable(data, "Xmp.GPano.PoseHeadingDegrees", float) and isExifTagUsable(data, "Exif.GPSInfo.GPSImgDirection", Fraction):
219
226
  gpsDir = int(round(float(Fraction(data["Exif.GPSInfo.GPSImgDirection"]))))
@@ -224,7 +231,7 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
224
231
  heading = gpanoHeading
225
232
  else:
226
233
  if gpsDir != gpanoHeading:
227
- warnings.append("Contradicting heading values found, GPSImgDirection value is used")
234
+ warnings.append(_("Contradicting heading values found, GPSImgDirection value is used"))
228
235
  heading = gpsDir
229
236
 
230
237
  elif isExifTagUsable(data, "Xmp.GPano.PoseHeadingDegrees", float):
@@ -236,6 +243,35 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
236
243
  elif "MAPCompassHeading" in data and isExifTagUsable(data["MAPCompassHeading"], "TrueHeading", float):
237
244
  heading = int(round(float(data["MAPCompassHeading"]["TrueHeading"])))
238
245
 
246
+ # Pitch & roll
247
+ pitch = None
248
+ roll = None
249
+ exifPRFields = ["Xmp.Camera.$$", "Exif.GPSInfo.GPS$$", "Xmp.GPano.Pose$$Degrees", "Xmp.GPano.InitialView$$Degrees"]
250
+ # For each potential EXIF field
251
+ for exifField in exifPRFields:
252
+ # Try out both Pitch & Roll variants
253
+ for checkField in ["Pitch", "Roll"]:
254
+ exifCheckField = exifField.replace("$$", checkField)
255
+ foundValue = None
256
+ # Look for float or fraction
257
+ if isExifTagUsable(data, exifCheckField, float):
258
+ foundValue = float(data[exifCheckField])
259
+ elif isExifTagUsable(data, exifCheckField, Fraction):
260
+ foundValue = float(Fraction(data[exifCheckField]))
261
+
262
+ # Save to correct variable (if not already set)
263
+ if foundValue is not None:
264
+ if checkField == "Pitch":
265
+ if pitch is None:
266
+ pitch = foundValue
267
+ else:
268
+ continue
269
+ elif checkField == "Roll":
270
+ if roll is None:
271
+ roll = foundValue
272
+ else:
273
+ continue
274
+
239
275
  # Make and model
240
276
  make = data.get("Exif.Image.Make") or data.get("MAPDeviceMake")
241
277
  model = data.get("Exif.Image.Model") or data.get("MAPDeviceModel")
@@ -306,18 +342,25 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
306
342
  errors = []
307
343
  missing_fields = set()
308
344
  if not lat or not lon:
309
- errors.append("No GPS coordinates or broken coordinates in picture EXIF tags")
345
+ errors.append(_("No GPS coordinates or broken coordinates in picture EXIF tags"))
310
346
  if not lat:
311
347
  missing_fields.add("lat")
312
348
  if not lon:
313
349
  missing_fields.add("lon")
314
350
  if d is None:
315
- errors.append("No valid date in picture EXIF tags")
351
+ errors.append(_("No valid date in picture EXIF tags"))
316
352
  missing_fields.add("datetime")
317
353
 
318
354
  if errors:
355
+ if len(errors) > 1:
356
+ listOfErrors = _("The picture is missing mandatory metadata:")
357
+ errorSep = "\n\t- "
358
+ listOfErrors += errorSep + errorSep.join(errors)
359
+ else:
360
+ listOfErrors = errors[0]
361
+
319
362
  raise PartialExifException(
320
- " and ".join(errors),
363
+ listOfErrors,
321
364
  missing_fields,
322
365
  PartialGeoPicTags(
323
366
  lat,
@@ -332,6 +375,8 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
332
375
  exif=data,
333
376
  tagreader_warnings=warnings,
334
377
  altitude=altitude,
378
+ pitch=pitch,
379
+ roll=roll,
335
380
  ),
336
381
  )
337
382
 
@@ -349,6 +394,8 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
349
394
  exif=data,
350
395
  tagreader_warnings=warnings,
351
396
  altitude=altitude,
397
+ pitch=pitch,
398
+ roll=roll,
352
399
  )
353
400
 
354
401
 
@@ -380,7 +427,7 @@ def decodeManyFractions(value: str) -> List[Fraction]:
380
427
  return vals
381
428
 
382
429
  except:
383
- raise ValueError("Not a valid list of fractions")
430
+ raise ValueError(_("Not a valid list of fractions"))
384
431
 
385
432
 
386
433
  def decodeLatLon(data: dict, group: str) -> Tuple[Optional[float], Optional[float], List[str]]:
@@ -393,7 +440,7 @@ def decodeLatLon(data: dict, group: str) -> Tuple[Optional[float], Optional[floa
393
440
  latRaw = decodeManyFractions(data[f"{group}.GPSLatitude"])
394
441
  if len(latRaw) == 3:
395
442
  if not isExifTagUsable(data, f"{group}.GPSLatitudeRef"):
396
- warnings.append("GPSLatitudeRef not found, assuming GPSLatitudeRef is North")
443
+ warnings.append(_("GPSLatitudeRef not found, assuming GPSLatitudeRef is North"))
397
444
  latRef = 1
398
445
  else:
399
446
  latRef = -1 if data[f"{group}.GPSLatitudeRef"].startswith("S") else 1
@@ -401,10 +448,10 @@ def decodeLatLon(data: dict, group: str) -> Tuple[Optional[float], Optional[floa
401
448
 
402
449
  lonRaw = decodeManyFractions(data[f"{group}.GPSLongitude"])
403
450
  if len(lonRaw) != 3:
404
- raise InvalidExifException("Broken GPS coordinates in picture EXIF tags")
451
+ raise InvalidExifException(_("Broken GPS coordinates in picture EXIF tags"))
405
452
 
406
453
  if not isExifTagUsable(data, f"{group}.GPSLongitudeRef"):
407
- warnings.append("GPSLongitudeRef not found, assuming GPSLongitudeRef is East")
454
+ warnings.append(_("GPSLongitudeRef not found, assuming GPSLongitudeRef is East"))
408
455
  lonRef = 1
409
456
  else:
410
457
  lonRef = -1 if data[f"{group}.GPSLongitudeRef"].startswith("W") else 1
@@ -422,13 +469,13 @@ def decodeLatLon(data: dict, group: str) -> Tuple[Optional[float], Optional[floa
422
469
  if rawLat and rawLon:
423
470
  latRef = 1
424
471
  if not isExifTagUsable(data, f"{group}.GPSLatitudeRef"):
425
- warnings.append("GPSLatitudeRef not found, assuming GPSLatitudeRef is North")
472
+ warnings.append(_("GPSLatitudeRef not found, assuming GPSLatitudeRef is North"))
426
473
  else:
427
474
  latRef = -1 if data[f"{group}.GPSLatitudeRef"].startswith("S") else 1
428
475
 
429
476
  lonRef = 1
430
477
  if not isExifTagUsable(data, f"{group}.GPSLongitudeRef"):
431
- warnings.append("GPSLongitudeRef not found, assuming GPSLongitudeRef is East")
478
+ warnings.append(_("GPSLongitudeRef not found, assuming GPSLongitudeRef is East"))
432
479
  else:
433
480
  lonRef = -1 if data[f"{group}.GPSLongitudeRef"].startswith("W") else 1
434
481
 
@@ -479,15 +526,19 @@ def decodeDateTimeOriginal(
479
526
  # Otherwise, default to UTC + warning
480
527
  else:
481
528
  d = d.replace(tzinfo=datetime.timezone.utc)
482
- warnings.append("Precise timezone information not found, fallback to UTC")
529
+ warnings.append(_("Precise timezone information not found, fallback to UTC"))
483
530
 
484
531
  # Otherwise, default to UTC + warning
485
532
  else:
486
533
  d = d.replace(tzinfo=datetime.timezone.utc)
487
- warnings.append("Precise timezone information not found (and no GPS coordinates to help), fallback to UTC")
534
+ warnings.append(_("Precise timezone information not found (and no GPS coordinates to help), fallback to UTC"))
488
535
 
489
536
  except ValueError as e:
490
- warnings.append("Skipping original date/time (from " + datetimeField + ") as it was not recognized:\n\t" + str(e))
537
+ warnings.append(
538
+ _("Skipping original date/time (from {datefield}) as it was not recognized: {v}").format(
539
+ datefield=datetimeField, v=data[datetimeField]
540
+ )
541
+ )
491
542
 
492
543
  return (d, warnings)
493
544
 
@@ -516,16 +567,19 @@ def decodeGPSDateTime(
516
567
  elif isExifTagUsable(data, f"{group}.GPSDateTime", List[Fraction]):
517
568
  timeRaw = decodeManyFractions(data[f"{group}.GPSDateTime"])
518
569
  else:
519
- raise ValueError(f"GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} group)")
570
+ timeRaw = None
571
+ warnings.append(
572
+ _("GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} group)").format(group=group)
573
+ )
520
574
 
521
- seconds, microseconds, msw = decodeSecondsAndMicroSeconds(
522
- str(float(timeRaw[2])),
523
- data["Exif.Photo.SubSecTimeOriginal"] if isExifTagUsable(data, "Exif.Photo.SubSecTimeOriginal", float) else "0",
524
- )
575
+ if timeRaw:
576
+ seconds, microseconds, msw = decodeSecondsAndMicroSeconds(
577
+ str(float(timeRaw[2])),
578
+ data["Exif.Photo.SubSecTimeOriginal"] if isExifTagUsable(data, "Exif.Photo.SubSecTimeOriginal", float) else "0",
579
+ )
525
580
 
526
- warnings += msw
581
+ warnings += msw
527
582
 
528
- if timeRaw:
529
583
  d = datetime.datetime.combine(
530
584
  datetime.date.fromisoformat(dateRaw),
531
585
  datetime.time(
@@ -544,7 +598,11 @@ def decodeGPSDateTime(
544
598
  d = d.astimezone(pytz.timezone(tz_name))
545
599
 
546
600
  except ValueError as e:
547
- warnings.append(f"Skipping GPS date/time ({group} group) as it was not recognized:\n\t{str(e)}")
601
+ warnings.append(
602
+ _("Skipping GPS date/time ({group} group) as it was not recognized: {v}").format(
603
+ group=group, v=data[f"{group}.GPSDateStamp"]
604
+ )
605
+ )
548
606
 
549
607
  return (d, warnings)
550
608
 
@@ -566,7 +624,9 @@ def decodeSecondsAndMicroSeconds(secondsRaw: str, microsecondsRaw: str) -> Tuple
566
624
  # Check if microseconds from decimal seconds is not mismatching microseconds from SubSecTime field
567
625
  if microseconds != microsecondsFromSeconds and microseconds > 0 and microsecondsFromSeconds > 0:
568
626
  warnings.append(
569
- f"Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is not matching value from EXIF field ({microseconds}). Max value will be kept."
627
+ _(
628
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is not matching value from EXIF field ({microseconds}). Max value will be kept."
629
+ ).format(microsecondsFromSeconds=microsecondsFromSeconds, microseconds=microseconds)
570
630
  )
571
631
  microseconds = max(microseconds, microsecondsFromSeconds)
572
632
  else:
@@ -0,0 +1,114 @@
1
+ # French translations for PACKAGE package.
2
+ # Copyright (C) 2024 THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # Adrien <panieravide@riseup.net>, 2024.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: PACKAGE VERSION\n"
9
+ "Report-Msgid-Bugs-To: \n"
10
+ "POT-Creation-Date: 2024-06-18 09:12+0200\n"
11
+ "PO-Revision-Date: 2024-06-18 09:09+0000\n"
12
+ "Last-Translator: PanierAvide <adrien@pavie.info>\n"
13
+ "Language-Team: French <http://weblate.panoramax.xyz/projects/panoramax/"
14
+ "tag-reader/fr/>\n"
15
+ "Language: fr\n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=UTF-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Plural-Forms: nplurals=2; plural=n > 1;\n"
20
+ "X-Generator: Weblate 5.4.3\n"
21
+
22
+ #: geopic_tag_reader/reader.py:177
23
+ msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
24
+ msgstr ""
25
+ "La latitude est hors des limites du WGS84 (devrait être entre [-90, 90])"
26
+
27
+ #: geopic_tag_reader/reader.py:179
28
+ msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
29
+ msgstr ""
30
+ "La longitude est hors des limites du WGS84 (devrait être entre [-180, 180])"
31
+
32
+ #: geopic_tag_reader/reader.py:221
33
+ msgid "Skipping Mapillary date/time as it was not recognized:"
34
+ msgstr "La date/heure de Mapillary est ignorée car non-reconnue :"
35
+
36
+ #: geopic_tag_reader/reader.py:234
37
+ msgid "Contradicting heading values found, GPSImgDirection value is used"
38
+ msgstr ""
39
+ "Valeurs d'orientation contradictoires, la valeur de GPSImgDirection est "
40
+ "utilisée"
41
+
42
+ #: geopic_tag_reader/reader.py:345
43
+ msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
44
+ msgstr "Coordonnees GPS absentes ou invalides dans les attributs EXIF de l'image"
45
+
46
+ #: geopic_tag_reader/reader.py:351
47
+ msgid "No valid date in picture EXIF tags"
48
+ msgstr "Aucune date valide dans les attributs EXIF de l'image"
49
+
50
+ #: geopic_tag_reader/reader.py:356
51
+ msgid " and "
52
+ msgstr " et "
53
+
54
+ #: geopic_tag_reader/reader.py:423
55
+ msgid "Not a valid list of fractions"
56
+ msgstr "Liste de fractions invalide"
57
+
58
+ #: geopic_tag_reader/reader.py:436 geopic_tag_reader/reader.py:465
59
+ msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
60
+ msgstr "GPSLatitudeRef non-trouvé, utilisation du Nord par défaut"
61
+
62
+ #: geopic_tag_reader/reader.py:444
63
+ msgid "Broken GPS coordinates in picture EXIF tags"
64
+ msgstr "Coordonnées GPS invalides dans les attributs EXIF de l'image"
65
+
66
+ #: geopic_tag_reader/reader.py:447 geopic_tag_reader/reader.py:471
67
+ msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
68
+ msgstr "GPSLongitudeRef non-trouvé, utilisation de l'Est par défaut"
69
+
70
+ #: geopic_tag_reader/reader.py:522
71
+ msgid "Precise timezone information not found, fallback to UTC"
72
+ msgstr ""
73
+ "Aucune information précise de fuseau horaire trouvée, UTC utilisé par défaut"
74
+
75
+ #: geopic_tag_reader/reader.py:527
76
+ msgid ""
77
+ "Precise timezone information not found (and no GPS coordinates to help), "
78
+ "fallback to UTC"
79
+ msgstr ""
80
+ "Aucune information précise de fuseau horaire trouvée (ni de coordonnées GPS "
81
+ "pour aider), UTC utilisé par défaut"
82
+
83
+ #: geopic_tag_reader/reader.py:530
84
+ #, python-brace-format
85
+ msgid ""
86
+ "Skipping original date/time (from {datefield}) as it was not recognized:"
87
+ msgstr ""
88
+ "Date/heure originale (issue de {datefield}) ignorée car elle n'a pas été "
89
+ "reconnue :"
90
+
91
+ #: geopic_tag_reader/reader.py:559
92
+ #, python-brace-format
93
+ msgid ""
94
+ "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
95
+ "group)"
96
+ msgstr ""
97
+ "GPSTimeStamp et GPSDateTime ne contiennent pas un format d'heure valide ("
98
+ "dans le groupe {group})"
99
+
100
+ #: geopic_tag_reader/reader.py:587
101
+ #, python-brace-format
102
+ msgid "Skipping GPS date/time ({group} group) as it was not recognized:"
103
+ msgstr ""
104
+ "Date/heure du GPS (groupe {group}) ignorée car elle n'a pas été reconnue :"
105
+
106
+ #: geopic_tag_reader/reader.py:609
107
+ #, python-brace-format
108
+ msgid ""
109
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
110
+ "not matching value from EXIF field ({microseconds}). Max value will be kept."
111
+ msgstr ""
112
+ "La valeur de micro-secondes lue à partir des secondes en valeurs décimales "
113
+ "({microsecondsFromSeconds}) ne correspond pas au champ EXIF dédié "
114
+ "({microseconds}). La valeur la plus élevée est conservée."
@@ -0,0 +1,111 @@
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-06-25 08:45+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/reader.py:177
21
+ msgid "Read latitude is out of WGS84 bounds (should be in [-90, 90])"
22
+ msgstr ""
23
+
24
+ #: geopic_tag_reader/reader.py:179
25
+ msgid "Read longitude is out of WGS84 bounds (should be in [-180, 180])"
26
+ msgstr ""
27
+
28
+ #: geopic_tag_reader/reader.py:221
29
+ #, python-brace-format
30
+ msgid "Skipping Mapillary date/time as it was not recognized: {v}"
31
+ msgstr ""
32
+
33
+ #: geopic_tag_reader/reader.py:234
34
+ msgid "Contradicting heading values found, GPSImgDirection value is used"
35
+ msgstr ""
36
+
37
+ #: geopic_tag_reader/reader.py:345
38
+ msgid "No GPS coordinates or broken coordinates in picture EXIF tags"
39
+ msgstr ""
40
+
41
+ #: geopic_tag_reader/reader.py:351
42
+ msgid "No valid date in picture EXIF tags"
43
+ msgstr ""
44
+
45
+ #: geopic_tag_reader/reader.py:356
46
+ msgid "The picture is missing mandatory metadata:"
47
+ msgstr ""
48
+
49
+ #: geopic_tag_reader/reader.py:430
50
+ msgid "Not a valid list of fractions"
51
+ msgstr ""
52
+
53
+ #: geopic_tag_reader/reader.py:443 geopic_tag_reader/reader.py:472
54
+ msgid "GPSLatitudeRef not found, assuming GPSLatitudeRef is North"
55
+ msgstr ""
56
+
57
+ #: geopic_tag_reader/reader.py:451
58
+ msgid "Broken GPS coordinates in picture EXIF tags"
59
+ msgstr ""
60
+
61
+ #: geopic_tag_reader/reader.py:454 geopic_tag_reader/reader.py:478
62
+ msgid "GPSLongitudeRef not found, assuming GPSLongitudeRef is East"
63
+ msgstr ""
64
+
65
+ #: geopic_tag_reader/reader.py:529
66
+ msgid "Precise timezone information not found, fallback to UTC"
67
+ msgstr ""
68
+
69
+ #: geopic_tag_reader/reader.py:534
70
+ msgid ""
71
+ "Precise timezone information not found (and no GPS coordinates to help), "
72
+ "fallback to UTC"
73
+ msgstr ""
74
+
75
+ #: geopic_tag_reader/reader.py:538
76
+ #, python-brace-format
77
+ msgid ""
78
+ "Skipping original date/time (from {datefield}) as it was not recognized: {v}"
79
+ msgstr ""
80
+
81
+ #: geopic_tag_reader/reader.py:572
82
+ #, python-brace-format
83
+ msgid ""
84
+ "GPSTimeStamp and GPSDateTime don't contain supported time format (in {group} "
85
+ "group)"
86
+ msgstr ""
87
+
88
+ #: geopic_tag_reader/reader.py:602
89
+ #, python-brace-format
90
+ msgid "Skipping GPS date/time ({group} group) as it was not recognized: {v}"
91
+ msgstr ""
92
+
93
+ #: geopic_tag_reader/reader.py:628
94
+ #, python-brace-format
95
+ msgid ""
96
+ "Microseconds read from decimal seconds value ({microsecondsFromSeconds}) is "
97
+ "not matching value from EXIF field ({microseconds}). Max value will be kept."
98
+ msgstr ""
99
+
100
+ #: geopic_tag_reader/writer.py:15
101
+ msgid ""
102
+ "Impossible to write the exif tags without the '[write-exif]' dependency "
103
+ "(that will need to install libexiv2).\n"
104
+ "Install this package with `pip install geopic-tag-reader[write-exif]` to use "
105
+ "this function"
106
+ msgstr ""
107
+
108
+ #: geopic_tag_reader/writer.py:131
109
+ #, python-brace-format
110
+ msgid "Unsupported key in additional tags ({k})"
111
+ msgstr ""
@@ -5,13 +5,16 @@ from geopic_tag_reader.model import PictureType
5
5
  from enum import Enum
6
6
  import timezonefinder # type: ignore
7
7
  import pytz
8
+ from geopic_tag_reader.i18n import _
8
9
 
9
10
  try:
10
11
  import pyexiv2 # type: ignore
11
12
  except ImportError:
12
13
  raise Exception(
13
- """Impossible to write the exif tags without the '[write-exif]' dependency (that will need to install libexiv2).
14
+ _(
15
+ """Impossible to write the exif tags without the '[write-exif]' dependency (that will need to install libexiv2).
14
16
  Install this package with `pip install geopic-tag-reader[write-exif]` to use this function"""
17
+ )
15
18
  )
16
19
 
17
20
  tz_finder = timezonefinder.TimezoneFinder()
@@ -125,7 +128,7 @@ def writePictureMetadata(picture: bytes, metadata: PictureMetadata) -> bytes:
125
128
  elif k.startswith("Exif."):
126
129
  updated_exif.update({k: v})
127
130
  else:
128
- raise UnsupportedExifTagException(f"Unsupported key in additional tags ({k})")
131
+ raise UnsupportedExifTagException(_("Unsupported key in additional tags ({k})").format(k=k))
129
132
 
130
133
  if updated_exif:
131
134
  img.modify_exif(updated_exif)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: geopic-tag-reader
3
- Version: 1.1.0
3
+ Version: 1.1.2
4
4
  Summary: GeoPicTagReader
5
5
  Author-email: Adrien PAVIE <panieravide@riseup.net>
6
6
  Requires-Python: >=3.8
@@ -17,39 +17,26 @@ Requires-Dist: black ~= 24.3 ; extra == "dev"
17
17
  Requires-Dist: mypy ~= 1.9 ; extra == "dev"
18
18
  Requires-Dist: pytest ~= 7.2.0 ; extra == "dev"
19
19
  Requires-Dist: pytest-datafiles ~= 3.0 ; extra == "dev"
20
- Requires-Dist: lazydocs ~= 0.4.8 ; extra == "dev"
21
20
  Requires-Dist: types-xmltodict ~= 0.13 ; extra == "dev"
22
21
  Requires-Dist: pre-commit ~= 3.3.3 ; extra == "dev"
22
+ Requires-Dist: mkdocs-material ~= 9.5.21 ; extra == "docs"
23
+ Requires-Dist: mkdocstrings[python] ~= 0.25.1 ; extra == "docs"
23
24
  Requires-Dist: python-dateutil ~= 2.8.2 ; extra == "write-exif"
24
- Project-URL: Home, https://gitlab.com/geovisio/geo-picture-tag-reader
25
+ Requires-Dist: types-python-dateutil ~= 2.9.0.20240316 ; extra == "write-exif"
26
+ Project-URL: Home, https://gitlab.com/panoramax/server/geo-picture-tag-reader
25
27
  Provides-Extra: build
26
28
  Provides-Extra: dev
29
+ Provides-Extra: docs
27
30
  Provides-Extra: write-exif
28
31
 
29
- # ![GeoVisio](https://gitlab.com/geovisio/api/-/raw/develop/images/logo_full.png)
32
+ # ![Panoramax](https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Panoramax.svg/40px-Panoramax.svg.png) Panoramax
30
33
 
31
- __GeoVisio__ is a complete solution for storing and __serving your own 📍📷 geolocated pictures__ (like [StreetView](https://www.google.com/streetview/) / [Mapillary](https://mapillary.com/)).
32
-
33
- ➡️ __Give it a try__ at [panoramax.ign.fr](https://panoramax.ign.fr/) or [geovisio.fr](https://geovisio.fr/viewer) !
34
-
35
- ## 📦 Components
36
-
37
- GeoVisio is __modular__ and made of several components, each of them standardized and ♻️ replaceable.
38
-
39
- ![GeoVisio architecture](https://gitlab.com/geovisio/api/-/raw/develop/images/big_picture.png)
40
-
41
- All of them are 📖 __open-source__ and available online:
42
-
43
- | 🌐 Server | 💻 Client |
44
- |:-----------------------------------------------------------------------:|:----------------------------------------------------:|
45
- | [API](https://gitlab.com/geovisio/api) | [Website](https://gitlab.com/geovisio/website) |
46
- | [Blur API](https://gitlab.com/geovisio/blurring) | [Web viewer](https://gitlab.com/geovisio/web-viewer) |
47
- | [GeoPic Tag Reader](https://gitlab.com/geovisio/geo-picture-tag-reader) | [Command line](https://gitlab.com/geovisio/cli) |
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/).
48
35
 
49
36
 
50
37
  # 📷 GeoPic Tag Reader
51
38
 
52
- This repository only contains the Python library to __read and write standardized metadata__ from geolocated pictures EXIF metadata.
39
+ This repository only contains the Python library to __read and write standardized metadata__ from geolocated pictures EXIF metadata. It can be used completely apart from all GeoVisio/Panoramax components for your own projects and needs.
53
40
 
54
41
  ## Features
55
42
 
@@ -71,7 +58,7 @@ geopic-tag-reader --help
71
58
 
72
59
  To know more about install and other options, see [install documentation](./docs/Install.md).
73
60
 
74
- If at some point you're lost or need help, you can contact us through [issues](https://gitlab.com/geovisio/geo-picture-tag-reader/-/issues) or by [email](mailto:panieravide@riseup.net).
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).
75
62
 
76
63
 
77
64
  ## Usage
@@ -137,26 +124,12 @@ editedImg.close()
137
124
 
138
125
  ## Contributing
139
126
 
140
- Pull requests are welcome. For major changes, please open an [issue](https://gitlab.com/geovisio/geo-picture-tag-reader/-/issues) first to discuss what you would like to change.
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.
141
128
 
142
129
  More information about developing is available in [documentation](./docs/Develop.md).
143
130
 
144
131
 
145
- ## 🤗 Special thanks
146
-
147
- ![Sponsors](https://gitlab.com/geovisio/api/-/raw/develop/images/sponsors.png)
148
-
149
- GeoVisio was made possible thanks to a group of ✨ __amazing__ people ✨ :
150
-
151
- - __[GéoVélo](https://geovelo.fr/)__ team, for 💶 funding initial development and for 🔍 testing/improving software
152
- - __[Carto Cité](https://cartocite.fr/)__ team (in particular Antoine Riche), for 💶 funding improvements on viewer (map browser, flat pictures support)
153
- - __[La Fabrique des Géocommuns (IGN)](https://www.ign.fr/institut/la-fabrique-des-geocommuns-incubateur-de-communs-lign)__ for offering long-term support and funding the [Panoramax](https://panoramax.fr/) initiative and core team (Camille Salou, Mathilde Ferrey, Christian Quest, Antoine Desbordes, Jean Andreani, Adrien Pavie)
154
- - Many _many_ __wonderful people__ who worked on various parts of GeoVisio or core dependencies we use : 🧙 Stéphane Péneau, 🎚 Albin Calais & Cyrille Giquello, 📷 [Damien Sorel](https://www.strangeplanet.fr/), Pascal Rhod, Nick Whitelegg...
155
- - __[Adrien Pavie](https://pavie.info/)__, for ⚙️ initial development of GeoVisio
156
- - And you all ✨ __GeoVisio users__ for making this project useful !
157
-
158
-
159
132
  ## ⚖️ License
160
133
 
161
- Copyright (c) GeoVisio team 2022-2023, [released under MIT license](https://gitlab.com/geovisio/geo-picture-tag-reader/-/blob/main/LICENSE).
134
+ Copyright (c) Panoramax team 2022-2024, [released under MIT license](https://gitlab.com/panoramax/server/geo-picture-tag-reader/-/blob/main/LICENSE).
162
135
 
@@ -0,0 +1,15 @@
1
+ geopic_tag_reader/__init__.py,sha256=DCAt2kbPER8vsAuxzbjuzgbgYxV4aajpxsaPx--ZQ28,47
2
+ geopic_tag_reader/camera.py,sha256=Nw6dQjnrUCCOXujjk8Y7IwjJPMuDf4DAGCmHk0LDfEg,1975
3
+ geopic_tag_reader/i18n.py,sha256=MnhttLwppXnioXYUdlTi1J9SPtSKhaHc9ARYtsS5t-M,303
4
+ geopic_tag_reader/main.py,sha256=ZEZaZEeaDxRjrVMwhR5lUYJWKkUcjd8avjqm7JxJdhM,3219
5
+ geopic_tag_reader/model.py,sha256=rsWVE3T1kpNsKXX8iv6xb_3PCVY6Ea7iU9WOqUgXklU,129
6
+ geopic_tag_reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ geopic_tag_reader/reader.py,sha256=lHAMafZK7rCFsO1joCXGrbjyYJfgwab7dQF-eKFLRHs,25337
8
+ geopic_tag_reader/writer.py,sha256=gUvs2fVe7VIMzsT6ZGnXOiKqtsTgndDaJeTTCYnOpXQ,8748
9
+ geopic_tag_reader/translations/geopic_tag_reader.pot,sha256=-Z6YYskO_jYKXGJssbfigAixHMcAXylNAMKebcjFCXY,3198
10
+ geopic_tag_reader/translations/fr/LC_MESSAGES/geopic_tag_reader.po,sha256=QxuV0AYxOAVCI4xaKDDCvgUfeym6oRQC2YeRJpI9i9c,4223
11
+ geopic_tag_reader-1.1.2.dist-info/entry_points.txt,sha256=c9YwjCNhxveDf-61_aSRlzcpoutvM6KQCerlzaVt_JU,64
12
+ geopic_tag_reader-1.1.2.dist-info/LICENSE,sha256=oHWDwXkJJb9zJzThMN3F9Li4yFhz1qxOUByouY7L3bI,1070
13
+ geopic_tag_reader-1.1.2.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
14
+ geopic_tag_reader-1.1.2.dist-info/METADATA,sha256=fH9KBmz593jrgLNiRrYrWYH2gSWBi_xPXghP22v_92k,4578
15
+ geopic_tag_reader-1.1.2.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- geopic_tag_reader/__init__.py,sha256=o8d20yL96tGKyj_0NXyBn_HJ2i1hofM-qTuKouf4ZkE,47
2
- geopic_tag_reader/camera.py,sha256=2Sr0jAt3RXUWazYMnkwF6J6lVnKvSp7Ac8g7yOHehVA,1643
3
- geopic_tag_reader/main.py,sha256=a0RmJIA0R5dTK1vzDZD3Q4MFFAiDv6pnRiFHjfIjeJU,3141
4
- geopic_tag_reader/model.py,sha256=rsWVE3T1kpNsKXX8iv6xb_3PCVY6Ea7iU9WOqUgXklU,129
5
- geopic_tag_reader/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- geopic_tag_reader/reader.py,sha256=w19zJ9CvdJyk_UxWcYX2T0DkhdQmw2xMzlAo1sR1ir0,22867
7
- geopic_tag_reader/writer.py,sha256=QmQqQpWgb6AL3Y0Kuzy7PF1asUsTVDq9buwJXfFSRq8,8672
8
- geopic_tag_reader-1.1.0.dist-info/entry_points.txt,sha256=c9YwjCNhxveDf-61_aSRlzcpoutvM6KQCerlzaVt_JU,64
9
- geopic_tag_reader-1.1.0.dist-info/LICENSE,sha256=oHWDwXkJJb9zJzThMN3F9Li4yFhz1qxOUByouY7L3bI,1070
10
- geopic_tag_reader-1.1.0.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
11
- geopic_tag_reader-1.1.0.dist-info/METADATA,sha256=DJ9OiZyvVGNMdveGev-V33kO9sqPPo-V9SADIezd0I4,6303
12
- geopic_tag_reader-1.1.0.dist-info/RECORD,,