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) diff --git a/tests/sample/pictures/dir1/test1/flickr_jerquiaga_2394751088_cc-by-nc.jpg b/tests/sample/pictures/dir1/test1/flickr_jerquiaga_2394751088_cc-by-nc.jpg new file mode 100644 index 0000000..f1b8c91 Binary files /dev/null and b/tests/sample/pictures/dir1/test1/flickr_jerquiaga_2394751088_cc-by-nc.jpg differ diff --git a/tests/test_gallery.py b/tests/test_gallery.py index 3dd1890..e44e8a0 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -18,7 +18,8 @@ REF = { 'dir1/test1': { 'title': 'An example sub-category', 'thumbnail': '11.jpg', - 'medias': ['11.jpg', 'archlinux-kiss-1024x640.png'], + 'medias': ['11.jpg', 'archlinux-kiss-1024x640.png', + 'flickr_jerquiaga_2394751088_cc-by-nc.jpg'], }, 'dir1/test2': { 'title': 'Test2', diff --git a/tests/test_image.py b/tests/test_image.py index 15e9db0..9502a4b 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -68,3 +68,22 @@ def test_exif_copy(tmpdir): raw, simple = get_exif_tags(dst_file) assert not raw assert not simple + + +def test_exif_gps(tmpdir): + """Test reading out correct geo tags""" + test_image = 'flickr_jerquiaga_2394751088_cc-by-nc.jpg' + src_file = os.path.join(CURRENT_DIR, 'sample', 'pictures', 'dir1', 'test1', + test_image) + dst_file = str(tmpdir.join(test_image)) + + settings = create_settings(img_size=(400, 300), copy_exif_data=True) + generate_image(src_file, dst_file, settings) + raw, simple = get_exif_tags(dst_file) + assert 'gps' in simple + + lat = 35.266666 + lon = -117.216666 + + assert abs(simple['gps']['lat'] - lat) < 0.0001 + assert abs(simple['gps']['lon'] - lon) < 0.0001 diff --git a/tests/test_zip.py b/tests/test_zip.py index 702e0c3..d3e3e98 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -31,7 +31,8 @@ def test_zipped_correctly(tmpdir): assert os.path.basename(zipped1[0]) == 'archive.zip' zip_file = zipfile.ZipFile(zipped1[0], 'r') - expected = ('11.jpg', 'archlinux-kiss-1024x640.png') + expected = ('11.jpg', 'archlinux-kiss-1024x640.png', + 'flickr_jerquiaga_2394751088_cc-by-nc.jpg') for filename in zip_file.namelist(): assert filename in expected