Today almost everyone uses a smart phone. These phones have many capabilities including the ability to take photos and videos. Because they also include a GPS, photos can be location enabled so that the geographic coordinates of each photo are captured and stored with the metadata that accompanies the photos. Photo metadata is stored in an Exchangeable image format (Exif). The Python Imaging Library (PIL) can be used to extract this information including latitude, longitude coordinates. Using this extracted coordinate information a reverse geocoding process can then be applied to each coordinate to determine the nearest address of the photo. This can be extremely useful for organizations such as property managers, real estate agents, local government organizations and more. Employees can be sent into the field with a smart phone to capture photos of properties or other assets without having to be concerned about capturing address and GPS information. For example, many local governments maintain an inventory of vacant homes in their city. One logical way of capturing this information would be to send personnel into the field to capture the location of each vacant home including the address and or coordinates. However, the process could be simplified by simply sending personnel into the field with a smart phone and having them take location enabled photos of each property. A post processing routine could then extract the coordinates of each photo and perform a reverse geocoding process to assign the address,save this information to a geodatabase where it could be used in various mapping applications, and copy the photos to a publicly accessible web server such as Dropbox or Amazon S3. An alternative would be to create a web or mobile mapping application that would allow citizens to upload location enabled photos and have the application extract the coordinates and assign the address.
In this post, a partial extract from a chapter in my new book, ArcGIS Blueprints, I’ll show you how to extract the coordinates from a location enabled photo taken from an iPhone, and apply a reverse geocoding process to derive the nearest address. The book chapter goes into more detail including how to write this information to a local feature class in a geodatabase and share the data through ArcGIS Online. The photos are also copied to a Dropbox account using the Python dropbox module so that the photos can be accessed through a web application.
The code is created through an ArcGIS Python Toolbox as seen below.
Taking the Photos
The code for this example does require that you use an iPhone or iPad device for taking the photos. If you have an Android or other device the metadata created with the photos will be different and require that your code be altered to account for the diffrences.
Photos taken with the Camera application on an iPhone can store geographic coordinates in the metadata associated with each photo. However, you will need to turn on Location Services. The steps for doing so are provided below:
- Open the Settings app on your iPhone.
- Select Privacy.
- You should see Location Services at the top of the Privacy dialog as seen in the screenshot below.
- Click Location Services, find the Camera app, and select While Using. This will ensure that any photos taken with the Camera app will include geographic coordinates.
Extracting Photo Metadata, Reverse Geocoding the Location, Writing to Dropbox, and Writing to a Feature Class
In this step you’ll write a tool that processes a series of photos taken with an Apple iPhone. The tool will extract the latitude, longitude coordinates of each photo and write the information as individual point features in a feature class stored in a file geodatabase. Coordinate information for the photos can be extracted using the Python PIL module. In a later step we’ll update the script to also copy the photos to Dropbox.
Note: I’m not describing all the code used to create this tool for the sake of brevity. What I’m leaving out is some of the code used to create the ArcGIS Python Toolbox as well as a function that creates a file geodatabase to store the data. These are relatively simple and common tasks but keep this in mind as you read the code. The goal here is mainly to highlight the sections of code dealing with metadata extraction, reverse geocoding, and copying the photos to Dropbox.
However, you can download the entire code and the photos.
The code also assumes that you have already installed the PIL, requests, and dropbox modules. Both can be done through pip.
You can click on any of the code samples below to enlarge them if they’re hard to read.
- Import the various Python modules that will be used in this application.
- In the ConvertPhotosToGeodatabase class I’ll draw your attention to a few areas of code. The getParameterInfo() method is used to capture input parameters from the tool dialog. In this case we’re capturing the path to the photos, path to the file geodatabase to be created, file geodatabase name, and output feature class name.
- Also, the execute() method captures these input parameters after the user has clicked the OK button from the tool dialog and then sends these as parameters to the makeGISpointsFromPics() function.
- Now let’s examine the makeGISpointsFromPics() function seen below.
- The line _createFGDB(path,fgdb,fc) calls a function to create a new file geodatabase and feature class based on the input received through the tool. I’m not going to go into an detail on this function. It’s pretty straight forward and simple so you can review this on your own.
- The code below gathers a list of photos with a file extension of .jpg or .JPG from the input picture folder.
- We then enter a for loop that will loop through all the pictures, extract the geographic coordinates from the photo metatadata, and write this information to the feature class. The following line of code retrieve the exif data for each picture.
- The get_exif_data() function is below. This function uses the PIL module to open the photo, and extract the exif metadata information including the GPS tags. This information is then passed back to the get_exif_data() function.
- Next, the lines below retrieve the latitude,longitude coordinates from the metadata using the get_lat_lon() function. This returns a list object from which we can retrieve the individual latitude and longitude values.
- Below you will find the code block that defines the get_lat_lon() function. This function retrieves the latitude, longitude and reference information from the exif metadata and then converts it from degrees, minutes, seconds to decimal degrees via a call to the _convert_to_degrees() function. I won’t go into detail on the _convert_to_degrees() function but it’s provided in the code for you to review if needed.
- The next two lines of code from the makeGISpointsFromPics() function will be used reverse geocode the retrieved point and send the photo to Dropbox. We’ll examine each more closely.
- The call to the getAddress() function passes in the latitude, longitude values retrieved from the photo metadata. The getAddress() function uses the Python requests module to send the coordinate information to the Esri World Geocoding Service. A distance value and output format are also passed to the geocoding service.
The distance value specifies the maximum distance (in meters) to use when searching for the nearest address. You can certainly alter this value if you’d like depending on your particular use case. The reverse geocoding service will search for the nearest address to the point location. Please note that Esri will allow you to perform reverse geocoding using this service as long as you don’t store the results. If you intend to store the results you will need to acquire ArcGIS Online credits. If this is not an option for you there are other reverse geocoding services available including one from Texas A&M.Also notice that we have passed in a response format of pjson (pretty json). The data returned by the reverse geocoding operation will be in a json format similar to that seen in the example below.
- The code block seen below from the getAddress() function is used to extract the address information from the response returned by the reverse geocoding operation.
- Next we’ll discuss the sendPhotoDropbox() function called from the makeGISpointsFromPics() function. The call to this function passes in the path to the photo to upload along with the photo name.
- The sendPhotoDropbox() function, seen below, makes a connection to a Dropbox account using the Python dropbox module and uploads the file. The details of this code are beyond the scope of this post but you can easily research the details of this library. It’s also detailed in Chapter 10 of my ArcGIS Blueprints book and in our Advanced ArcGIS Programming with Python course.
- Now we’ll return again to the makeGISpointsFromPics() function. The code block below uses ArcPy to create a new Point object from the extracted latitude, longitude coordinates and inserts this object into the feature class using an InsertCursor object. Several attributes including the address are also inserted into the row.
And there you have it. This custom tool in an ArcGIS Python toolbox loops through a list of location enabled photos taken with an iPhone and extracts the latitude, longitude coordinates stored with the metadata for each photo, reverse geocodes the coordinates to obtain the nearest address, uploads the photo to a Dropbox account, and writes a new point to a feature class. The output should look appear as seen in the screenshot below.
Programming ArcGIS with Python Classes from Geospatial Training Services
Programming ArcGIS with Python Workshop
Advanced ArcGIS Programming with Python