diff options
author | Olivier Tilloy <olivier@tilloy.net> | 2010-01-19 19:42:25 +0100 |
---|---|---|
committer | Olivier Tilloy <olivier@tilloy.net> | 2010-01-19 19:42:25 +0100 |
commit | bbc8f8cebe280a0bcd295839af23b2156b75eb89 (patch) | |
tree | d3aa3e0a3a0df2021179dba7c02b5ba99b9d2c1d /src | |
parent | b7b0316bfddefb2451308f1d4ea4edf35b45ff44 (diff) | |
download | pyexiv2-bbc8f8cebe280a0bcd295839af23b2156b75eb89.tar.gz |
GPSCoordinate class to match the XMP GPSCoordinate type.
Diffstat (limited to 'src')
-rw-r--r-- | src/pyexiv2/__init__.py | 3 | ||||
-rw-r--r-- | src/pyexiv2/utils.py | 109 | ||||
-rw-r--r-- | src/pyexiv2/xmp.py | 14 |
3 files changed, 124 insertions, 2 deletions
diff --git a/src/pyexiv2/__init__.py b/src/pyexiv2/__init__.py index 4a4322f..4b83d54 100644 --- a/src/pyexiv2/__init__.py +++ b/src/pyexiv2/__init__.py @@ -64,7 +64,8 @@ from pyexiv2.exif import ExifValueError, ExifTag from pyexiv2.iptc import IptcValueError, IptcTag from pyexiv2.xmp import XmpValueError, XmpTag from pyexiv2.utils import FixedOffset, Rational, NotifyingList, \ - undefined_to_string, string_to_undefined + undefined_to_string, string_to_undefined, \ + GPSCoordinate __version__ = (0, 2, 1) diff --git a/src/pyexiv2/utils.py b/src/pyexiv2/utils.py index 02fca1d..09a27c9 100644 --- a/src/pyexiv2/utils.py +++ b/src/pyexiv2/utils.py @@ -359,3 +359,112 @@ class NotifyingList(list): if deleted: self._notify_listeners() + +class GPSCoordinate(object): + + """ + A class representing GPS coordinates (e.g. a latitude or a longitude). + + Its attributes (degrees, minutes, seconds, direction) are read-only + properties. + """ + + _format_re = \ + re.compile(r'(?P<degrees>-?\d+),' + '(?P<minutes>\d+)(,(?P<seconds>\d+)|\.(?P<fraction>\d+))' + '(?P<direction>[NSEW])') + + def __init__(self, degrees, minutes, seconds, direction): + """ + Constructor. + + @param degrees: degrees + @type degrees: C{int} + @param minutes: minutes + @type minutes: C{int} + @param seconds: seconds + @type seconds: C{int} + @param direction: direction ('N', 'S', 'E' or 'W') + @type direction: C{str} + """ + if degrees < 0 or degrees > 90: + raise ValueError() + self._degrees = degrees + if minutes < 0 or minutes > 60: + raise ValueError() + self._minutes = minutes + if seconds < 0 or seconds > 60: + raise ValueError() + self._seconds = seconds + if direction not in ('N', 'S', 'E', 'W'): + raise ValueError() + self._direction = direction + + @property + def degrees(self): + return self._degrees + + @property + def minutes(self): + return self._minutes + + @property + def seconds(self): + return self._seconds + + @property + def direction(self): + return self._direction + + @staticmethod + def from_string(string): + """ + Instantiate a GPSCoordinate from a string formatted as C{DDD,MM,SSk} or + C{DDD,MM.mmk} where C{DDD} is a number of degrees, C{MM} is a number of + minutes, C{SS} is a number of seconds, C{mm} is a fraction of minutes, + and C{k} is a single character C{N}, C{S}, C{E}, or C{W} indicating a + direction (north, south, east, west). + + @param string: a string representation of a GPS coordinate + @type string: C{str} + + @return: the GPS coordinate parsed + @rtype: L{GPSCoordinate} + + @raise ValueError: if the format of the string is invalid + """ + match = GPSCoordinate._format_re.match(string) + if match is None: + raise ValueError('Invalid format for a GPS coordinate: %s' % string) + gd = match.groupdict() + fraction = gd['fraction'] + if fraction is not None: + seconds = int(round(int(fraction[:2]) * 0.6)) + else: + seconds = int(gd['seconds']) + return GPSCoordinate(int(gd['degrees']), int(gd['minutes']), seconds, + gd['direction']) + + def __eq__(self, other): + """ + Compare two GPS coordinates for equality. + + @param other: the GPS coordinate to compare to self for equality + @type other: L{GPSCoordinate} + + @return: C{True} if equal, C{False} otherwise + @rtype: C{bool} + """ + return (self._degrees == other._degrees) and \ + (self._minutes == other._minutes) and \ + (self._seconds == other._seconds) and \ + (self._direction == other._direction) + + def __str__(self): + """ + Return a string representation of the GPS coordinate conforming to the + XMP specification. + """ + return '%d,%d,%d%s' % (self._degrees, self._minutes, self._seconds, + self._direction) + diff --git a/src/pyexiv2/xmp.py b/src/pyexiv2/xmp.py index 60fa7aa..7cf82e1 100644 --- a/src/pyexiv2/xmp.py +++ b/src/pyexiv2/xmp.py @@ -26,7 +26,7 @@ import libexiv2python -from pyexiv2.utils import FixedOffset, Rational +from pyexiv2.utils import FixedOffset, Rational, GPSCoordinate import datetime import re @@ -290,6 +290,12 @@ class XmpTag(object): # TODO raise NotImplementedError('XMP conversion for type [%s]' % type) + elif type == 'GPSCoordinate': + try: + return GPSCoordinate.from_string(value) + except ValueError: + raise XmpValueError(value, type) + elif type == 'Integer': try: return int(value) @@ -379,6 +385,12 @@ class XmpTag(object): else: raise XmpValueError(value, type) + elif type == 'GPSCoordinate': + if isinstance(value, GPSCoordinate): + return str(value) + else: + raise XmpValueError(value, type) + elif type == 'Integer': if isinstance(value, (int, long)): return str(value) |