Go program finds photos with nearby GPS coordinates
In the Hood
Every photo you take with your mobile phone stores the GPS location in the Exif data. A Go program was let loose on Mike Schilli's photo collection to locate shots taken within an area around a reference image.
Just recently, my favorite restaurant in San Francisco, Chow, shut down unexpectedly. On top of the traumatic experience of having to find a new eatery, I was overcome by the desire to find old photos of the place from the good old days on my mobile phone. But how? I sure didn't tag them, but who does, anyway? Having said that, every cell phone photo contains GPS information, and the phone's photo app can group the photos as dots on a map.
Of course, over the years, I had outsourced the photos to other media. Not to worry, my new favorite programming language, Go, comes with image processing routines, prompting me to browse my photo collection for photos taken in or near the restaurant.
To-Do
The Unix exiftool
tool finds the metadata of a JPG file in a flash, leaving social media users wondering what juicy bites of data they are giving to Facebook and company when they post them. In addition to the date and time, the altitude, and the direction of the camera, there are also GPS coordinates that record the exact location on the earth's surface where the picture was taken (Figure 1). Online guru Kevin Mitnick even reports that the authorities once tracked down a Bolivian drug lord, because he had published a vacation photo that still contained the metadata of his secret whereabouts [1].
Verbose Photos
Quite surprisingly, in the Exif section of the image file, metadata such as the latitude and longitude of a shot's location are by no means stored in a computer-friendly format. Instead, the mobile phone might put the string 122 deg 25' 46.82'' W
there under the GPS Longitude
tag and the letter W
for western longitude under the GPS Longitude Ref
tag.
To convert this string into something that can be used later to calculate distance from a reference point, you need to use a library function that converts 122 degrees, 25 angular minutes, and 46.82 angular seconds into floating-point format and negates the numerical value for the W
in western longitude, because only eastern longitudes are positive. The result is a value of -122.429672
for the longitude; based on this, along with the latitude, which is determined in a similar way, another library can then determine the geographical offset to the longitude and latitude of another image.
Now the distance between two points on the earth's surface, given as latitude and longitude, cannot be calculated quite as trivially as within a two-dimensional coordinate system. But if you quickly put on your old trigonometry hat from your school days, you can still work it out. The technical term for the curved distance is "orthodrome" ([2], Figure 2); it's a segment of the great circle on the (approximated) spherical surface of the earth, which is why the whole thing is also known as the "great circle distance." Thanks to the Internet, you don't even have to type this by hand, but can turn to a Go library like golang-geo
[3].
With these tools, whipping up an algorithm in the script shown today (Listing 1, [4]) is easy as pie. It accepts a reference image and a search path for the photo collection. It extracts the reference GPS coordinates from the former and then rummages through all the images in the photo collection one after another, comparing their GPS coordinates with the reference and reporting a hit if the distance to the reference image is below a preset minimum.
Listing 1
geosearch.go
Deep Sea Diver
The filepath.Walk
function from Go's core treasure trove plumbs into the depths of the files in the photo collection referenced as a directory no matter how deeply nested. The call in line 45 accepts a callback function (Visit()
defined in line 51). To ensure that this function has the GPS data of the reference image ready for comparisons, a structure of the type Walker
(defined as of line 14) gets created as its receiver, which is how Go ties functions to structs as objects.
First, lines 40 to 43 initialize a Walker
instance in w
by setting the refLat
and refLong
attributes of the GPS data to the values of the reference image, and then Walk()
in line 45 receives w.Visit
as a callback function. Since Visit()
in line 51 is defined with a receiver of the *Walker
type, as a result, the file system walker calls the Visit()
callback function for each file found, but passes the Walker
structure filled with the reference data to it each time, which means that Visit()
can conveniently pick up the location of the reference image in line 63 and use it to compute the offset distance.
The callback also uses a regular expression in line 53 to check whether the file just found also has a .jpg
suffix in its name; thanks to the "(?i)"
expression in the regex string, this also matches uppercase letters as in .JPG
. The somewhat strange MustCompile()
call for compiling the regular expression in line 53 is attributable to Go's strict error management. Every time a function gets called that could possibly go wrong, the program has to check if all systems are go, or an error has occurred, in which case it has to initiate rescue actions. Now, in theory, compiling a regular expression could go wrong (for example, if it contains illegal syntax), but with a static (and hopefully tested) string, this is impossible.
In this way, functions with a "must" in the name, like MustCompile()
for regular expressions, save the programmer from handling errors by internally aborting the program with a Panic()
if something goes wrong. By definition, the function itself does not return an error, because it only returns if everything actually works out.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
News
-
AlmaLinux OS Kitten 10 Gives Power Users a Sneak Preview
If you're looking to kick the tires of AlmaLinux's upstream version, the developers have a purrfect solution.
-
Gnome 47.1 Released with a Few Fixes
The latest release of the Gnome desktop is all about fixing a few nagging issues and not about bringing new features into the mix.
-
System76 Unveils an Ampere-Powered Thelio Desktop
If you're looking for a new desktop system for developing autonomous driving and software-defined vehicle solutions. System76 has you covered.
-
VirtualBox 7.1.4 Includes Initial Support for Linux kernel 6.12
The latest version of VirtualBox has arrived and it not only adds initial support for kernel 6.12 but another feature that will make using the virtual machine tool much easier.
-
New Slimbook EVO with Raw AMD Ryzen Power
If you're looking for serious power in a 14" ultrabook that is powered by Linux, Slimbook has just the thing for you.
-
The Gnome Foundation Struggling to Stay Afloat
The foundation behind the Gnome desktop environment is having to go through some serious belt-tightening due to continued financial problems.
-
Thousands of Linux Servers Infected with Stealth Malware Since 2021
Perfctl is capable of remaining undetected, which makes it dangerous and hard to mitigate.
-
Halcyon Creates Anti-Ransomware Protection for Linux
As more Linux systems are targeted by ransomware, Halcyon is stepping up its protection.
-
Valve and Arch Linux Announce Collaboration
Valve and Arch have come together for two projects that will have a serious impact on the Linux distribution.
-
Hacker Successfully Runs Linux on a CPU from the Early ‘70s
From the office of "Look what I can do," Dmitry Grinberg was able to get Linux running on a processor that was created in 1971.