From 4c9bc9e868f64e1d46905b0f619264c4dc576e3f Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Sun, 23 Dec 2012 10:06:40 +0100 Subject: [PATCH 01/18] Make the marker draggable. --- easy_maps/templates/easy_maps/map.html | 1 + 1 file changed, 1 insertion(+) diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index 9f92899..2835757 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -42,6 +42,7 @@ var marker = new google.maps.Marker({ position: latlng, map: map, + draggable: true, title: "{{ map.address }}" }); {% endblock %} From d1a0d5a5cc030b79f3334d81364e619c176dd575 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Sun, 23 Dec 2012 10:36:25 +0100 Subject: [PATCH 02/18] Update latitude and longitude when the marker is dragged. --- easy_maps/templates/easy_maps/map.html | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index 2835757..41b7534 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -45,6 +45,12 @@ draggable: true, title: "{{ map.address }}" }); + google.maps.event.addListener(marker, 'dragend', function(evt) { + var ll = marker.getPosition(); + // FIXME: fix id names + document.getElementById('id_latitude').value = ll.lat(); + document.getElementById('id_longitude').value = ll.lng(); + }); {% endblock %} } From 2dbb249b5fd1151d126a9d49c8127a280c43ea4a Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Sun, 23 Dec 2012 10:50:46 +0100 Subject: [PATCH 03/18] Factorize code about geolocalization. --- easy_maps/geo.py | 20 ++++++++++++++++++++ easy_maps/models.py | 18 +++++------------- 2 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 easy_maps/geo.py diff --git a/easy_maps/geo.py b/easy_maps/geo.py new file mode 100644 index 0000000..7c5c7a9 --- /dev/null +++ b/easy_maps/geo.py @@ -0,0 +1,20 @@ +from django.conf import settings +from django.utils.encoding import smart_str + +from geopy import geocoders + +def geolocalize(address): + """From an address return the values needed to fullify an Address model form + """ + 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') + s_address = smart_str(address) + computed_address, (latitude, longitude,) = g.geocode(s_address, exactly_one=False)[0] + geocode_error = False + except (UnboundLocalError, ValueError,geocoders.google.GQueryError): + geocode_error = True + + return computed_address, latitude, longitude, geocode_error diff --git a/easy_maps/models.py b/easy_maps/models.py index 40ba9fd..e85fbe9 100644 --- a/easy_maps/models.py +++ b/easy_maps/models.py @@ -1,7 +1,7 @@ -from django.conf import settings from django.db import models -from django.utils.encoding import smart_str -from geopy import geocoders + +from .geo import geolocalize + class Address(models.Model): address = models.CharField(max_length=255, db_index=True) @@ -14,16 +14,8 @@ def fill_geocode_data(self): if not self.address: 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] - self.geocode_error = False - except (UnboundLocalError, ValueError,geocoders.google.GQueryError): - self.geocode_error = True + + self.computed_address, self.latitude, self.longitude, self.geocode_error = geolocalize(self.address) def save(self, *args, **kwargs): # fill geocode data if it is unknown From 5ca4a70763aada4684f7d3367544452da2deacb6 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Sun, 23 Dec 2012 12:09:00 +0100 Subject: [PATCH 04/18] Allow to update field values without saving. Using an AJAX call we can obtain geolocalized data without saving the model. The admin add a 'geo/' URL to call. --- easy_maps/admin.py | 46 ++++++++++++++++++++++++++ easy_maps/templates/easy_maps/map.html | 37 ++++++++++++++++++++- easy_maps/widgets.py | 2 +- 3 files changed, 83 insertions(+), 2 deletions(-) diff --git a/easy_maps/admin.py b/easy_maps/admin.py index 57a2b8d..b3ca1cb 100644 --- a/easy_maps/admin.py +++ b/easy_maps/admin.py @@ -1,8 +1,15 @@ from django.contrib import admin from django import forms +from django.conf.urls.defaults import patterns, url +from django.http import HttpResponse, HttpResponseBadRequest +from django.views.decorators.csrf import csrf_exempt from .models import Address from .widgets import AddressWithMapWidget +from .geo import geolocalize + +import simplejson + class AddressAdmin(admin.ModelAdmin): list_display = ['address', 'computed_address', 'latitude', 'longitude', 'geocode_error'] @@ -14,3 +21,42 @@ class Meta: widgets = { 'address': AddressWithMapWidget({'class': 'vTextField'}) } + + def get_urls(self): + """Add a view that serves geolocalized data on POST request + """ + urls = super(AddressAdmin, self).get_urls() + my_urls = patterns('', + url(r'^geo/$', self.admin_site.admin_view(self.get_geo), name='address_json'), + ) + return my_urls + urls + + # FIXME: add CSRF protection look at https://docs.djangoproject.com/en/1.4/ref/contrib/csrf/#ajax + # for example in passing a CSRF token + @csrf_exempt + def get_geo(self, request): + """Return a json that will be used to insert correct value + into the model form. + """ + if request.method != "POST" or not request.POST.has_key('address') or request.POST['address'] == '': + return HttpResponseBadRequest() + + computed_address, latitude, longitude, geocode_error = geolocalize(request.POST["address"]) + return HttpResponse(simplejson.dumps( + { + 'computed_address': computed_address, + 'latitude': latitude, + 'longitude': longitude, + 'geocode_error': geocode_error, + } + ), content_type='application/json') + +class AddressAdminForm(forms.ModelForm): + class Meta: + widgets = { + 'address': AddressWithMapWidget({'class': 'vTextField'}) + } + +class AddressInlineAdmin(admin.StackedInline): + extra = 1 + form = AddressAdminForm diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index 41b7534..7642162 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -24,6 +24,14 @@ {% block map_js %} {% endblock %} diff --git a/easy_maps/templatetags/easy_maps_tags.py b/easy_maps/templatetags/easy_maps_tags.py index be7f949..5c0d2cd 100644 --- a/easy_maps/templatetags/easy_maps_tags.py +++ b/easy_maps/templatetags/easy_maps_tags.py @@ -44,19 +44,19 @@ def __init__(self, address, width, height, zoom, template_name): def render(self, context): try: - address = self.address.resolve(context) + address = self.address.resolve(context) or '' template_name = self.template_name.resolve(context) - map, _ = Address.objects.get_or_create(address=address or '') - if _: - map.latitude = settings.EASY_MAPS_CENTER[0] or -34.397 - map.longitude = settings.EASY_MAPS_CENTER[1] or 150.644 - map.save() + + latitude = settings.EASY_MAPS_CENTER[0] or -34.397 + longitude = settings.EASY_MAPS_CENTER[1] or 150.644 context.update({ 'map': map, 'width': self.width, 'height': self.height, + 'latitude': latitude, + 'longitude': longitude, 'zoom': self.zoom, 'template_name': template_name }) diff --git a/easy_maps/widgets.py b/easy_maps/widgets.py index 2215805..298cd7e 100644 --- a/easy_maps/widgets.py +++ b/easy_maps/widgets.py @@ -3,7 +3,19 @@ class AddressWithMapWidget(TextInput): def render(self, name, value, attrs=None): + # retrieve the field's id otherwise it's not possible + # to use correctly the JS + _id = attrs["id"] + + # we assume two conditions on 'id' + assert _id.find('id_') == 0 + + find_id = _id.find('address') + assert find_id > 0 + + _id = _id[:find_id] 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}) + map_template = Template("{% load easy_maps_tags %}{% easy_map address 700 200 16 %}") + + context = Context({'id': _id, 'id_safe': _id.replace('-', '_'), 'address': value}) return default_html + map_template.render(context) From 5f9eed927d4659393b4ac01a7313fc6021ed6f9e Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Wed, 26 Dec 2012 20:55:03 +0100 Subject: [PATCH 09/18] Add example models. --- easy_maps_tests/test_app/models.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/easy_maps_tests/test_app/models.py b/easy_maps_tests/test_app/models.py index b7e03fc..135ad27 100644 --- a/easy_maps_tests/test_app/models.py +++ b/easy_maps_tests/test_app/models.py @@ -1 +1,27 @@ #hello, testrunner! +from django.db import models +from django.contrib import admin + +from easy_maps.models import Address +from easy_maps.admin import AddressInlineAdmin + +class Brand(models.Model): + name = models.CharField(max_length=100) + + def count(self): + return self.shop_set.count() + +class Shop(Address): + brand = models.ForeignKey(Brand) + +class ShopInlineAdmin(AddressInlineAdmin): + model = Shop + +class BrandAdmin(admin.ModelAdmin): + list_display = ['name', 'count',] + model = Brand + inlines = [ + ShopInlineAdmin, + ] + +admin.site.register(Brand, BrandAdmin) From 1f0a767506c8380bff56417265b6f8daa283d514 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Thu, 3 Jan 2013 09:40:27 +0100 Subject: [PATCH 10/18] Put the javascript files into the Media class. In this way is avoid the multiple inclusion of the Google API javascript file. --- README.rst | 4 +++- easy_maps/static/js/easy_maps.js | 27 ++++++++++++++++++++++++++ easy_maps/templates/easy_maps/map.html | 2 -- easy_maps/widgets.py | 5 +++++ 4 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 easy_maps/static/js/easy_maps.js diff --git a/README.rst b/README.rst index 126b277..6ce2b92 100644 --- a/README.rst +++ b/README.rst @@ -17,7 +17,9 @@ Installation pip install django-easy-maps Then add 'easy_maps' to INSTALLED_APPS and run ``./manage.py syncdb`` -(or ``./manage.py migrate easy_maps`` if South is in use) +(or ``./manage.py migrate easy_maps`` if South is in use). Since there are +some media files needed to be used, you have to collect the static files +distributed with this application (using ``./manage collectstatic``). Settings ======== diff --git a/easy_maps/static/js/easy_maps.js b/easy_maps/static/js/easy_maps.js new file mode 100644 index 0000000..67bfcb2 --- /dev/null +++ b/easy_maps/static/js/easy_maps.js @@ -0,0 +1,27 @@ +// will contain the boundary of the map. +var g_lat_long_bound = new google.maps.LatLngBounds(); + +function easy_map_add_listener(id, marker) { + // update the coordinate on marker dragging + google.maps.event.addListener(marker, 'dragend', function(evt) { + var ll = marker.getPosition(); + // FIXME: fix id names + document.getElementById(id + 'latitude').value = ll.lat(); + document.getElementById(id + 'longitude').value = ll.lng(); + }); +} + +function easy_maps_add_marker(map, marker) { + var latlng = new google.maps.LatLng(marker.latitude, marker.longitude); + var marker = new google.maps.Marker({ + position: latlng, + map: map, + draggable: true, + title: marker.address + }); + + // add marker's coordinate to the boundary + g_lat_long_bound.extend(latlng); + + return marker; +} diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index a4cccc4..49f88df 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -3,8 +3,6 @@ {% with longitude|stringformat:"f" as long %} {% block api_js %} - - {% endblock %} {% block html %} diff --git a/easy_maps/widgets.py b/easy_maps/widgets.py index 298cd7e..15e25a5 100644 --- a/easy_maps/widgets.py +++ b/easy_maps/widgets.py @@ -2,6 +2,11 @@ from django.template import Template, Context class AddressWithMapWidget(TextInput): + class Media: + js = ( + 'https://maps.google.com/maps/api/js?sensor=false', + 'js/easy_maps.js', + ) def render(self, name, value, attrs=None): # retrieve the field's id otherwise it's not possible # to use correctly the JS From 39645fd1b1dc3b4d4c63675b30b52dc5f89fb1a7 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Wed, 2 Jan 2013 11:40:45 +0100 Subject: [PATCH 11/18] easy_map tag now display more than one marker. --- easy_maps/models.py | 12 ++++++++++++ easy_maps/templates/easy_maps/map.html | 19 ++++++++++++++++--- easy_maps/templatetags/easy_maps_tags.py | 24 +++++++++++++++++++----- 3 files changed, 47 insertions(+), 8 deletions(-) diff --git a/easy_maps/models.py b/easy_maps/models.py index b3b0020..92e7288 100644 --- a/easy_maps/models.py +++ b/easy_maps/models.py @@ -31,3 +31,15 @@ class Meta: verbose_name = "EasyMaps Address" verbose_name_plural = "Address Geocoding Cache" + def json(self): + """Returns a JSON representation of the address data to be used + with the javascript in a template. + """ + import simplejson + dic = { + 'address': self.address, + 'computed_address': self.computed_address, + 'latitude': self.latitude, + 'longitude': self.longitude, + } + return simplejson.dumps(dic) diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index 49f88df..a861570 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -2,9 +2,6 @@ {% with latitude|stringformat:"f" as lat %} {% with longitude|stringformat:"f" as long %} -{% block api_js %} -{% endblock %} - {% block html %}
1 %} + // display all the markers + // http://blog.shamess.info/2009/09/29/zoom-to-fit-all-markers-on-google-maps-api-v3/ + map.fitBounds(g_lat_long_bound); + {% else %} + easy_maps_add_listener("{{ id }}", marker); + {% endif %} + {% endif %} + {% endblock %} } diff --git a/easy_maps/templatetags/easy_maps_tags.py b/easy_maps/templatetags/easy_maps_tags.py index 5c0d2cd..49389ae 100644 --- a/easy_maps/templatetags/easy_maps_tags.py +++ b/easy_maps/templatetags/easy_maps_tags.py @@ -10,7 +10,15 @@ def easy_map(parser, token): """ The syntax: + {% easy_map
[ ] [] [using ] %} + + or + + {% easy_map [ ] [] [using ] %} + + where in the second case you pass a queryset containing the addresses to be + visualized. """ width, height, zoom, template_name = None, None, None, None params = token.split_contents() @@ -47,16 +55,22 @@ def render(self, context): address = self.address.resolve(context) or '' template_name = self.template_name.resolve(context) + if isinstance(address, basestring): + # if not exists the searched address then created an unsaved instance + try: + address = Address.objects.get(address=address) + except: + address = Address(address=address) - latitude = settings.EASY_MAPS_CENTER[0] or -34.397 - longitude = settings.EASY_MAPS_CENTER[1] or 150.644 + address = [address,] context.update({ - 'map': map, + 'markers': address, 'width': self.width, 'height': self.height, - 'latitude': latitude, - 'longitude': longitude, + # FIXME: if the list is empty? + 'latitude': address[0].latitude, + 'longitude': address[0].longitude, 'zoom': self.zoom, 'template_name': template_name }) From 22cf24ab5480606ff3d7ab0d53234c979c219224 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Thu, 3 Jan 2013 15:58:00 +0100 Subject: [PATCH 12/18] Remove single marker JS code. --- easy_maps/templates/easy_maps/map.html | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index a861570..9acc0b2 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -41,20 +41,6 @@ var map = new google.maps.Map(mapElem, mapOptions); {% block extra_js %} - var marker = new google.maps.Marker({ - position: latlng, - map: map, - draggable: true, - title: "{{ address }}" - }); - - // update the coordinate on marker dragging - google.maps.event.addListener(marker, 'dragend', function(evt) { - var ll = marker.getPosition(); - // FIXME: fix id names - document.getElementById('{{id}}latitude').value = ll.lat(); - document.getElementById('{{id}}longitude').value = ll.lng(); - }); // update the map and fields on click var button = document.getElementById("map-button-{{ id }}"); From 7ff1a64b5acc62f6c138f85d37e9af51b6737d79 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Wed, 2 Jan 2013 15:28:20 +0100 Subject: [PATCH 13/18] Remove useless fields. --- easy_maps/admin.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/easy_maps/admin.py b/easy_maps/admin.py index b3ca1cb..5cfc106 100644 --- a/easy_maps/admin.py +++ b/easy_maps/admin.py @@ -12,8 +12,6 @@ class AddressAdmin(admin.ModelAdmin): - list_display = ['address', 'computed_address', 'latitude', 'longitude', 'geocode_error'] - list_filter = ['geocode_error'] search_fields = ['address'] class form(forms.ModelForm): From 7a29c31bf075ecdb6b28997559d423550edbfb76 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Wed, 2 Jan 2013 15:52:01 +0100 Subject: [PATCH 14/18] Remove useless dictionary entry. --- easy_maps/templatetags/easy_maps_tags.py | 1 - 1 file changed, 1 deletion(-) diff --git a/easy_maps/templatetags/easy_maps_tags.py b/easy_maps/templatetags/easy_maps_tags.py index 49389ae..6a0c340 100644 --- a/easy_maps/templatetags/easy_maps_tags.py +++ b/easy_maps/templatetags/easy_maps_tags.py @@ -72,7 +72,6 @@ def render(self, context): 'latitude': address[0].latitude, 'longitude': address[0].longitude, 'zoom': self.zoom, - 'template_name': template_name }) return render_to_string(template_name, context_instance=context) except template.VariableDoesNotExist: From c100bf29bd189bbd07dd063d2e80955ac8265a11 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Thu, 3 Jan 2013 08:34:20 +0100 Subject: [PATCH 15/18] Use relative imports. --- easy_maps/templatetags/easy_maps_tags.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/easy_maps/templatetags/easy_maps_tags.py b/easy_maps/templatetags/easy_maps_tags.py index 6a0c340..0fc38e9 100644 --- a/easy_maps/templatetags/easy_maps_tags.py +++ b/easy_maps/templatetags/easy_maps_tags.py @@ -1,7 +1,7 @@ #coding: utf-8 from django import template from django.template.loader import render_to_string -from easy_maps.models import Address +from ..models import Address from .. import settings register = template.Library() From f19cbb9a30634e614f33ce38310c3e21ddc96291 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Thu, 3 Jan 2013 15:50:40 +0100 Subject: [PATCH 16/18] Factorize form definition. --- easy_maps/admin.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/easy_maps/admin.py b/easy_maps/admin.py index 5cfc106..59843bc 100644 --- a/easy_maps/admin.py +++ b/easy_maps/admin.py @@ -10,15 +10,15 @@ import simplejson +class AddressAdminForm(forms.ModelForm): + class Meta: + widgets = { + 'address': AddressWithMapWidget({'class': 'vTextField'}) + } class AddressAdmin(admin.ModelAdmin): search_fields = ['address'] - - class form(forms.ModelForm): - class Meta: - widgets = { - 'address': AddressWithMapWidget({'class': 'vTextField'}) - } + form = AddressAdminForm def get_urls(self): """Add a view that serves geolocalized data on POST request @@ -49,12 +49,6 @@ def get_geo(self, request): } ), content_type='application/json') -class AddressAdminForm(forms.ModelForm): - class Meta: - widgets = { - 'address': AddressWithMapWidget({'class': 'vTextField'}) - } - class AddressInlineAdmin(admin.StackedInline): extra = 1 form = AddressAdminForm From dc3be04c631c97eda065f37cd330a8c223d7f788 Mon Sep 17 00:00:00 2001 From: Gianluca Pacchiella Date: Thu, 3 Jan 2013 17:20:49 +0100 Subject: [PATCH 17/18] Move callback function to the button. --- easy_maps/static/js/easy_maps.js | 38 ++++++++++++++++++++++++-- easy_maps/templates/easy_maps/map.html | 35 ------------------------ easy_maps/widgets.py | 2 +- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/easy_maps/static/js/easy_maps.js b/easy_maps/static/js/easy_maps.js index 67bfcb2..8d17cd3 100644 --- a/easy_maps/static/js/easy_maps.js +++ b/easy_maps/static/js/easy_maps.js @@ -1,13 +1,45 @@ // will contain the boundary of the map. var g_lat_long_bound = new google.maps.LatLngBounds(); -function easy_map_add_listener(id, marker) { +function easy_maps_set_form_value(id_prefix) { + return function (computed_address, lat, lng, error) { + document.getElementById(id_prefix + 'computed_address').value = computed_address; + document.getElementById(id_prefix + 'latitude').value = lat + document.getElementById(id_prefix + 'longitude').value = lng; + document.getElementById(id_prefix + 'geocode_error').value = error; + }; +} + +function easy_maps_bind_button (id_prefix) { + django.jQuery.post( + // FIXME: this is hardcoded + '/admin/easy_maps/address/geo/', { + //'{% url admin:address_json %}', { + 'address': document.getElementById(id_prefix + 'address').value + }, + function(data) { + easy_maps_set_form_value(id_prefix)( + data["computed_address"], + data["latitude"], + data["longitude"], + data["geocode_error"] + ); + var center = new google.maps.LatLng(data["latitude"], data["longitude"]); + marker.setPosition(center); + map.setCenter(center); + } + ); + + return false; +} + +function easy_maps_add_listener(id_prefix, marker) { // update the coordinate on marker dragging google.maps.event.addListener(marker, 'dragend', function(evt) { var ll = marker.getPosition(); // FIXME: fix id names - document.getElementById(id + 'latitude').value = ll.lat(); - document.getElementById(id + 'longitude').value = ll.lng(); + document.getElementById(id_prefix + 'latitude').value = ll.lat(); + document.getElementById(id_prefix + 'longitude').value = ll.lng(); }); } diff --git a/easy_maps/templates/easy_maps/map.html b/easy_maps/templates/easy_maps/map.html index 9acc0b2..7540297 100644 --- a/easy_maps/templates/easy_maps/map.html +++ b/easy_maps/templates/easy_maps/map.html @@ -18,15 +18,6 @@ {% block map_js %}