geopic-tag-reader 1.0.6__py3-none-any.whl → 1.1.1__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.
- geopic_tag_reader/__init__.py +1 -1
- geopic_tag_reader/main.py +3 -1
- geopic_tag_reader/reader.py +92 -14
- geopic_tag_reader/writer.py +2 -2
- {geopic_tag_reader-1.0.6.dist-info → geopic_tag_reader-1.1.1.dist-info}/METADATA +4 -4
- geopic_tag_reader-1.1.1.dist-info/RECORD +12 -0
- geopic_tag_reader-1.0.6.dist-info/RECORD +0 -12
- {geopic_tag_reader-1.0.6.dist-info → geopic_tag_reader-1.1.1.dist-info}/LICENSE +0 -0
- {geopic_tag_reader-1.0.6.dist-info → geopic_tag_reader-1.1.1.dist-info}/WHEEL +0 -0
- {geopic_tag_reader-1.0.6.dist-info → geopic_tag_reader-1.1.1.dist-info}/entry_points.txt +0 -0
geopic_tag_reader/__init__.py
CHANGED
geopic_tag_reader/main.py
CHANGED
|
@@ -22,13 +22,15 @@ def read(
|
|
|
22
22
|
|
|
23
23
|
print("Latitude:", metadata.lat)
|
|
24
24
|
print("Longitude:", metadata.lon)
|
|
25
|
-
print("Timestamp:", metadata.ts)
|
|
25
|
+
print("Timestamp:", metadata.ts.isoformat())
|
|
26
26
|
print("Heading:", metadata.heading)
|
|
27
27
|
print("Type:", metadata.type)
|
|
28
28
|
print("Make:", metadata.make)
|
|
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:")
|
geopic_tag_reader/reader.py
CHANGED
|
@@ -7,11 +7,15 @@ import re
|
|
|
7
7
|
import json
|
|
8
8
|
from fractions import Fraction
|
|
9
9
|
from geopic_tag_reader import camera
|
|
10
|
+
import timezonefinder # type: ignore
|
|
11
|
+
import pytz
|
|
10
12
|
|
|
11
13
|
# This is a fix for invalid MakerNotes leading to picture not read at all
|
|
12
14
|
# https://github.com/LeoHsiao1/pyexiv2/issues/58
|
|
13
15
|
pyexiv2.set_log_level(4)
|
|
14
16
|
|
|
17
|
+
tz_finder = timezonefinder.TimezoneFinder()
|
|
18
|
+
|
|
15
19
|
|
|
16
20
|
@dataclass
|
|
17
21
|
class CropValues:
|
|
@@ -41,8 +45,8 @@ class GeoPicTags:
|
|
|
41
45
|
Attributes:
|
|
42
46
|
lat (float): GPS Latitude (in WGS84)
|
|
43
47
|
lon (float): GPS Longitude (in WGS84)
|
|
44
|
-
ts (
|
|
45
|
-
heading (int): Picture heading (in degrees, North = 0°, East = 90°, South = 180°, West = 270°)
|
|
48
|
+
ts (datetime): The capture date (date & time with timezone)
|
|
49
|
+
heading (int): Picture heading/yaw (in degrees, North = 0°, East = 90°, South = 180°, West = 270°)
|
|
46
50
|
type (str): The kind of picture (flat, equirectangular)
|
|
47
51
|
make (str): The camera manufacturer name
|
|
48
52
|
model (str): The camera model name
|
|
@@ -51,6 +55,8 @@ class GeoPicTags:
|
|
|
51
55
|
exif (dict[str, str]): Raw EXIF tags from picture (following Exiv2 naming scheme, see https://exiv2.org/metadata.html)
|
|
52
56
|
tagreader_warnings (list[str]): List of thrown warnings during metadata reading
|
|
53
57
|
altitude (float): altitude (in m) (optional)
|
|
58
|
+
pitch (float): Picture pitch angle, compared to horizon (in degrees, bottom = -90°, horizon = 0°, top = 90°)
|
|
59
|
+
roll (float): Picture roll angle, on a right/left axis (in degrees, left-arm down = -90°, flat = 0°, right-arm down = 90°)
|
|
54
60
|
|
|
55
61
|
|
|
56
62
|
Implementation note: this needs to be sync with the PartialGeoPicTags structure
|
|
@@ -58,7 +64,7 @@ class GeoPicTags:
|
|
|
58
64
|
|
|
59
65
|
lat: float
|
|
60
66
|
lon: float
|
|
61
|
-
ts:
|
|
67
|
+
ts: datetime.datetime
|
|
62
68
|
heading: Optional[int]
|
|
63
69
|
type: str
|
|
64
70
|
make: Optional[str]
|
|
@@ -68,6 +74,8 @@ class GeoPicTags:
|
|
|
68
74
|
exif: Dict[str, str] = field(default_factory=lambda: {})
|
|
69
75
|
tagreader_warnings: List[str] = field(default_factory=lambda: [])
|
|
70
76
|
altitude: Optional[float] = None
|
|
77
|
+
pitch: Optional[float] = None
|
|
78
|
+
roll: Optional[float] = None
|
|
71
79
|
|
|
72
80
|
|
|
73
81
|
class InvalidExifException(Exception):
|
|
@@ -86,7 +94,7 @@ class PartialGeoPicTags:
|
|
|
86
94
|
|
|
87
95
|
lat: Optional[float] = None
|
|
88
96
|
lon: Optional[float] = None
|
|
89
|
-
ts: Optional[
|
|
97
|
+
ts: Optional[datetime.datetime] = None
|
|
90
98
|
heading: Optional[int] = None
|
|
91
99
|
type: Optional[str] = None
|
|
92
100
|
make: Optional[str] = None
|
|
@@ -96,6 +104,8 @@ class PartialGeoPicTags:
|
|
|
96
104
|
exif: Dict[str, str] = field(default_factory=lambda: {})
|
|
97
105
|
tagreader_warnings: List[str] = field(default_factory=lambda: [])
|
|
98
106
|
altitude: Optional[float] = None
|
|
107
|
+
pitch: Optional[float] = None
|
|
108
|
+
roll: Optional[float] = None
|
|
99
109
|
|
|
100
110
|
|
|
101
111
|
class PartialExifException(Exception):
|
|
@@ -142,6 +152,11 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
142
152
|
except:
|
|
143
153
|
pass
|
|
144
154
|
|
|
155
|
+
# Sanitize charset information
|
|
156
|
+
for k, v in data.items():
|
|
157
|
+
if isinstance(v, str):
|
|
158
|
+
data[k] = re.sub(r"charset=[^\s]+", "", v).strip()
|
|
159
|
+
|
|
145
160
|
# Parse latitude/longitude
|
|
146
161
|
lat, lon, llw = decodeLatLon(data, "Exif.GPSInfo")
|
|
147
162
|
if len(llw) > 0:
|
|
@@ -163,13 +178,13 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
163
178
|
raise InvalidExifException("Read longitude is out of WGS84 bounds (should be in [-180, 180])")
|
|
164
179
|
|
|
165
180
|
# Parse date/time
|
|
166
|
-
d, llw = decodeGPSDateTime(data, "Exif.GPSInfo")
|
|
181
|
+
d, llw = decodeGPSDateTime(data, "Exif.GPSInfo", lat, lon)
|
|
167
182
|
|
|
168
183
|
if len(llw) > 0:
|
|
169
184
|
warnings.extend(llw)
|
|
170
185
|
|
|
171
186
|
if d is None:
|
|
172
|
-
d, llw = decodeGPSDateTime(data, "Xmp.exif")
|
|
187
|
+
d, llw = decodeGPSDateTime(data, "Xmp.exif", lat, lon)
|
|
173
188
|
if len(llw) > 0:
|
|
174
189
|
warnings.extend(llw)
|
|
175
190
|
|
|
@@ -180,7 +195,7 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
180
195
|
"Xmp.GPano.SourceImageCreateTime",
|
|
181
196
|
]:
|
|
182
197
|
if d is None:
|
|
183
|
-
d, llw = decodeDateTimeOriginal(data, exifField)
|
|
198
|
+
d, llw = decodeDateTimeOriginal(data, exifField, lat, lon)
|
|
184
199
|
if len(llw) > 0:
|
|
185
200
|
warnings.extend(llw)
|
|
186
201
|
|
|
@@ -204,7 +219,7 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
204
219
|
except Exception as e:
|
|
205
220
|
warnings.append("Skipping Mapillary date/time as it was not recognized:\n\t" + str(e))
|
|
206
221
|
|
|
207
|
-
# Heading
|
|
222
|
+
# Heading/Yaw
|
|
208
223
|
heading = None
|
|
209
224
|
if isExifTagUsable(data, "Xmp.GPano.PoseHeadingDegrees", float) and isExifTagUsable(data, "Exif.GPSInfo.GPSImgDirection", Fraction):
|
|
210
225
|
gpsDir = int(round(float(Fraction(data["Exif.GPSInfo.GPSImgDirection"]))))
|
|
@@ -227,6 +242,35 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
227
242
|
elif "MAPCompassHeading" in data and isExifTagUsable(data["MAPCompassHeading"], "TrueHeading", float):
|
|
228
243
|
heading = int(round(float(data["MAPCompassHeading"]["TrueHeading"])))
|
|
229
244
|
|
|
245
|
+
# Pitch & roll
|
|
246
|
+
pitch = None
|
|
247
|
+
roll = None
|
|
248
|
+
exifPRFields = ["Xmp.Camera.$$", "Exif.GPSInfo.GPS$$", "Xmp.GPano.Pose$$Degrees", "Xmp.GPano.InitialView$$Degrees"]
|
|
249
|
+
# For each potential EXIF field
|
|
250
|
+
for exifField in exifPRFields:
|
|
251
|
+
# Try out both Pitch & Roll variants
|
|
252
|
+
for checkField in ["Pitch", "Roll"]:
|
|
253
|
+
exifCheckField = exifField.replace("$$", checkField)
|
|
254
|
+
foundValue = None
|
|
255
|
+
# Look for float or fraction
|
|
256
|
+
if isExifTagUsable(data, exifCheckField, float):
|
|
257
|
+
foundValue = float(data[exifCheckField])
|
|
258
|
+
elif isExifTagUsable(data, exifCheckField, Fraction):
|
|
259
|
+
foundValue = float(Fraction(data[exifCheckField]))
|
|
260
|
+
|
|
261
|
+
# Save to correct variable (if not already set)
|
|
262
|
+
if foundValue is not None:
|
|
263
|
+
if checkField == "Pitch":
|
|
264
|
+
if pitch is None:
|
|
265
|
+
pitch = foundValue
|
|
266
|
+
else:
|
|
267
|
+
continue
|
|
268
|
+
elif checkField == "Roll":
|
|
269
|
+
if roll is None:
|
|
270
|
+
roll = foundValue
|
|
271
|
+
else:
|
|
272
|
+
continue
|
|
273
|
+
|
|
230
274
|
# Make and model
|
|
231
275
|
make = data.get("Exif.Image.Make") or data.get("MAPDeviceMake")
|
|
232
276
|
model = data.get("Exif.Image.Model") or data.get("MAPDeviceModel")
|
|
@@ -313,7 +357,7 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
313
357
|
PartialGeoPicTags(
|
|
314
358
|
lat,
|
|
315
359
|
lon,
|
|
316
|
-
d
|
|
360
|
+
d,
|
|
317
361
|
heading,
|
|
318
362
|
pic_type,
|
|
319
363
|
make,
|
|
@@ -323,6 +367,8 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
323
367
|
exif=data,
|
|
324
368
|
tagreader_warnings=warnings,
|
|
325
369
|
altitude=altitude,
|
|
370
|
+
pitch=pitch,
|
|
371
|
+
roll=roll,
|
|
326
372
|
),
|
|
327
373
|
)
|
|
328
374
|
|
|
@@ -330,7 +376,7 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
330
376
|
return GeoPicTags(
|
|
331
377
|
lat,
|
|
332
378
|
lon,
|
|
333
|
-
d
|
|
379
|
+
d,
|
|
334
380
|
heading,
|
|
335
381
|
pic_type,
|
|
336
382
|
make,
|
|
@@ -340,6 +386,8 @@ def readPictureMetadata(picture: bytes) -> GeoPicTags:
|
|
|
340
386
|
exif=data,
|
|
341
387
|
tagreader_warnings=warnings,
|
|
342
388
|
altitude=altitude,
|
|
389
|
+
pitch=pitch,
|
|
390
|
+
roll=roll,
|
|
343
391
|
)
|
|
344
392
|
|
|
345
393
|
|
|
@@ -429,7 +477,9 @@ def decodeLatLon(data: dict, group: str) -> Tuple[Optional[float], Optional[floa
|
|
|
429
477
|
return (lat, lon, warnings)
|
|
430
478
|
|
|
431
479
|
|
|
432
|
-
def decodeDateTimeOriginal(
|
|
480
|
+
def decodeDateTimeOriginal(
|
|
481
|
+
data: dict, datetimeField: str, lat: Optional[float] = None, lon: Optional[float] = None
|
|
482
|
+
) -> Tuple[Optional[datetime.datetime], List[str]]:
|
|
433
483
|
d = None
|
|
434
484
|
warnings = []
|
|
435
485
|
|
|
@@ -442,7 +492,6 @@ def decodeDateTimeOriginal(data: dict, datetimeField: str) -> Tuple[Optional[dat
|
|
|
442
492
|
secondsRaw, microsecondsRaw, msw = decodeSecondsAndMicroSeconds(
|
|
443
493
|
timeRaw[2], data["Exif.Photo.SubSecTimeOriginal"] if isExifTagUsable(data, "Exif.Photo.SubSecTimeOriginal", float) else "0"
|
|
444
494
|
)
|
|
445
|
-
tz = decodeTimeOffset(data, f"Exif.Photo.OffsetTime{'Original' if 'DateTimeOriginal' in datetimeField else ''}")
|
|
446
495
|
warnings += msw
|
|
447
496
|
|
|
448
497
|
d = datetime.datetime.combine(
|
|
@@ -452,9 +501,30 @@ def decodeDateTimeOriginal(data: dict, datetimeField: str) -> Tuple[Optional[dat
|
|
|
452
501
|
minutesRaw,
|
|
453
502
|
secondsRaw,
|
|
454
503
|
microsecondsRaw,
|
|
455
|
-
tzinfo=tz or datetime.timezone.utc,
|
|
456
504
|
),
|
|
457
505
|
)
|
|
506
|
+
|
|
507
|
+
# Timezone handling
|
|
508
|
+
# Try to read from EXIF
|
|
509
|
+
tz = decodeTimeOffset(data, f"Exif.Photo.OffsetTime{'Original' if 'DateTimeOriginal' in datetimeField else ''}")
|
|
510
|
+
if tz is not None:
|
|
511
|
+
d = d.replace(tzinfo=tz)
|
|
512
|
+
|
|
513
|
+
# Otherwise, try to deduct from coordinates
|
|
514
|
+
elif lon is not None and lat is not None:
|
|
515
|
+
tz_name = tz_finder.timezone_at(lng=lon, lat=lat)
|
|
516
|
+
if tz_name is not None:
|
|
517
|
+
d = pytz.timezone(tz_name).localize(d)
|
|
518
|
+
# Otherwise, default to UTC + warning
|
|
519
|
+
else:
|
|
520
|
+
d = d.replace(tzinfo=datetime.timezone.utc)
|
|
521
|
+
warnings.append("Precise timezone information not found, fallback to UTC")
|
|
522
|
+
|
|
523
|
+
# Otherwise, default to UTC + warning
|
|
524
|
+
else:
|
|
525
|
+
d = d.replace(tzinfo=datetime.timezone.utc)
|
|
526
|
+
warnings.append("Precise timezone information not found (and no GPS coordinates to help), fallback to UTC")
|
|
527
|
+
|
|
458
528
|
except ValueError as e:
|
|
459
529
|
warnings.append("Skipping original date/time (from " + datetimeField + ") as it was not recognized:\n\t" + str(e))
|
|
460
530
|
|
|
@@ -467,7 +537,9 @@ def decodeTimeOffset(data: dict, offsetTimeField: str) -> Optional[datetime.tzin
|
|
|
467
537
|
return None
|
|
468
538
|
|
|
469
539
|
|
|
470
|
-
def decodeGPSDateTime(
|
|
540
|
+
def decodeGPSDateTime(
|
|
541
|
+
data: dict, group: str, lat: Optional[float] = None, lon: Optional[float] = None
|
|
542
|
+
) -> Tuple[Optional[datetime.datetime], List[str]]:
|
|
471
543
|
d = None
|
|
472
544
|
warnings = []
|
|
473
545
|
|
|
@@ -504,6 +576,12 @@ def decodeGPSDateTime(data: dict, group: str) -> Tuple[Optional[datetime.datetim
|
|
|
504
576
|
),
|
|
505
577
|
)
|
|
506
578
|
|
|
579
|
+
# Set timezone from coordinates
|
|
580
|
+
if lon is not None and lat is not None:
|
|
581
|
+
tz_name = tz_finder.timezone_at(lng=lon, lat=lat)
|
|
582
|
+
if tz_name is not None:
|
|
583
|
+
d = d.astimezone(pytz.timezone(tz_name))
|
|
584
|
+
|
|
507
585
|
except ValueError as e:
|
|
508
586
|
warnings.append(f"Skipping GPS date/time ({group} group) as it was not recognized:\n\t{str(e)}")
|
|
509
587
|
|
geopic_tag_reader/writer.py
CHANGED
|
@@ -3,11 +3,11 @@ from datetime import datetime, timedelta
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from geopic_tag_reader.model import PictureType
|
|
5
5
|
from enum import Enum
|
|
6
|
+
import timezonefinder # type: ignore
|
|
7
|
+
import pytz
|
|
6
8
|
|
|
7
9
|
try:
|
|
8
10
|
import pyexiv2 # type: ignore
|
|
9
|
-
import timezonefinder # type: ignore
|
|
10
|
-
import pytz
|
|
11
11
|
except ImportError:
|
|
12
12
|
raise Exception(
|
|
13
13
|
"""Impossible to write the exif tags without the '[write-exif]' dependency (that will need to install libexiv2).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: geopic-tag-reader
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.1.1
|
|
4
4
|
Summary: GeoPicTagReader
|
|
5
5
|
Author-email: Adrien PAVIE <panieravide@riseup.net>
|
|
6
6
|
Requires-Python: >=3.8
|
|
@@ -9,6 +9,9 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
9
9
|
Requires-Dist: typer ~= 0.12
|
|
10
10
|
Requires-Dist: xmltodict ~= 0.13
|
|
11
11
|
Requires-Dist: pyexiv2 == 2.8.3
|
|
12
|
+
Requires-Dist: timezonefinder == 6.2.0
|
|
13
|
+
Requires-Dist: pytz ~= 2023.3
|
|
14
|
+
Requires-Dist: types-pytz ~= 2023.3.0.1
|
|
12
15
|
Requires-Dist: flit ~= 3.8.0 ; extra == "build"
|
|
13
16
|
Requires-Dist: black ~= 24.3 ; extra == "dev"
|
|
14
17
|
Requires-Dist: mypy ~= 1.9 ; extra == "dev"
|
|
@@ -17,9 +20,6 @@ Requires-Dist: pytest-datafiles ~= 3.0 ; extra == "dev"
|
|
|
17
20
|
Requires-Dist: lazydocs ~= 0.4.8 ; extra == "dev"
|
|
18
21
|
Requires-Dist: types-xmltodict ~= 0.13 ; extra == "dev"
|
|
19
22
|
Requires-Dist: pre-commit ~= 3.3.3 ; extra == "dev"
|
|
20
|
-
Requires-Dist: timezonefinder == 6.2.0 ; extra == "write-exif"
|
|
21
|
-
Requires-Dist: pytz ~= 2023.3 ; extra == "write-exif"
|
|
22
|
-
Requires-Dist: types-pytz ~= 2023.3.0.1 ; extra == "write-exif"
|
|
23
23
|
Requires-Dist: python-dateutil ~= 2.8.2 ; extra == "write-exif"
|
|
24
24
|
Project-URL: Home, https://gitlab.com/geovisio/geo-picture-tag-reader
|
|
25
25
|
Provides-Extra: build
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
geopic_tag_reader/__init__.py,sha256=7hT1At7Ce4FtNeMuBZa7cDah_cJoIhJrnQWbEAF0BE8,47
|
|
2
|
+
geopic_tag_reader/camera.py,sha256=2Sr0jAt3RXUWazYMnkwF6J6lVnKvSp7Ac8g7yOHehVA,1643
|
|
3
|
+
geopic_tag_reader/main.py,sha256=ZEZaZEeaDxRjrVMwhR5lUYJWKkUcjd8avjqm7JxJdhM,3219
|
|
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=4yoQU-ljOgLmaH_hc1m4o1YkSNgaPfM38NVmiM7rpbE,24543
|
|
7
|
+
geopic_tag_reader/writer.py,sha256=QmQqQpWgb6AL3Y0Kuzy7PF1asUsTVDq9buwJXfFSRq8,8672
|
|
8
|
+
geopic_tag_reader-1.1.1.dist-info/entry_points.txt,sha256=c9YwjCNhxveDf-61_aSRlzcpoutvM6KQCerlzaVt_JU,64
|
|
9
|
+
geopic_tag_reader-1.1.1.dist-info/LICENSE,sha256=oHWDwXkJJb9zJzThMN3F9Li4yFhz1qxOUByouY7L3bI,1070
|
|
10
|
+
geopic_tag_reader-1.1.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
|
11
|
+
geopic_tag_reader-1.1.1.dist-info/METADATA,sha256=iMhClAI5r1h2jpB_o-dwcSP3QVz4PuBPf4jWmZ_10Ks,6303
|
|
12
|
+
geopic_tag_reader-1.1.1.dist-info/RECORD,,
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
geopic_tag_reader/__init__.py,sha256=AB9t1irfcb0A7j8MHsCnqxKwAll8VkwJKfgEAPE4Jw4,47
|
|
2
|
-
geopic_tag_reader/camera.py,sha256=2Sr0jAt3RXUWazYMnkwF6J6lVnKvSp7Ac8g7yOHehVA,1643
|
|
3
|
-
geopic_tag_reader/main.py,sha256=LohJW0xX0A8DAbcUR_BnJ2UvN4qo6yydzOGtq3zIArA,3129
|
|
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=PK1mN7TFFC8V70-pU_EM6cTESNcAed1GydHWUkqkT3A,21325
|
|
7
|
-
geopic_tag_reader/writer.py,sha256=L13ewadZhZic13k66FsQvqGQ_s512nssKvvSwZp4Ggg,8680
|
|
8
|
-
geopic_tag_reader-1.0.6.dist-info/entry_points.txt,sha256=c9YwjCNhxveDf-61_aSRlzcpoutvM6KQCerlzaVt_JU,64
|
|
9
|
-
geopic_tag_reader-1.0.6.dist-info/LICENSE,sha256=oHWDwXkJJb9zJzThMN3F9Li4yFhz1qxOUByouY7L3bI,1070
|
|
10
|
-
geopic_tag_reader-1.0.6.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
|
|
11
|
-
geopic_tag_reader-1.0.6.dist-info/METADATA,sha256=US7A6UqzLzwlLvtCiHWPQszwLTNtNt_Pzka6DLJBbmg,6375
|
|
12
|
-
geopic_tag_reader-1.0.6.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|