Create an Azure Function with Python to Retrieve GPS Coordinates from an Image EXIF in Azure Storage

Author by Nathan Lasnoski

This post is part of a series. If you want to work on creating your own Wildlife ID environment, start here!

We started with provisioning an Azure storage environment that this uses to retrieve images. In this post we’ll provision the Azure Function, deploy the code via VS Code and configure an App Insights availability tracker.

Let’s start by creating a function:

Fill in the function details and use our already created resource group. Ensure you create this as Python so it has the correct runtime stack.

Then select “create” and build your function. It will take a few minutes to initially provision. While doing that, ensure you have prepared your Visual Studio Code environment for Python development.


Open Visual Studio Code and create a new project, then taking the code below and inserting into the editor:

import logging
import requests
import json
import azure.functions as func
from PIL import Image
from PIL.ExifTags import TAGS
from urllib.request import urlopen
from io import BytesIO
from PIL.ExifTags import GPSTAGS

def get_geotagging(exif):
    if not exif:
        raise ValueError("No EXIF metadata found")

    geotagging = {}
    for (idx, tag) in TAGS.items():
        if tag == 'GPSInfo':
            if idx not in exif:
                raise ValueError("No EXIF geotagging found")

            for (key, val) in GPSTAGS.items():
                if key in exif[idx]:
                    geotagging[val] = exif[idx][key]

    return geotagging

def get_decimal_from_dms(dms, ref):

    degrees = dms[0][0] / dms[0][1]
    minutes = dms[1][0] / dms[1][1] / 60.0
    seconds = dms[2][0] / dms[2][1] / 3600.0

    if ref in ['S', 'W']:
        degrees = -degrees
        minutes = -minutes
        seconds = -seconds

    return round(degrees + minutes + seconds, 5)

def get_coordinates(geotags):
    lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])

    lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])

    return (lat,lon)
       

def main(req: func.HttpRequest) -> func.HttpResponse:
    logging.info('Python HTTP trigger function processed a request.')

    name = req.params.get('name')
    if not name:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            name = req_body.get('name')

            name = req.params.get('name')
   
    imagename = req.params.get('imagename')
    if not imagename:
        try:
            req_body = req.get_json()
        except ValueError:
            pass
        else:
            imagename = req_body.get('imagename')


    #url = "https://storageaccountrgbiraaaf.blob.core.windows.net/wildlife/IMG_1175.jpg"
    #url2 = "https://storageaccountrgbiraaaf.blob.core.windows.net/wildlife/" + imagename
    url2 = "https://" + name +"/" + imagename
    response = requests.get(url2)
    img = Image.open(BytesIO(response.content))
    exif = img._getexif()
    geotags = get_geotagging(exif)

    lat = get_decimal_from_dms(geotags['GPSLatitude'], geotags['GPSLatitudeRef'])

    lon = get_decimal_from_dms(geotags['GPSLongitude'], geotags['GPSLongitudeRef'])

     


    if name:
        
        
        #return func.HttpResponse(f"Here are the geotags: {lat}, {lon} for {imagename} at location {url2}")
        
         return func.HttpResponse (
            json.dumps({
            'lat': lat,
            'lon': lon
 
            }) 
         )

    else:
        return func.HttpResponse(
             "Please pass a name on the query string or in the request body",
             status_code=400
        )

This should then look like this:


Sign in to your Azure Account, then select the up arrow to deploy, selecting your Azure Function as the deployment target:

Selecting the target Azure Function:

Then deploy. Yes, we want this to move also into a DevOps environment and we’ll do that ASAP.

You should then get a successful deployment:

Now that you have an Azure Function, let’s test it. Start by uploading a file with EXIF data into your storage account we created earlier.

Then, get the link to the image and validate you can access it:

Then, let’s go back to our Azure Function and grab the function URL for us to use in testing.

So, then copy the default key

So, then combine your URL with the following:

You’ll get this in return (or whatever your GPS coordinates are):

Ok… one last thing, now let’s make sure we have an availability test configured for the Azure Function. Select the Application Insights instance:

Then select Availability:

Select “Add Test” to create the availability test:

Put in the successful test we did above into your availability test, set to run every 5 minutes. Click “create”.

Now you’re cooking with gas! You now have an Azure Function that provides GPS data based on an image.

Nathan Lasnoski

Author

Nathan Lasnoski

Chief Technology Officer