Commits

Mikhail Korobov committed df9d4a5

Switch to GoogleV3; more customization hooks.

Comments (0)

Files changed (5)

 
 ::
 
-    pip install geopy
+    pip install 'geopy >= 0.95'
     pip install django-easy-maps
 
 Then add 'easy_maps' to INSTALLED_APPS and run ``./manage.py syncdb``
 Settings
 ========
 
-If working on localhost you can run into Google Maps API lockdown. If this happens
+If you need a place where center the map when no address is inserted yet add
+the latitude and longitude to the EASY_MAPS_CENTER variable in your
+settings.py like the following::
+
+    EASY_MAPS_CENTER = (-41.3, 32)
+
+Sometimes you can run into Google Maps API lockdown. If this happens
 then create a EASY_MAPS_GOOGLE_KEY in your settings.py file::
 
     EASY_MAPS_GOOGLE_KEY = "your-google-maps-api-key"
 
-If you need a place where center the map when no address is inserted yet add the
-latitudine and longitude to the EASY_MAPS_CENTER variable in your settings.py
-like the following::
-
-    EASY_MAPS_CENTER = (-41.3, 32)
-
 Usage
 =====
 
 Please refer to http://code.google.com/apis/maps/documentation/javascript/ for
 detailed Google Maps JavaScript API help.
 
+Customizing geocoder
+--------------------
+
+To use a custom geocoder set EASY_MAPS_GEOCODE option::
+
+    # settings.py
+
+    from django_easy_maps import geocode
+
+    def my_geocode(address):
+        """
+        Given an address (an unicode string), return
+        ``(computed_address, (latitude, longitude))`` tuple.
+        """
+        try:
+            # ...
+            return computed_address, (latitude, longitude)
+        except (...) as e:
+            raise geocode.Error(e)
+
+    EASY_MAPS_GEOCODE = my_geocode
+
+
+
 Address model
 =============
 

easy_maps/geocode.py

+# -*- coding: utf-8 -*-
+from __future__ import absolute_import
+from django.utils.encoding import smart_str
+from geopy import geocoders
+
+class Error(Exception):
+    pass
+
+
+def google_v3(address):
+    """
+    Given an address, return ``(computed_address, (latitude, longitude))``
+    tuple using Google Geocoding API v3.
+    """
+    try:
+        g = geocoders.GoogleV3()
+        address = smart_str(address)
+        return g.geocode(address, exactly_one=False)[0]
+    except (UnboundLocalError, ValueError, geocoders.google.GQueryError) as e:
+        raise Error(e)

easy_maps/models.py

+from __future__ import absolute_import
+import logging
+
 from django.conf import settings
 from django.db import models
-from django.utils.encoding import smart_str
-from geopy import geocoders
+
+from . import geocode
+
+logger = logging.getLogger(__name__)
+
 
 class Address(models.Model):
     address = models.CharField(max_length=255, db_index=True)
             self.geocode_error = True
             return
         try:
-            if hasattr(settings, "EASY_MAPS_GOOGLE_KEY") and settings.EASY_MAPS_GOOGLE_KEY:
-                g = geocoders.Google(settings.EASY_MAPS_GOOGLE_KEY)
-            else:
-                g = geocoders.Google(resource='maps')
-            address = smart_str(self.address)
-            self.computed_address, (self.latitude, self.longitude,) = g.geocode(address, exactly_one=False)[0]
+            do_geocode = getattr(settings, "EASY_MAPS_GEOCODE", geocode.google_v3)
+            self.computed_address, (self.latitude, self.longitude,) = do_geocode(self.address)
             self.geocode_error = False
-        except (UnboundLocalError, ValueError,geocoders.google.GQueryError):
+        except geocode.Error as e:
+            try:
+                logger.error(e)
+            except Exception:
+                logger.error("Geocoding error for address %s", self.address)
+
             self.geocode_error = True
+            # TODO: store the exception
 
     def save(self, *args, **kwargs):
         # fill geocode data if it is unknown

easy_maps/templatetags/easy_maps_tags.py

                    '{% easy_map <address> <width> <height> [zoom] [using <template_name>] %}')
     return EasyMapNode(address, width, height, zoom, template_name)
 
+
 class EasyMapNode(template.Node):
     def __init__(self, address, width, height, zoom, template_name):
         self.address = template.Variable(address)
         self.zoom = zoom or 16
         self.template_name = template.Variable(template_name or '"easy_maps/map.html"')
 
+    def get_map(self, address):
+        if isinstance(address, Address):
+            return address
+
+        if address == '':
+            map_ = Address(latitude=settings.EASY_MAPS_CENTER[0],
+                           longitude=settings.EASY_MAPS_CENTER[1])
+        else:
+            map_, _ = Address.objects.get_or_create(address=address)
+
+        return map_
+
+
     def render(self, context):
         try:
             address = self.address.resolve(context)
             template_name = self.template_name.resolve(context)
-
-            map = address
-            if not isinstance(address, Address):
-                if address == '':
-                    map = Address(latitude=settings.EASY_MAPS_CENTER[0], longitude=settings.EASY_MAPS_CENTER[1])
-                else:
-                    map, _ = Address.objects.get_or_create(address=address)
+            map_ = self.get_map(address)
 
             context.update({
-                'map': map,
+                'map': map_,
                 'width': self.width,
                 'height': self.height,
                 'zoom': self.zoom,

easy_maps/widgets.py

+from __future__ import absolute_import
 from django.forms import TextInput
-from django.template import Template, Context
+from django import template
 
 class AddressWithMapWidget(TextInput):
+    width = 700
+    height = 200
+    zoom = 16
+
     def render(self, name, value, attrs=None):
+        tpl = "{{% load easy_maps_tags %}}" \
+              "{{% easy_map address {0.width} {0.height} {0.zoom} %}}".format(self)
+        map_template = template.Template(tpl)
+        context = template.Context({'address': value})
+
         default_html = super(AddressWithMapWidget, self).render(name, value, attrs)
-        map_template = Template("{% load easy_maps_tags %}{% easy_map address 700 200 16 %}")
-        context = Context({'address': value})
         return default_html + map_template.render(context)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.