From 4940bbdc0c7e218e4a2e5478f413dbf98731dd88 Mon Sep 17 00:00:00 2001 From: Matthias Vogelgesang Date: Thu, 1 Aug 2013 21:35:02 +0200 Subject: [PATCH] Read GPS coordinates from EXIF tags If GPS information can be found in the EXIF tags, latitude and longitude will be extracted and added to the `media.exif.gps` variable. Some ideas how to get the GPS tags from the EXIF tags where borrowed from Eran Sandler's gist (licensed under MIT license): https://gist.github.com/erans/983821 --- docs/index.rst | 14 ++++++++++++ sigal/image.py | 59 +++++++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 0889320..71a272c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -234,6 +234,20 @@ templates. If available, you can use: This will output something like "Monday, 25. June 2013", depending on your locale. +``media.exif.gps`` + If not None, the dict contains two keys ``lat`` and ``lon`` denoting the + GPS coordinates of the location where the image was taken. ``lat`` will + always be referenced to the north pole whereas ``lon`` will be referenced to + east to the prime meridan. To provide a link on an OpenStreetMap you could + write a template like this: + + .. code-block:: jinja + + {% if media.exif.gps %} + Go to location + {% endif %} + .. _album-information-label: diff --git a/sigal/image.py b/sigal/image.py index f1e893e..39e4455 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -20,13 +20,22 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +# Additional copyright notice: +# +# Several lines of code concerning extraction of GPS data from EXIF tags where +# taken from a GitHub Gist by Eran Sandler at +# +# https://gist.github.com/erans/983821 +# +# and partially modified. The code in question is licensed under MIT license. + import logging import pilkit.processors import sys from PIL import Image as PILImage from PIL import ImageDraw, ImageOps -from PIL.ExifTags import TAGS +from PIL.ExifTags import TAGS, GPSTAGS from pilkit.processors import Transpose, Adjust from pilkit.utils import save_image from datetime import datetime @@ -107,6 +116,29 @@ def add_copyright(img, text): draw.text((5, img.size[1] - 15), '\xa9 ' + text) +def _get_exif_data(filename): + img = PILImage.open(filename) + exif = img._getexif() or {} + data = dict((TAGS.get(t, t), v) for (t, v) in exif.items()) + + if 'GPSInfo' in data: + gps_data = {} + + for tag in data['GPSInfo']: + gps_data[GPSTAGS.get(tag, tag)] = data['GPSInfo'][tag] + + data['GPSInfo'] = gps_data + + return data + + +def _get_degrees(v): + d = float(v[0][0]) / float(v[0][1]) + m = float(v[1][0]) / float(v[1][1]) + s = float(v[2][0]) / float(v[1][1]) + return d + (m / 60.0) + (s / 3600.0) + + def get_exif_tags(source): """Read EXIF tags from file @source and return a tuple of two dictionaries, the first one containing the raw EXIF data, the second one a simplified @@ -117,16 +149,12 @@ def get_exif_tags(source): if not '.jpg' in source.lower(): return (None, None) - img = PILImage.open(source) - try: - exif = img._getexif() + data = _get_exif_data(source) except (TypeError, IOError): - exif = None logger.warning(u'Could not read EXIF data from {0}'.format(source)) return (None, None) - data = dict((TAGS.get(t, t), v) for (t, v) in exif.items()) if exif else {} simple = {} # Provide more accessible tags in the 'simple' key @@ -153,4 +181,23 @@ def get_exif_tags(source): msg = u'Could not parse DateTimeOriginal of %s: %s' % (source, e) logger.warning(msg) + if 'GPSInfo' in data: + info = data['GPSInfo'] + lat_info = info.get('GPSLatitude') + lon_info = info.get('GPSLongitude') + lat_ref_info = info.get('GPSLatitudeRef') + lon_ref_info = info.get('GPSLongitudeRef') + + if lat_info and lon_info and lat_ref_info and lon_ref_info: + lat = _get_degrees(lat_info) + lon = _get_degrees(lon_info) + + if lat_ref_info != 'N': + lat = 0 - lat + + if lon_ref_info != 'E': + lon = 0 - lon + + simple['gps'] = {'lat': lat, 'lon': lon} + return (data, simple)