Go program finds photos with nearby GPS coordinates

In the Hood

© Lead Image © Tatiana Venkova, 123RF.com

© Lead Image © Tatiana Venkova, 123RF.com

Article from Issue 225/2019

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.


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].

Figure 1: The mobile phone photo contains the GPS data of the location where it was shot.

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].

Figure 2: The shortest connection between two points on a spherical surface. Wikipedia, in the public domain, CC BY-SA 3.0

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



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

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy Linux Magazine

Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

  • Perl: Elasticsearch

    The Elasticsearch full-text search engine quickly finds expressions even in huge text collections. With a few tricks, you can even locate photos that have been shot in the vicinity of a reference image.

  • Treasure Hunt

    A geolocation guessing game based on the popular Wordle evaluates a player's guesses based on the distance from and direction to the target location. Mike Schilli turns this concept into a desktop game in Go using the photos from his private collection.

  • Obfuscation Filter

    Mike Schilli loves his privacy. That's why he's created a Go program that adds a geo-obfuscation layer to cellphone photos before they are published on online platforms to prevent inquisitive minds from inferring the location.

  • Cave Painter

    While searching for a method to draw geodata right into the terminal, Mike Schilli discovers the wondrous world of map projections.

  • Perl: Plotting GPS Data

    Perl hackers take to the hills with a navigation system that provides a graphical rendering of a hiking tour.

comments powered by Disqus
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.

Learn More