PhotoGPS is a simple and fast web app you can use to instantly plot your photos on Google Maps, based on where they were taken.
It does this by extracting the GPS location (latitude & longitude) from the Exif metadata within the photo itself, and then using Google Maps API to display exactly where this is in the world. In modern browsers, there exists an API for reading files directly in the browser. This API is called FileReader and it is what PhotoGPS uses to read your images directly, without first uploading them to the cloud. This keeps your data completely private from any server, while speeding up the extraction and plotting process significantly. You can plot literally 1000 photos in less than a minute.
Building it out!
The initial PhotoGPS app showed markers on the Google Maps for every GPS-embedded photo it processed, but how can you even reflect on the memories taken at that point without a preview of the image. So a more recent version of PhotoGPS included thumbnail previews of your images (as shown in the screenshot above). Getting the thumbnails without using a server side is not too complicated in modern browsers. I just read the file in as a DataURL using the FileReader API, wrap that data in an image and voila!
Scaling it up (or down)!
The above code is proof of concept, and would result in the entire full resolution file being processed and spat back out as a base64 string. When you start processing thousands of photos, each being stored as full resolution base64 strings, you’re going to suck up a lot of memory pretty damn fast, so the obvious thing we can do is reduce the size of the image we store in the browser. We can use some fancy HTML5 Canvas elements to reduce the image by creating a canvas element of a particular size (let’s say 100x100), calculating the proper dimensions of the image that would fit in such a box, and rendering the image in the canvas from our base64 string, then immediately asking for the rendered copy back (at 100x100 size), and throw out the original.
Speeding it up!
In the initial versions of PhotoGPS outlined previously, the speed at which files were processing relied on the size of the file itself. This was because initial techniques for reading and extracting the GPS location from the files were inefficient. It involved reading in the entire file including all of the image data to extract the GPS location and image data and then proceeding to extract the data. With a little research, I was able to determine that the Exif data is limited to a certain section of the file (within the first 128 kilobytes of the file). This meant that we can reduce the extraction time from O(n) where n is the filesize, to O(1). Awesome!
EXIF.readFromBinaryFile is a function exposed by a library I discovered (and contributed to) which allows EXIF parsing in the browser. I added thumbnail extraction to this library by reading the Exif spec and blog posts on the topic of client-side Exif extraction, and with a little bit of elbow grease I was able to get thumbnail extraction working!
This allowed me to get previews for images without reading in the entire file, unless the Exif thumbnail was not present in which case I didn’t have much choice but to read the whole file in to generate a thumbnail using HTML5 Canvas like discussed earlier.
This little project was great for a lot of reasons. I learned a lot when it comes to the FileReader API and how much you can really do completely on the client-side. It challenged my low-level skills (reading specs can be a bitch) and pushed me to implement something which I knew was possible but had not yet been documented well. PhotoGPS is a nice little app I use from time to time to plot my photos and see how much I have explored, and what is still left to be discovered!
Keep on discovering!