I was using a popular program to manage my photos. However, when I found out that the ability to sort and collect photos based on GPS data was somewhat limited, I wrote this bash script to sort them for me. The script uses the data from the exiftool command.
My particular use case was that I have been overseeing a large building project and needed to see all of the photos that I had taken at that site – roughly a spacial range of 750 feet. The problem was I had about a thousand photos of that site mixed in with the tens of thousands I have taken during the past 2 years. This script pulled all the photos taken at that target site into one folder.
Here is the way it works:
- Get the GPS data from a “seed” photo (saves me from fumbling with GPS degrees/decimals)
- Input the desired surrounding range for that seed photo in feet e.g. 60 feet (sorry metric world)
- Iterate through a folder of photos looking for a match in the desired range
- Copy photos that match to a designated destination folder
There’s not a great deal of error trapping so it might be possible to throw a monkey wrench into this and get less than stellar results. But for what I needed, it worked perfectly.
Here is the usage:
./photo_gps_sorter.sh <path/to/a/seed_photo.jpg> <distance in feet> <source_folder> <destination_folder>
example:
./photo_gps_sorter.sh ~/photos/20221104_154512.jpg 750 ~/photos ~/my_site
Here is the script:
#!/usr/bin/bash
# Check if a seed photo
if [ -z "$1" ]; then
echo "Usage: $0 <path/to/a/seed_photo.jpg> <distance in feet> <source_folder> <destination_folder> (Please specify seed photo i.e. a photo that seeds the gps coordinates )"
exit 1
fi
# Check if a distance in feet from the seed photo
if [ -z "$2" ]; then
echo "Usage: $0 <path/to/a/seed_photo.jpg> <distance in feet> <source_folder> <destination_folder> (Please the distance in feet for a gps range e.g. 500)"
exit 1
fi
# Check if a source folder
if [ -z "$3" ]; then
echo "Usage: $0 <path/to/a/seed_photo.jpg> <distance in feet> <source_folder> <destination_folder> (Please indicate a source folder)"
exit 1
fi
# Check if a destination folder
if [ -z "$4" ]; then
echo "Usage: $0 <path/to/a/seed_photo.jpg> <distance in feet> <source_folder> <destination_folder> (Please indicate a destination folder)"
exit 1
fi
seed_photo="$1"
range_in_feet="$2"
source_dir="$3"
destination_dir="$4"
# Create the destination directory if it doesn't exist
mkdir -p "$destination_dir"
# Extract GPS coordinates from the seed photo
seed_latitude=$(exiftool -n -gpslatitude -s3 "$seed_photo")
seed_longitude=$(exiftool -n -gpslongitude -s3 "$seed_photo")
# Calculate the minimum and maximum latitude and longitude values assuming 364,000 feet in a one degree
min_latitude=$(awk "BEGIN {print $seed_latitude - ($range_in_feet / 364000)}")
max_latitude=$(awk "BEGIN {print $seed_latitude + ($range_in_feet / 364000)}")
min_longitude=$(awk "BEGIN {print $seed_longitude - ($range_in_feet / (364000 * cos($seed_latitude * 3.14159 / 180)))}")
max_longitude=$(awk "BEGIN {print $seed_longitude + ($range_in_feet / (364000 * cos($seed_latitude * 3.14159 / 180)))}")
# Find all JPEG files in the source directory recursively
find "$source_dir" -type f -iname "*.jpg" -print0 | while IFS= read -r -d $'\0' image; do
# Extract the GPS coordinates using exiftool
latitude=$(exiftool -n -gpslatitude -s3 "$image")
longitude=$(exiftool -n -gpslongitude -s3 "$image")
# check the contents of the GPS data.
# If the length of the string of both variables is non-zero, the condition is true (met)
# If the length of either string is zero, the condition is false and the photo is skipped
if [ -n "$latitude" ] && [ -n "$longitude" ]; then
# Compare the extracted coordinates with the specified range
# if there is a match, awk returns "1" and the condition is true and the photo is copied
# if no match, awk returns 0, the condition is false and the photo is skipped
if (( $(awk "BEGIN {print ($latitude >= $min_latitude && $latitude <= $max_latitude && $longitude >= $min_longitude && $longitude <= $max_longitude) ? 1 : 0}") )); then
# Copy the image to the destination directory
cp "$image" "$destination_dir"
echo "Copied $image to $destination_dir"
fi
else
echo "Skipping $image -- Incomplete GPS data"
fi
done