Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added IPTC parsing by pyExiv2 module #116

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 115 additions & 17 deletions ImportPhotos.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,19 @@
import uuid
import json

# Import python module




# Import python modules:
try:
import pyexiv2

from pyexiv2 import Image
PYEXIV_OK = 'true'
except:
PYEXIV_OK = 'false'

CHECK_MODULE = ''
try:
import exifread
Expand Down Expand Up @@ -270,8 +282,7 @@ def initGui(self):
self.toolMouseClick = MouseClick(self.canvas, self)

def setMouseClickMapTool(self):

# Set photos layer as active layer
"""Set photos layer as active layer"""
for layer in self.project_instance.mapLayers().values():
if layer.type() == QgsMapLayerType.VectorLayer and layer.fields().names() == FIELDS:
self.iface.setActiveLayer(layer)
Expand All @@ -290,13 +301,21 @@ def unload(self):
del self.toolbar

def run(self):
"""Remind user about missed python modules and exit"""
if CHECK_MODULE == '':
self.showMessage(
self.tr('Python Modules'),
self.tr('Please install python module "exifread" or "PIL".'),
'Warning')
return

"""Remind user about missed python module pyexiv2 - and continue"""
if PYEXIV_OK == "false":
self.showMessage(
self.tr('Python Module for IPTC'),
self.tr('If you need titles, headings and descriptions from IPTC metadata \n(not only one comment from EXIF),\n please install python module "pyexiv2".'),
'Warning')

self.dlg.out.setText('')
self.dlg.imp.setText('')
self.dlg.canvas_extent.setChecked(False)
Expand Down Expand Up @@ -340,15 +359,15 @@ def toolButtonImport(self):

def import_photos(self):
self.layer_renderer = self.dlg.findChild(QgsRuleBasedRendererWidget, "renderer_widget").renderer()

"""check for form filling"""
file_not_found = False
if self.dlg.imp.text() == '' and not os.path.isdir(self.dlg.imp.text()):
file_not_found = True
msg = self.tr('Please select a directory photos.')
if self.dlg.out.text() == '' and not os.path.isabs(self.dlg.out.text()):
file_not_found = True
msg = self.tr('Please define output file location.')

"""if form not filled - exit"""
if file_not_found:
self.showMessage('Warning', msg, 'Warning')
return
Expand All @@ -366,6 +385,7 @@ def import_photos(self):

self.dlg.close()
self.call_import_photos()
"""will be good to return waiting cursor till photoes are importing: """
# QGuiApplication.setOverrideCursor(Qt.WaitCursor)
# photos_to_import.sort()
# try:
Expand Down Expand Up @@ -413,7 +433,8 @@ def import_photos_task(self, task, wait_time):
try:
if not os.path.isdir(photo_path) and photo_path.lower().endswith(
tuple(SUPPORTED_PHOTOS_EXTENSIONS)):
geo_info = self.get_geo_infos_from_photo(photo_path)
iptcTitle,iptcHeadline,iptcDescription = self.get_iptc_from_photo(photo_path) #yozki.com
geo_info = self.get_geo_infos_from_photo(photo_path,iptcTitle,iptcHeadline,iptcDescription) #yozki.com
if geo_info and geo_info["properties"]["Lat"] and geo_info["properties"]["Lon"]:
geo_info = json.dumps(geo_info)
fields = QgsJsonUtils.stringToFields(geo_info, CODEC)
Expand Down Expand Up @@ -448,6 +469,8 @@ def import_photos_task(self, task, wait_time):

# Save vector layer as a Shapefile
driver = EXTENSION_DRIVERS[os.path.splitext(self.dlg.out.text())[1]]
# Deprecated since version QGIS: 3.20 use writeAsVectorFormatV3 instead:
# error_code, error_message = QgsVectorFileWriter.writeAsVectorFormatV3(
error_code, error_message = QgsVectorFileWriter.writeAsVectorFormat(
self.temp_photos_layer, self.dlg.out.text(), "utf-8",
QgsCoordinateReferenceSystem(self.temp_photos_layer.crs().authid()),
Expand Down Expand Up @@ -488,7 +511,7 @@ def completed(self, result):
self.tr('photo(s) skipped (because of missing location).'),
str(int(out_of_bounds_photos_counter)),
self.tr('photo(s) skipped (because not in canvas extent).'))
self.showMessage(title, msg, self.tr('Information'))
self.showMessage(title, msg, 'Information')

self.layerPhotos_final = QgsVectorLayer(
self.dlg.out.text(),
Expand Down Expand Up @@ -622,14 +645,63 @@ def update_photos(self):

self.showMessage(title, msg, 'Information')

def get_geo_infos_from_photo(self, photo_path):
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
####### delete this. it is only delimiter #######################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
def get_iptc_from_photo(self, photo_path):
try:
###########################################################
############# PyEXIV implementing ##############
###########################################################
#Let's get only Title, Header and Description from IPTC data via pyExiV2 module:
#debug self.showMessage("Photo Path", photo_path, "Warning")
imageExiv = Image(photo_path)
iptcArray = imageExiv.read_iptc()
try:
iptcTitle = iptcArray['Iptc.Application2.ObjectName']
except: #except KeyError:
iptcTitle = ""
try:
iptcHeadline = iptcArray['Iptc.Application2.Headline']
except: #except KeyError:
iptcHeadline=''
try:
iptcDescription = iptcArray['Iptc.Application2.Caption']
except: #except KeyError:
iptcDescription=''

#debug self.showMessage("iptcRead", "go next stage", "Information")
return iptcTitle,iptcHeadline,iptcDescription

except Exception as e:
return '', ''
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
####### delete this. it is temp delimiter#######################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################

def get_geo_infos_from_photo(self, photo_path,iptcTitle,iptcHeadline,iptcDescription):

try:
rel_path = self.get_path_relative_to_project_root(photo_path)
ImagesSrc = '<img src = "' + rel_path + '" width="300" height="225"/>'


if CHECK_MODULE == 'exifread':
with open(photo_path, 'rb') as imgpathF:
tags = exifread.process_file(imgpathF, details=False)

tags = exifread.process_file(imgpathF, details=True)
if not set(tags.keys()).union({"GPS GPSLongitude", "GPS GPSLatitude"}):
return False

Expand Down Expand Up @@ -691,22 +763,48 @@ def get_geo_infos_from_photo(self, photo_path):
model = ''
except:
model = ''

################################################################
####### iptc title from pyexiv is have priority: ###############
try:
if 'Image ImageDescription' in tags:
title = tags['Image ImageDescription']
if iptcTitle > "":
title = iptcTitle
elif 'Image Title' in tags:
title = tags['Title']
else:
title = ''
title = tags
except:
title = ''

################################################################
####### iptc description from pyexiv is have priority: #########
try:
if 'EXIF UserComment' in tags:
user_comm = tags['EXIF UserComment'].printable
if iptcDescription > "": # if IPTC Description exists..
if iptcHeadline > "": #if headline exists - also prefixing description with "headline" string:
user_comm = iptcHeadline+" - "+iptcDescription
user_comm = iptcDescription
elif 'Image ImageDescription' in tags:
user_comm = tags['Image ImageDescription'].printable
else:
user_comm = ''
except:
user_comm = ''
################################################################
###### original code was looks like this: ####################
# if 'Image ImageDescription' in tags:
# user_comm = tags['Image ImageDescription'].printable
# else:
# user_comm = ''
# except:
# user_comm = ''
################################################################
################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################
#################################################################################################################################################################################

elif CHECK_MODULE == 'PIL':
a = {}
Expand Down