Merge pull request #29 from matze/exif-gps
Read GPS coordinates from EXIF tags
This commit is contained in:
@@ -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 %}
|
||||
<a href="http://openstreetmap.org/index.html?lat={{
|
||||
media.exif.gps.lat }}&lon={{ media.exif.long}}">Go to location</a>
|
||||
{% endif %}
|
||||
|
||||
|
||||
.. _album-information-label:
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 148 KiB |
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user