Fixed #9955 -- Added `GoogleMapSet`, which enables multiple `GoogleMap` objects to placed on the same page; moved all templates to `google` parent dir, and added 'google-map.html' for an included example. Thanks to mandric for the ticket and aromano for initial patch.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10011 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2009-03-09 19:22:24 +00:00
parent a7ff8b79ff
commit d45e24a1f8
8 changed files with 152 additions and 53 deletions

View File

@ -56,6 +56,6 @@
* GOOGLE_MAPS_URL (optional): Must have a substitution ('%s') for the API * GOOGLE_MAPS_URL (optional): Must have a substitution ('%s') for the API
version. version.
""" """
from django.contrib.gis.maps.google.gmap import GoogleMap from django.contrib.gis.maps.google.gmap import GoogleMap, GoogleMapSet
from django.contrib.gis.maps.google.overlays import GEvent, GMarker, GPolygon, GPolyline from django.contrib.gis.maps.google.overlays import GEvent, GMarker, GPolygon, GPolyline
from django.contrib.gis.maps.google.zoom import GoogleZoom from django.contrib.gis.maps.google.zoom import GoogleZoom

View File

@ -19,14 +19,14 @@ class GoogleMap(object):
xmlns = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML). xmlns = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML).
def __init__(self, key=None, api_url=None, version=None, def __init__(self, key=None, api_url=None, version=None,
center=None, zoom=None, dom_id='map', load_func='gmap_load', center=None, zoom=None, dom_id='map',
kml_urls=[], polygons=[], polylines=[], markers=[], kml_urls=[], polygons=[], polylines=[], markers=[],
template='gis/google/js/google-map.js', template='gis/google/google-single.js',
extra_context={}): js_module='geodjango',extra_context={}):
# The Google Maps API Key defined in the settings will be used # The Google Maps API Key defined in the settings will be used
# if not passed in as a parameter. The use of an API key is # if not passed in as a parameter. The use of an API key is
# _required_. # _required_.
if not key: if not key:
try: try:
self.key = settings.GOOGLE_MAPS_API_KEY self.key = settings.GOOGLE_MAPS_API_KEY
@ -36,7 +36,7 @@ class GoogleMap(object):
self.key = key self.key = key
# Getting the Google Maps API version, defaults to using the latest ("2.x"), # Getting the Google Maps API version, defaults to using the latest ("2.x"),
# this is not necessarily the most stable. # this is not necessarily the most stable.
if not version: if not version:
self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x') self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x')
else: else:
@ -51,7 +51,8 @@ class GoogleMap(object):
# Setting the DOM id of the map, the load function, the JavaScript # Setting the DOM id of the map, the load function, the JavaScript
# template, and the KML URLs array. # template, and the KML URLs array.
self.dom_id = dom_id self.dom_id = dom_id
self.load_func = load_func self.extra_context = extra_context
self.js_module = js_module
self.template = template self.template = template
self.kml_urls = kml_urls self.kml_urls = kml_urls
@ -76,11 +77,10 @@ class GoogleMap(object):
else: else:
self.polylines.append(GPolyline(pline)) self.polylines.append(GPolyline(pline))
# If GMarker, GPolygons, and/or GPolylines # If GMarker, GPolygons, and/or GPolylines are used the zoom will be
# are used the zoom will be automatically # automatically calculated via the Google Maps API. If both a zoom
# calculated via the Google Maps API. If both a zoom level and a # level and a center coordinate are provided with polygons/polylines,
# center coordinate are provided with polygons/polylines, no automatic # no automatic determination will occur.
# determination will occur.
self.calc_zoom = False self.calc_zoom = False
if self.polygons or self.polylines or self.markers: if self.polygons or self.polylines or self.markers:
if center is None or zoom is None: if center is None or zoom is None:
@ -93,19 +93,22 @@ class GoogleMap(object):
if center is None: center = (0, 0) if center is None: center = (0, 0)
self.center = center self.center = center
# Setting the parameters for the javascript template. def render(self):
"""
Generates the JavaScript necessary for displaying this Google Map.
"""
params = {'calc_zoom' : self.calc_zoom, params = {'calc_zoom' : self.calc_zoom,
'center' : self.center, 'center' : self.center,
'dom_id' : self.dom_id, 'dom_id' : self.dom_id,
'js_module' : self.js_module,
'kml_urls' : self.kml_urls, 'kml_urls' : self.kml_urls,
'load_func' : self.load_func,
'zoom' : self.zoom, 'zoom' : self.zoom,
'polygons' : self.polygons, 'polygons' : self.polygons,
'polylines' : self.polylines, 'polylines' : self.polylines,
'markers' : self.markers, 'markers' : self.markers,
} }
params.update(extra_context) params.update(self.extra_context)
self.js = render_to_string(self.template, params) return render_to_string(self.template, params)
@property @property
def body(self): def body(self):
@ -115,16 +118,21 @@ class GoogleMap(object):
@property @property
def onload(self): def onload(self):
"Returns the `onload` HTML <body> attribute." "Returns the `onload` HTML <body> attribute."
return mark_safe('onload="%s()"' % self.load_func) return mark_safe('onload="%s.%s_load()"' % (self.js_module, self.dom_id))
@property @property
def api_script(self): def api_script(self):
"Returns the <script> tag for the Google Maps API javascript." "Returns the <script> tag for the Google Maps API javascript."
return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key)) return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key))
@property
def js(self):
"Returns only the generated Google Maps JavaScript (no <script> tags)."
return self.render()
@property @property
def scripts(self): def scripts(self):
"Returns all <script></script> tags required for Google Maps JavaScript." "Returns all <script></script> tags required with Google Maps JavaScript."
return mark_safe('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>' % (self.api_script, self.js)) return mark_safe('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>' % (self.api_script, self.js))
@property @property
@ -136,3 +144,75 @@ class GoogleMap(object):
def xhtml(self): def xhtml(self):
"Returns XHTML information needed for IE VML overlays." "Returns XHTML information needed for IE VML overlays."
return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns) return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns)
class GoogleMapSet(GoogleMap):
def __init__(self, *args, **kwargs):
"""
A class for generating sets of Google Maps that will be shown on the
same page together.
Example:
gmapset = GoogleMapSet( GoogleMap( ... ), GoogleMap( ... ) )
gmapset = GoogleMapSet( [ gmap1, gmap2] )
"""
# The `google-multi.js` template is used instead of `google-single.js`
# by default.
template = kwargs.pop('template', 'gis/google/google-multi.js')
# This is the template used to generate the GMap load JavaScript for
# each map in the set.
self.map_template = kwargs.pop('map_template', 'gis/google/google-map.js')
# Running GoogleMap.__init__(), and resetting the template
# value with default obtained above.
super(GoogleMapSet, self).__init__(**kwargs)
self.template = template
# If a tuple/list passed in as first element of args, then assume
if isinstance(args[0], (tuple, list)):
self.maps = args[0]
else:
self.maps = args
# Generating DOM ids for each of the maps in the set.
self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))]
def load_map_js(self):
"""
Returns JavaScript containing all of the loading routines for each
map in this set.
"""
result = []
for dom_id, gmap in zip(self.dom_ids, self.maps):
# Backup copies the GoogleMap DOM id and template attributes.
# They are overridden on each GoogleMap instance in the set so
# that only the loading JavaScript (and not the header variables)
# is used with the generated DOM ids.
tmp = (gmap.template, gmap.dom_id)
gmap.template = self.map_template
gmap.dom_id = dom_id
result.append(gmap.js)
# Restoring the backup values.
gmap.template, gmap.dom_id = tmp
return mark_safe(''.join(result))
def render(self):
"""
Generates the JavaScript for the collection of Google Maps in
this set.
"""
params = {'js_module' : self.js_module,
'dom_ids' : self.dom_ids,
'load_map_js' : self.load_map_js(),
}
params.update(self.extra_context)
return render_to_string(self.template, params)
@property
def onload(self):
"Returns the `onload` HTML <body> attribute."
# Overloaded to use the `load` function defined in the
# `google-multi.js`, which calls the load routines for
# each one of the individual maps in the set.
return mark_safe('onload="%s.load()"' % self.js_module)

View File

@ -0,0 +1,2 @@
{% block vars %}var geodjango = {};{% endblock %}
{% block functions %}{% endblock %}

View File

@ -0,0 +1,12 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" {{ gmap.xmlns }}>
<head>
<title>{% block title %}Google Maps via GeoDjango{% endblock %}</title>
{{ gmap.style }}
{{ gmap.scripts }}
</head>
<body {{ gmap.onload }} {{ gmap.onunload }}>
{% if gmap.dom_ids %}{% for dom_id in gmap.dom_ids %}<div id="{{ dom_id }}" style="width:600px;height:400px;"></div>{% endfor %}
{% else %}<div id="{{ gmap.dom_id }}" style="width:600px;height:400px;"></div>{% endif %}
</body>
</html>

View File

@ -0,0 +1,29 @@
{% autoescape off %}
{% block load %}{{ js_module }}.{{ dom_id }}_load = function(){
if (GBrowserIsCompatible()) {
{{ js_module }}.{{ dom_id }} = new GMap2(document.getElementById("{{ dom_id }}"));
{{ js_module }}.{{ dom_id }}.setCenter(new GLatLng({{ center.1 }}, {{ center.0 }}), {{ zoom }});
{% block controls %}{{ js_module }}.{{ dom_id }}.addControl(new GSmallMapControl());
{{ js_module }}.{{ dom_id }}.addControl(new GMapTypeControl());{% endblock %}
{% if calc_zoom %}var bounds = new GLatLngBounds(); var tmp_bounds = new GLatLngBounds();{% endif %}
{% for kml_url in kml_urls %}{{ js_module }}.{{ dom_id }}_kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_kml{{ forloop.counter }});{% endfor %}
{% for polygon in polygons %}{{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }} = new {{ polygon }};
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }});
{% for event in polygon.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_poly{{ forloop.parentloop.counter }}, {{ event }});{% endfor %}
{% if calc_zoom %}tmp_bounds = {{ js_module }}.{{ dom_id }}_poly{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
{% for polyline in polylines %}{{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }} = new {{ polyline }};
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }});
{% for event in polyline.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_polyline{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
{% if calc_zoom %}tmp_bounds = {{ js_module }}.{{ dom_id }}_polyline{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
{% for marker in markers %}{{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }} = new {{ marker }};
{{ js_module }}.{{ dom_id }}.addOverlay({{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }});
{% for event in marker.events %}GEvent.addListener({{ js_module }}.{{ dom_id }}_marker{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
{% if calc_zoom %}bounds.extend({{ js_module }}.{{ dom_id }}_marker{{ forloop.counter }}.getLatLng()); {% endif %}{% endfor %}
{% if calc_zoom %}{{ js_module }}.{{ dom_id }}.setCenter(bounds.getCenter(), {{ js_module }}.{{ dom_id }}.getBoundsZoomLevel(bounds));{% endif %}
{% block load_extra %}{% endblock %}
}else {
alert("Sorry, the Google Maps API is not compatible with this browser.");
}
}
{% endblock %}{% endautoescape %}

View File

@ -0,0 +1,8 @@
{% extends "gis/google/google-base.js" %}
{% block functions %}
{{ load_map_js }}
{{ js_module }}.load = function(){
{% for dom_id in dom_ids %}{{ js_module }}.{{ dom_id }}_load();
{% endfor %}
}
{% endblock %}

View File

@ -0,0 +1,2 @@
{% extends "gis/google/google-base.js" %}
{% block functions %}{% include "gis/google/google-map.js" %}{% endblock %}

View File

@ -1,34 +0,0 @@
{% autoescape off %}{% block vars %}var map;{% endblock %}
{% block functions %}{% endblock %}
{% block load %}function {{ load_func }}(){
if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("{{ dom_id }}"));
map.setCenter(new GLatLng({{ center.1 }}, {{ center.0 }}), {{ zoom }});
{% block controls %}map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());{% endblock %}
{% if calc_zoom %}var bounds = new GLatLngBounds(); var tmp_bounds = new GLatLngBounds();{% endif %}
{% for kml_url in kml_urls %}var kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
map.addOverlay(kml{{ forloop.counter }});{% endfor %}
{% for polygon in polygons %}var poly{{ forloop.counter }} = new {{ polygon }};
map.addOverlay(poly{{ forloop.counter }});
{% for event in polygon.events %}GEvent.addListener(poly{{ forloop.parentloop.counter }}, {{ event }});{% endfor %}
{% if calc_zoom %}tmp_bounds = poly{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
{% for polyline in polylines %}var polyline{{ forloop.counter }} = new {{ polyline }};
map.addOverlay(polyline{{ forloop.counter }});
{% for event in polyline.events %}GEvent.addListener(polyline{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
{% if calc_zoom %}tmp_bounds = polyline{{ forloop.counter }}.getBounds(); bounds.extend(tmp_bounds.getSouthWest()); bounds.extend(tmp_bounds.getNorthEast());{% endif %}{% endfor %}
{% for marker in markers %}var marker{{ forloop.counter }} = new {{ marker }};
map.addOverlay(marker{{ forloop.counter }});
{% for event in marker.events %}GEvent.addListener(marker{{ forloop.parentloop.counter }}, {{ event }}); {% endfor %}
{% if calc_zoom %}bounds.extend(marker{{ forloop.counter }}.getLatLng()); {% endif %}{% endfor %}
{% if calc_zoom %}map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));{% endif %}
{% block load_extra %}{% endblock %}
}else {
alert("Sorry, the Google Maps API is not compatible with this browser.");
}
}
{% endblock %}{% endautoescape %}