Using EXIF To Extract GPS Data From Photo

Ivan Luk
Level Up Coding
Published in
4 min readJan 13, 2021

--

I live in the city, so on a pleasant sunny day it is always a joy to see plenty of people walking their dogs of different shapes and sizes. However, it is equally annoying to see dog poops of different shapes and sizes littered all over the sidewalk not picked up by dog owners. Is this a huge problem or is it just a minor annoyance magnified by my disgust? So I decided an app with the ability to display the location of an unpicked-up pile of poop on Google Maps will give me visual indication of how serious the problem is.

Everyone carries a smart phone these days and almost every smart phone can capture picture along with its GPS location in its metadata. All I need is an app that allows a user to upload the picture and then extract the GPS data embedded in the picture file to display the location on Google Maps.

As seen in the above screenshot, the GPS locations from pictures are displayed successfully on the map. This is accomplished using “exif-js” for React. The function allows me to extract all kinds of metadata from the picture file. As long as the picture file contains the GPS information, I can extract the latitude and longitude and produce a GPS object for Google Maps to display in a marker. However, I run into a slight problem.

When the user clicks on the New Report button on the menu bar, the above form pops up to allow the user to upload a picture. For good measure, I allow them to choose the relative size of the unpicked-up poop so a large pile shows up on the map with a more prominently sized icon. When the user clicks submit, the data is extracted and the GPS location and size is then uploaded to the backend. This, however, is where the problem comes in.

The code above seems straightforward enough. The issue I am having is the callback function in the EXIF.getData method. This is executed asynchronously so that means the callback function is executed whenever the file data comes back. In order to make sure the GPS data is uploaded when captured, I need to execute all the backend operations (fetch related code) within the callback. In a normal React/Redux app, in the second “.then” after a “fetch”, we usually call a function to update the Redux store (“this.props.someFunction(data)” followed by redirecting to the page I want to display after the Redux store is refreshed with the newly added data (notice I commented out “this.props.history.push(‘/reports’)). I am not able to do any of that in this callback. The issue is “this”. Within the callback function, “this” has the context of the picture file being processed (see lines 77, 78, 79). Any reference using “this” results in an error “cannot read property “….” of undefined”. I cannot perform “this.props.someFunction(data)”, nor can I perform “this.setState” because of the context of “this” in the callback is still the picture file.

Consequently, I cannot use the JSON data I received back from the backend to update the Redux store, even though the just uploaded poop report has been added to the database via “fetch”. Now my Redux store is outdated when I refresh. Furthermore, as soon as I leave the callback function, I will no longer have access to the JSON data. And since I cannot use “history.push” due to context problem, I am not even able to redirect to a different component. In effect, the Report form stays “open” after user has clicked submit.

Here is how I get around this “issue” for now. I added an Exit button!

This enables me to circumvent the context issue and, at the very least, close the Report form and redirect to the desired component — the poop report map.

By the time I get here, I have already lost the JSON data I received from the backend. As a result, I can no longer append the new report data to the Redux store. I have no choice but to retrieve the entire set again from the backend and refresh the entire Redux store. Not the most efficient, I have to admit. This, however, ensures I have all the updated data to display when I redirect to the Report component.

I have read about using binding in this type of context issue but my knowledge in JavaScript is just not enough to help me figure out the proper solution. If you are reading this and have encountered a similar issue while using EXIF, please feel free to comment. Any suggestion is greatly appreciated.

--

--

Used to manage software development in financial services. Recently acquired skills include full stack development and DNA extraction/sequencing.