|
0
|
1 |
from django.conf import settings |
|
|
2 |
from django.contrib.gis import geos |
|
|
3 |
from django.template.loader import render_to_string |
|
|
4 |
from django.utils.safestring import mark_safe |
|
|
5 |
|
|
|
6 |
class GoogleMapException(Exception): pass |
|
|
7 |
from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker, GIcon |
|
|
8 |
|
|
|
9 |
# The default Google Maps URL (for the API javascript) |
|
|
10 |
# TODO: Internationalize for Japan, UK, etc. |
|
|
11 |
GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&v=%s&key=' |
|
|
12 |
|
|
|
13 |
class GoogleMap(object): |
|
|
14 |
"A class for generating Google Maps JavaScript." |
|
|
15 |
|
|
|
16 |
# String constants |
|
|
17 |
onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps |
|
|
18 |
vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML |
|
|
19 |
xmlns = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML). |
|
|
20 |
|
|
|
21 |
def __init__(self, key=None, api_url=None, version=None, |
|
|
22 |
center=None, zoom=None, dom_id='map', |
|
|
23 |
kml_urls=[], polylines=None, polygons=None, markers=None, |
|
|
24 |
template='gis/google/google-map.js', |
|
|
25 |
js_module='geodjango', |
|
|
26 |
extra_context={}): |
|
|
27 |
|
|
|
28 |
# The Google Maps API Key defined in the settings will be used |
|
|
29 |
# if not passed in as a parameter. The use of an API key is |
|
|
30 |
# _required_. |
|
|
31 |
if not key: |
|
|
32 |
try: |
|
|
33 |
self.key = settings.GOOGLE_MAPS_API_KEY |
|
|
34 |
except AttributeError: |
|
|
35 |
raise GoogleMapException('Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).') |
|
|
36 |
else: |
|
|
37 |
self.key = key |
|
|
38 |
|
|
|
39 |
# Getting the Google Maps API version, defaults to using the latest ("2.x"), |
|
|
40 |
# this is not necessarily the most stable. |
|
|
41 |
if not version: |
|
|
42 |
self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x') |
|
|
43 |
else: |
|
|
44 |
self.version = version |
|
|
45 |
|
|
|
46 |
# Can specify the API URL in the `api_url` keyword. |
|
|
47 |
if not api_url: |
|
|
48 |
self.api_url = mark_safe(getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version) |
|
|
49 |
else: |
|
|
50 |
self.api_url = api_url |
|
|
51 |
|
|
|
52 |
# Setting the DOM id of the map, the load function, the JavaScript |
|
|
53 |
# template, and the KML URLs array. |
|
|
54 |
self.dom_id = dom_id |
|
|
55 |
self.extra_context = extra_context |
|
|
56 |
self.js_module = js_module |
|
|
57 |
self.template = template |
|
|
58 |
self.kml_urls = kml_urls |
|
|
59 |
|
|
|
60 |
# Does the user want any GMarker, GPolygon, and/or GPolyline overlays? |
|
|
61 |
overlay_info = [[GMarker, markers, 'markers'], |
|
|
62 |
[GPolygon, polygons, 'polygons'], |
|
|
63 |
[GPolyline, polylines, 'polylines']] |
|
|
64 |
|
|
|
65 |
for overlay_class, overlay_list, varname in overlay_info: |
|
|
66 |
setattr(self, varname, []) |
|
|
67 |
if overlay_list: |
|
|
68 |
for overlay in overlay_list: |
|
|
69 |
if isinstance(overlay, overlay_class): |
|
|
70 |
getattr(self, varname).append(overlay) |
|
|
71 |
else: |
|
|
72 |
getattr(self, varname).append(overlay_class(overlay)) |
|
|
73 |
|
|
|
74 |
# If GMarker, GPolygons, and/or GPolylines are used the zoom will be |
|
|
75 |
# automatically calculated via the Google Maps API. If both a zoom |
|
|
76 |
# level and a center coordinate are provided with polygons/polylines, |
|
|
77 |
# no automatic determination will occur. |
|
|
78 |
self.calc_zoom = False |
|
|
79 |
if self.polygons or self.polylines or self.markers: |
|
|
80 |
if center is None or zoom is None: |
|
|
81 |
self.calc_zoom = True |
|
|
82 |
|
|
|
83 |
# Defaults for the zoom level and center coordinates if the zoom |
|
|
84 |
# is not automatically calculated. |
|
|
85 |
if zoom is None: zoom = 4 |
|
|
86 |
self.zoom = zoom |
|
|
87 |
if center is None: center = (0, 0) |
|
|
88 |
self.center = center |
|
|
89 |
|
|
|
90 |
def render(self): |
|
|
91 |
""" |
|
|
92 |
Generates the JavaScript necessary for displaying this Google Map. |
|
|
93 |
""" |
|
|
94 |
params = {'calc_zoom' : self.calc_zoom, |
|
|
95 |
'center' : self.center, |
|
|
96 |
'dom_id' : self.dom_id, |
|
|
97 |
'js_module' : self.js_module, |
|
|
98 |
'kml_urls' : self.kml_urls, |
|
|
99 |
'zoom' : self.zoom, |
|
|
100 |
'polygons' : self.polygons, |
|
|
101 |
'polylines' : self.polylines, |
|
|
102 |
'icons': self.icons, |
|
|
103 |
'markers' : self.markers, |
|
|
104 |
} |
|
|
105 |
params.update(self.extra_context) |
|
|
106 |
return render_to_string(self.template, params) |
|
|
107 |
|
|
|
108 |
@property |
|
|
109 |
def body(self): |
|
|
110 |
"Returns HTML body tag for loading and unloading Google Maps javascript." |
|
|
111 |
return mark_safe('<body %s %s>' % (self.onload, self.onunload)) |
|
|
112 |
|
|
|
113 |
@property |
|
|
114 |
def onload(self): |
|
|
115 |
"Returns the `onload` HTML <body> attribute." |
|
|
116 |
return mark_safe('onload="%s.%s_load()"' % (self.js_module, self.dom_id)) |
|
|
117 |
|
|
|
118 |
@property |
|
|
119 |
def api_script(self): |
|
|
120 |
"Returns the <script> tag for the Google Maps API javascript." |
|
|
121 |
return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key)) |
|
|
122 |
|
|
|
123 |
@property |
|
|
124 |
def js(self): |
|
|
125 |
"Returns only the generated Google Maps JavaScript (no <script> tags)." |
|
|
126 |
return self.render() |
|
|
127 |
|
|
|
128 |
@property |
|
|
129 |
def scripts(self): |
|
|
130 |
"Returns all <script></script> tags required with Google Maps JavaScript." |
|
|
131 |
return mark_safe('%s\n <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n </script>' % (self.api_script, self.js)) |
|
|
132 |
|
|
|
133 |
@property |
|
|
134 |
def style(self): |
|
|
135 |
"Returns additional CSS styling needed for Google Maps on IE." |
|
|
136 |
return mark_safe('<style type="text/css">%s</style>' % self.vml_css) |
|
|
137 |
|
|
|
138 |
@property |
|
|
139 |
def xhtml(self): |
|
|
140 |
"Returns XHTML information needed for IE VML overlays." |
|
|
141 |
return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns) |
|
|
142 |
|
|
|
143 |
@property |
|
|
144 |
def icons(self): |
|
|
145 |
"Returns a sequence of GIcon objects in this map." |
|
|
146 |
return set([marker.icon for marker in self.markers if marker.icon]) |
|
|
147 |
|
|
|
148 |
class GoogleMapSet(GoogleMap): |
|
|
149 |
|
|
|
150 |
def __init__(self, *args, **kwargs): |
|
|
151 |
""" |
|
|
152 |
A class for generating sets of Google Maps that will be shown on the |
|
|
153 |
same page together. |
|
|
154 |
|
|
|
155 |
Example: |
|
|
156 |
gmapset = GoogleMapSet( GoogleMap( ... ), GoogleMap( ... ) ) |
|
|
157 |
gmapset = GoogleMapSet( [ gmap1, gmap2] ) |
|
|
158 |
""" |
|
|
159 |
# The `google-multi.js` template is used instead of `google-single.js` |
|
|
160 |
# by default. |
|
|
161 |
template = kwargs.pop('template', 'gis/google/google-multi.js') |
|
|
162 |
|
|
|
163 |
# This is the template used to generate the GMap load JavaScript for |
|
|
164 |
# each map in the set. |
|
|
165 |
self.map_template = kwargs.pop('map_template', 'gis/google/google-single.js') |
|
|
166 |
|
|
|
167 |
# Running GoogleMap.__init__(), and resetting the template |
|
|
168 |
# value with default obtained above. |
|
|
169 |
super(GoogleMapSet, self).__init__(**kwargs) |
|
|
170 |
self.template = template |
|
|
171 |
|
|
|
172 |
# If a tuple/list passed in as first element of args, then assume |
|
|
173 |
if isinstance(args[0], (tuple, list)): |
|
|
174 |
self.maps = args[0] |
|
|
175 |
else: |
|
|
176 |
self.maps = args |
|
|
177 |
|
|
|
178 |
# Generating DOM ids for each of the maps in the set. |
|
|
179 |
self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))] |
|
|
180 |
|
|
|
181 |
def load_map_js(self): |
|
|
182 |
""" |
|
|
183 |
Returns JavaScript containing all of the loading routines for each |
|
|
184 |
map in this set. |
|
|
185 |
""" |
|
|
186 |
result = [] |
|
|
187 |
for dom_id, gmap in zip(self.dom_ids, self.maps): |
|
|
188 |
# Backup copies the GoogleMap DOM id and template attributes. |
|
|
189 |
# They are overridden on each GoogleMap instance in the set so |
|
|
190 |
# that only the loading JavaScript (and not the header variables) |
|
|
191 |
# is used with the generated DOM ids. |
|
|
192 |
tmp = (gmap.template, gmap.dom_id) |
|
|
193 |
gmap.template = self.map_template |
|
|
194 |
gmap.dom_id = dom_id |
|
|
195 |
result.append(gmap.js) |
|
|
196 |
# Restoring the backup values. |
|
|
197 |
gmap.template, gmap.dom_id = tmp |
|
|
198 |
return mark_safe(''.join(result)) |
|
|
199 |
|
|
|
200 |
def render(self): |
|
|
201 |
""" |
|
|
202 |
Generates the JavaScript for the collection of Google Maps in |
|
|
203 |
this set. |
|
|
204 |
""" |
|
|
205 |
params = {'js_module' : self.js_module, |
|
|
206 |
'dom_ids' : self.dom_ids, |
|
|
207 |
'load_map_js' : self.load_map_js(), |
|
|
208 |
'icons' : self.icons, |
|
|
209 |
} |
|
|
210 |
params.update(self.extra_context) |
|
|
211 |
return render_to_string(self.template, params) |
|
|
212 |
|
|
|
213 |
@property |
|
|
214 |
def onload(self): |
|
|
215 |
"Returns the `onload` HTML <body> attribute." |
|
|
216 |
# Overloaded to use the `load` function defined in the |
|
|
217 |
# `google-multi.js`, which calls the load routines for |
|
|
218 |
# each one of the individual maps in the set. |
|
|
219 |
return mark_safe('onload="%s.load()"' % self.js_module) |
|
|
220 |
|
|
|
221 |
@property |
|
|
222 |
def icons(self): |
|
|
223 |
"Returns a sequence of all icons in each map of the set." |
|
|
224 |
icons = set() |
|
|
225 |
for map in self.maps: icons |= map.icons |
|
|
226 |
return icons |