Bash Script to Sort Photos Based on GPS

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

1 Like