Skip to content

Django Google Maps form widget

by Ofir on August 25th, 2009

We’re building a Django application at work, and I had to implement a way for a user to select a location on a map, in order to be able to save the exact coordinates and the zoom level the user chose.
I looked around to see if something similar had been developed, but I did not find anything so simple and useful as what I later developed, so for the sake of open source-ness I’m sharing it here with you.
It is implemented as a standard Django form widget so just do as any other Django widget, for example:

class PersonForm(forms.Form):
    name = forms.CharField()
    website = forms.URLField()
    location = forms.CharField(widget=GMapInput(attrs={'width':'600', 'height':'400'}))

Here’s the code for the GMapInput class (sorry for the docs in Spanish, but I’m in Mexico):

from django.forms.widgets import Input
from django.utils.safestring import mark_safe
 
class GMapInput(Input):
    """
    Widget para seleccionar un punto en un mapa de Google
    """
 
    def render(self, name, value, attrs=None):
        """
        Atributos extras:
         - width: ancho del mapa en pixeles
         - height: alto del mapa en pixeles
         - center: latitud,longitud del punto central del mapa
         - zoom: zoom inicial del mapa, 1 - 17
        """
 
        final_attrs = self.build_attrs(attrs)
        width = final_attrs['width'] if 'width' in final_attrs else '500'
        height = final_attrs['height'] if 'height' in final_attrs else '300'
        center = final_attrs['center'] if 'center' in final_attrs else '21.983801,-100.964355' # Centro de México
        zoom = final_attrs['zoom'] if 'zoom' in final_attrs else '4' # Zoom amplio, se ve todo un país
 
        widget = u'''<div style="margin-left:7em; padding-left:30px;">
                    <input type="hidden" value="%(value)s" name="%(name)s" id="%(id)s" />
                    <div id="%(id)s_map" style="width: %(width)spx; height: %(height)spx"></div></div>
                    <script type="text/javascript">
                        var %(id)s_map = new GMap2(document.getElementById("%(id)s_map"));
                        %(id)s_map.addControl(new GLargeMapControl3D());
 
                        var %(id)s_marker;
                        function %(id)s_updateField() {
                            document.getElementById("%(id)s").value = %(id)s_marker.getLatLng().toUrlValue() + "|" + %(id)s_map.getZoom();
                            %(id)s_map.panTo(%(id)s_marker.getLatLng(), true);
                        }
                    ''' % { 'value': value, 'name': name, 'id': final_attrs['id'], 'width': width, 'height': height }
 
        if value is None or value == '':
            widget = widget + u'''
                        %(id)s_map.setCenter(new GLatLng(%(center)s), %(zoom)s);
                        var %(id)s_clickListener = GEvent.addListener(%(id)s_map, "click", function(overlay, latlng) {
                            if(latlng) {
                                %(id)s_marker = new GMarker(latlng, {draggable: true});
                                %(id)s_map.addOverlay(%(id)s_marker);
                                %(id)s_updateField();
 
                                GEvent.addListener(%(id)s_marker, "dragend", %(id)s_updateField);
                                GEvent.addListener(%(id)s_map, "zoomend", %(id)s_updateField);
                                GEvent.addListener(%(id)s_map, "dblclick", function (overlay, latlng) { %(id)s_marker.setLatLng(latlng); %(id)s_updateField(); });
                                GEvent.removeListener(%(id)s_clickListener);
                            }
                        });
                    </script>''' % { 'id': final_attrs['id'], 'center': center, 'zoom': zoom }
        else:
            values = value.partition('|')
 
            widget = widget + u'''
                        %(id)s_map.setCenter(new GLatLng(%(coords)s), %(zoom)s);
                        %(id)s_marker = new GMarker(new GLatLng(%(coords)s), {draggable: true});
                        %(id)s_map.addOverlay(%(id)s_marker);
 
                        GEvent.addListener(%(id)s_marker, "dragend", %(id)s_updateField);
                        GEvent.addListener(%(id)s_map, "zoomend", %(id)s_updateField);
                        GEvent.addListener(%(id)s_map, "dblclick", function (overlay, latlng) { %(id)s_marker.setLatLng(latlng); %(id)s_updateField(); });
                    ''' % { 'id': final_attrs['id'], 'coords': values[0], 'zoom': values[2] }
 
        return mark_safe(widget)

Don’t forget to add the <script> tag linking to the Google Maps API.

It is yet far from perfect, but I still hope it helps someone out there.

Share or bookmark this post:
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Reddit
  • Slashdot
  • StumbleUpon
  • Technorati
  • Twitter

From → django, python

2 Comments
  1. Marcos permalink

    This is exactly what I was looking for.
    Thanks man,
    Marcos

  2. So have I!
    Thanks

Leave a Reply

Note: XHTML is allowed. Your email address will never be published.

Subscribe to this comment feed via RSS