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-single.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-map.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 = []
|
---|
225 | for map in self.maps: icons.extend(map.icons)
|
---|
226 | return icons
|
---|