Django form geocode submitted address to get lat, lon and postal code

One of my Django applications includes a form where user can enter and submit a property address.

django form

The user submitting the form might not know the postal code so I left it optional. However the postal code is a key piece of information for this particular application so I wanted to ensure that I was getting it.

I also wanted to geocode the address immediately to get the address latitude and longitude so it could be shown to user on a Leaflet.js map.

There are lots of free geocoding services for low intensity usage but I ended up using Google Geocoding which is free under certain usage level. You just need to create a Geocoding API project and use the credentials to set up the geocoding.

To interact with the geocoding API I tried Python gecoding modules geopy and geocoder but in the end just used Python Requests module instead as it was less complicated.

When the user clicked the Submit button, behind the scenes, Requests submitted the address to Google’s Geocoding API, gets the JSON response containing the latitude, longitude and postal code which are then written to the application database.

I will update the code in future to check if the user’s postal code is correct and replace it if it is incorrect. Will wait to see how the postal code accuracy looks. Making geocoding API requests too often could bump me over the free usage limit.

The Django View that contains the code described above is shown below.

def property_add(request):
   
    property_list = Property.objects.filter(user_id=request.user.id).order_by('created')
    
    if request.method == 'POST':
        form = PropertyForm(request.POST)
        if form.is_valid():
            new_property = form.save(commit=False)
            address = "%s, %s, %s, %s" % (new_property.address1, new_property.city, new_property.state, new_property.postcode)
            google_geocode_key = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxx'
            url = 'https://maps.googleapis.com/maps/api/geocode/json?address=' + "'" + address + "'" + '&key=' + google_geocode_key
            
            try:
                response = requests.get(url)
                geoArray = response.json()
                new_property.lat = geoArray['results'][0]['geometry']['location']['lat']
                new_property.lon = geoArray['results'][0]['geometry']['location']['lng']
                new_postcode = geoArray['results'][0]['address_components'][7]['long_name']
                new_fsa = geoArray['results'][0]['address_components'][7]['short_name'][:3]
            except:
                new_property.lat = None
                new_property.lon = None
                new_postcode = None
                new_fsa = None
           
            if new_property.postcode:
                new_property.fsa = new_property.postcode[:3]
            else:
                new_property.postcode = new_postcode
                new_property.fsa = new_fsa
           
            new_property.user_id = request.user.id
            new_property = form.save()
            return HttpResponseRedirect(reverse(property, args=(new_property.pk,)))
    else:
        form = PropertyForm()

    context_dict = {
        'form': form, 
        'property_list': property_list,
    }
        
    return render(
        request,
        'property_form.html',
        context_dict,
        context_instance = RequestContext(
            request,
            {
                'title':'Add Property',
             }
            )
    )    
    

4 Thoughts on “Django form geocode submitted address to get lat, lon and postal code

  1. Avatar Daniel James on August 21, 2019 at 3:19 pm said:

    Hi Curtis,

    I’m currently developing an application in Django, and I want to give users the ability to enter a location and get matched with any listings (objects in the database) that match the location the user has entered. Very similar to what you might see on many sites across the web such as Airbnb or Zillow. I have looked into GeoDjango, but just could help but think, there must be a simpler way to do this. Could I successfully achieve this with a Google API, or would I need to go the GeoDjango route? Any advice, help or solutions whatsoever would be deeply appreciated. Thank you!

  2. Avatar Curtis on August 21, 2019 at 10:12 pm said:

    Hey Daniel, depends on what you mean by “enter a location”. In this post the user enters an address, which is geocoded. Once you have lat and lon you can use that to identify other things that are at or near to that lat and lon.

    Below is example Django query using SQL that returns a list of locations including location name, lat and lon, and their calculated distance from the location of interest.

    In my case, I have a location page that uses query below to show list of nearby locations.

    In your case, you would be allowing user to select/provide a location that you geocode and then identify locations nearby. You have to decide definition of “at that location” eg is user selected location exactly at that location or within 50 meters etc. In query below if the distance = 0 then both sets of lat and lon are exactly same.

    nearby_locations = Location.objects.raw(‘SELECT id, name, lat, lon, 1000 * SQRT(POW(111.2 * (lat – %s), 2) + POW(111.2 * (%s – lon) * COS(lat / 57.3), 2)) AS distance, case (SELECT distance) when 0 then “target” else “nearby” end as icontype FROM locations HAVING distance < 500 ORDER BY distance', (location_lat, location_lon))

  3. Avatar Daniel James on August 27, 2019 at 11:47 am said:

    Hi Curtis, Thank you very much for the reply. I went ahead and used Geopy to geocode all of the

    instances of the model in the database, and was able to plot the location of each instance on a map

    using a mapbox API. I have also attached a mapbox search API which allows the user to enter

    a location and the API automatically geocodes the inputted address and zooms in to the location on

    the map. I just have one more challenge. How would I attach a list of all of the instances of the model

    to the map, so that when the user scrolls the map, the list updated accordingly only showing results

    within that specific area? For example, let’s say the user zooms into the city of

    Columbus, Ohio. The list of instances would automatically update and would no longer contain any

    instances outside of that city. Would achieving something like this have more to do with GeoJson or

    Javascript or would this still be done on the Django side of things. Any bit of guidance whatsoever

    would be greatly appreciated. Once again, thanks for all of your help!

    • Avatar Curtis on August 27, 2019 at 10:50 pm said:

      Geopy sounds like great package to use. Will use it next time I need geocoding.

      Dynamically updating map instance list would be both Javascript and Django. Would need Django view(s) to take inputs from Javascript and return results back to Javascript. It should be “asynchronous” updating so page doesn’t have to entirely refresh just the map and list.

      I imagine Javascript event firing on some user input eg moving map or dropping pin.

      Not sure if it would take one or more Django views. But seems like there are two tasks to do:

      1) After event would need to identify new coordinates of map eg new centroid of visible map plus perhaps some zoom factor.
      2) Send new coordinates to Django view to create and send new list of instances back to front end.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Post Navigation