I do this on Windows. You could write it from scratch using python and some of the libraries available to process jpeg files, but I've implemented it using batch files, exiftool
(https://exiftool.org/), and basic QGIS. In this set-up, it's best if you split it into steps.
First, create a shell script/batch file that maintains a list of the photos in the target directory, together with their GPS coordinates and other relevant metadata, as a CSV file. I use the following Windows batch file, which you will have to adapt:
@echo off
rem %1 is directory to process
if x%1x==xx goto needdir
cd "%~dp0%"
set inputdir="%~f1\*.jp*"
set outputfile="%~dp0%~n1_raw.csv"
if exist %outputfile% (copy %outputfile% %outputfile%.bak)
echo Processing %inputdir% to %outputfile%
"%USERPROFILE%\Executables\exiftool.exe" -printFormat exiftool_qgis_new.fmt -m -f -api MissingTagValue=0 %inputdir% > %outputfile%
exit /b
:needdir
set /p "inputdir= Full directory to process? "
"%~f0" "%inputdir%"
You'll have to point properly to where you store the exiftool
executable if not in your path. The .fmt
file provides instructions to exiftool which tags to write to the csv file. I use the following, which may well be overkill for you:
#This outputs exif tags in format suitable for QGIS importing, with post processing in QGIS
#[HEAD]Photo,PhotoDate,Longitude,Latitude,Altitude,Azimuth,SourceFile,Orientation,PanoOrigName,Pitch,Yaw
"$Filename",${DateTimeOriginal#;DateFmt("%Y-%m-%d %H:%M:%S")},${GPSLongitude#;$_=sprintf('%.6f',$_)},${GPSLatitude#;$_=sprintf('%.6f',$_)},${GPSAltitude#;$_=sprintf('%.1f',$_)},${GPSImgDirection#;$_=sprintf('%.1f',$_)},"$Directory\$Filename",${Orientation#;if($_==3){$_=2}elsif($_==6){$_=1}elsif($_==8){$_=3}else{$_=0}},"$ImageDescription",$GimbalPitchDegree,$CameraYawDegree
#needs -f -api MissingTagValue=0 to avoid missing long and lat which then make QGis choke. As sideeffect missing PhotoDate is coded as 0 rather than null.
This batch file can be invoked manually from the terminal window in QGIS. You can also set up an operating system scheduled task to run it daily. It takes a couple of minutes on my SSD drive with 3000 photos.
You can then directly add the csv file to QGIS as a vector layer (as a delimited text layer), and use all of QGIS styling power to display it. For instance, you can display the photo in Form view or even as an HTML map tip, use the date/age of the photo to change the color or symbol in the canvas, even draw angles of view, etc. That's outside the scope of this question/answer. If you run the script that generates it manually, it's a good idea to specify Watch file
when adding it as the delimited text layer.
In my .fmt
file above, I have "Photo"
(the filename, no directory) and "SourceFile"
(with full directory included) as separate attributes. You may not need both.
Finally, be aware that the script would clobber any changes you make to the csv file within QGIS (and it's not a good idea to write-edit csv files anyway). So you may find it useful to maintain a separate Master
layer (say in a gpkg) which builds on the Raw
csv file but lets you e.g. categorize or add other info on photos, move them around (in case GPS was wrong), etc. In that case, you will need a processing algorithm to append new entries from the raw file to the master file. The magic incantation that worked for me was a SQL query along the lines of:
SELECT * FROM raw WHERE Photo NOT IN (SELECT master.Photo FROM master INNER JOIN raw WHERE master.Photo=raw.Photo)
(I use this embedded in a more complex Model designer model.)
Finally, I should mention I've cobbled this together over 2 years or so. Meanwhile, QGIS has added features/plugins. So for instance it is now easier to append features in processing. And the ImportPhotos
plugin can do some of the heavy lifting. I haven't gone back to explore if I could coerce them to do precisely what I want, but you might want to look first if you can use that more easily than some of the above.