Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

gis: Fixed #6746 by marking safe `GoogleMap` internal XHTML/JavaScrip…

…t; added support for `GPolygon` and `GPolyline` overlays via the `polygons` and `polylines` keywords; the `zoom` keyword may now take a geometry for automatic zoom level determination; *.pyc files are now ignored in `django.contrib.gis` modules.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7213 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit ae0e5835107ff6aa5fe5f4b44fadf318d843462a 1 parent 22e016f
Justin Bronn authored
7  django/contrib/gis/maps/google/__init__.py
@@ -19,7 +19,7 @@
19 19
        {{ google.scripts }}
20 20
      </head>
21 21
      {{ google.body }}
22  
-     <div id="{{ google.dom_id }}"></div>
  22
+     <div id="{{ google.dom_id }}" style="width:600px;height:400px;"></div>
23 23
      </body>
24 24
      </html>
25 25
 
@@ -47,7 +47,7 @@
47 47
      body tag to load the generated javascript.  By default, returns:
48 48
      <body onload="gmap_load()" onunload="GUnload()">
49 49
 
50  
-   - The `id` property returns the DOM id for the map.  Defaults to "map".
  50
+   - The `dom_id` property returns the DOM id for the map.  Defaults to "map".
51 51
 
52 52
   The following attributes may be set or customized in your local settings:
53 53
    * GOOGLE_MAPS_API_KEY: String of your Google Maps API key.  These are tied to
@@ -56,5 +56,6 @@
56 56
    * GOOGLE_MAPS_URL (optional): Must have a substitution ('%s') for the API
57 57
       version.
58 58
 """
59  
-from django.contrib.gis.maps.google.gmap import GoogleMap
  59
+from django.contrib.gis.maps.google.gmap import GoogleMap, GZOOM
  60
+from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline
60 61
 from django.contrib.gis.maps.google.zoom import GoogleZoom
120  django/contrib/gis/maps/google/gmap.py
... ...
@@ -1,24 +1,32 @@
1 1
 from django.conf import settings
  2
+from django.contrib.gis import geos
2 3
 from django.template.loader import render_to_string
  4
+from django.utils.safestring import mark_safe
  5
+
  6
+# Declaring the GoogleMapException prior to getting the 
  7
+# default `GZOOM` GoogleZoom instance.
  8
+class GoogleMapException(Exception): pass
  9
+from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline
  10
+from django.contrib.gis.maps.google.zoom import GoogleZoom
  11
+GZOOM = GoogleZoom()
3 12
 
4 13
 # The default Google Maps URL (for the API javascript)
5 14
 # TODO: Internationalize for Japan, UK, etc.
6 15
 GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&amp;v=%s&amp;key='
7 16
 
8  
-class GoogleMapException(Exception): pass
9  
-        
10 17
 class GoogleMap(object):
11  
-    "A class for generating Google Maps javascript."
  18
+    "A class for generating Google Maps JavaScript."
12 19
 
13 20
     # String constants
14  
-    onunload = 'onunload="GUnload()"' # Cleans up after Google Maps
15  
-    vml_css  = 'v\:* {behavior:url(#default#VML);}' # CSS for IE VML
16  
-    xmlns    = 'xmlns:v="urn:schemas-microsoft-com:vml"' # XML Namespace (for IE VML).
  21
+    onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps
  22
+    vml_css  = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML
  23
+    xmlns    = mark_safe('xmlns:v="urn:schemas-microsoft-com:vml"') # XML Namespace (for IE VML).
17 24
 
18 25
     def __init__(self, key=None, api_url=None, version=None,
19  
-                 center_lat=0.0, center_lon=0.0, zoom=1, 
20  
-                 dom_id='map', load_func='gmap_load', 
21  
-                 kml_urls=[], template='gis/google/js/google-map.js',
  26
+                 center=None, center_lat=0.0, center_lon=0.0,
  27
+                 zoom=None, dom_id='map', load_func='gmap_load', 
  28
+                 kml_urls=[], polygons=[], polylines=[],
  29
+                 template='gis/google/js/google-map.js',
22 30
                  extra_context={}):
23 31
 
24 32
         # The Google Maps API Key defined in the settings will be used
@@ -26,77 +34,109 @@ def __init__(self, key=None, api_url=None, version=None,
26 34
         #  _required_.
27 35
         if not key:
28 36
             try:
29  
-                self._key = settings.GOOGLE_MAPS_API_KEY
  37
+                self.key = settings.GOOGLE_MAPS_API_KEY
30 38
             except AttributeError:
31  
-                raise GoogleMapException, 'Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).'
  39
+                raise GoogleMapException('Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).')
32 40
         else:
33  
-            self._key = key
  41
+            self.key = key
34 42
         
35 43
         # Getting the Google Maps API version, defaults to using the latest ("2.x"),
36 44
         #  this is not necessarily the most stable.
37 45
         if not version:
38  
-            try:
39  
-                self._version = settings.GOOGLE_MAPS_API_VERSION
40  
-            except AttributeError:
41  
-                self._version = '2.x'
  46
+            self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x')
42 47
         else:
43  
-            self._version = version
  48
+            self.version = version
44 49
 
45 50
         # Can specify the API URL in the `api_url` keyword.
46 51
         if not api_url:
47  
-            try:
48  
-                self._url = settings.GOOGLE_MAPS_URL % self._version
49  
-            except AttributeError:
50  
-                self._url = GOOGLE_MAPS_URL % self._version
  52
+            self.api_url = mark_safe(getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version)
51 53
         else:
52  
-            self._url = api_url
  54
+            self.api_url = api_url
53 55
 
54  
-        # Setting the DOM id of the map, the center lat/lon, the load function,
55  
-        #  and the zoom.
  56
+        # Setting the DOM id of the map, the load function, the JavaScript
  57
+        # template, and the KML URLs array.
56 58
         self.dom_id = dom_id
57  
-        self.center_lat = center_lat
58  
-        self.center_lon = center_lon
59 59
         self.load_func = load_func
60 60
         self.template = template
61  
-        self.zoom = zoom
  61
+        self.kml_urls = kml_urls
  62
+        
  63
+        # Does the user want any GPolygon or GPolyline overlays?
  64
+        self.polygons, self.polylines = [], []
  65
+        if polygons:
  66
+            for poly in polygons:
  67
+                if isinstance(poly, GPolygon): 
  68
+                    self.polygons.append(poly)
  69
+                else:
  70
+                    self.polygons.append(GPolygon(poly))
  71
+        if polylines:
  72
+            for pline in polylines:
  73
+                if isinstance(pline, GPolyline):
  74
+                    self.polylines.append(pline)
  75
+                else:
  76
+                    self.polylines.append(GPolyline(pline))
  77
+       
  78
+        # Automatically determining the zoom level if there are
  79
+        # GPolygon and/or GPolyline overlays.
  80
+        if (self.polygons or self.polylines) and zoom is None:
  81
+            envelopes = [p.envelope for p in self.polygons]
  82
+            envelopes.extend([p.envelope for p in self.polylines])
  83
+            # Creating a MultiPolygon of all the envelopes, this will
  84
+            # be used in determining the zoom level.
  85
+            zoom = geos.MultiPolygon(envelopes)
  86
+            zoom.srid = 4326
  87
+        
  88
+        # If a GEOSGeometry object is passed in for the `zoom` keyword
  89
+        # argument, then try to automatically determine an appropriate
  90
+        # zoom level.
  91
+        if isinstance(zoom, geos.GEOSGeometry):
  92
+            self.zoom = GZOOM.get_zoom(zoom)
  93
+        else:
  94
+            self.zoom = zoom
  95
+
  96
+        # The map center coordinate -- the `center_lon` and `center_lat` keyword
  97
+        # are deprecated.
  98
+        if not center:
  99
+            center = (center_lon, center_lat)
  100
+        self.center = center
62 101
 
63 102
         # Setting the parameters for the javascript template.
64  
-        params = {'center_lat' : center_lat,
65  
-                  'center_lon' : center_lon,
66  
-                  'dom_id' : dom_id,
67  
-                  'kml_urls' : kml_urls,
68  
-                  'load_func' : load_func,
69  
-                  'zoom' : zoom,
  103
+        params = {'center' : self.center,
  104
+                  'dom_id' : self.dom_id,
  105
+                  'kml_urls' : self.kml_urls,
  106
+                  'load_func' : self.load_func,
  107
+                  'zoom' : self.zoom,
  108
+                  'polygons' : self.polygons,
  109
+                  'polylines' : self.polylines,
70 110
                   }
71 111
         params.update(extra_context)
72  
-        self.js = render_to_string(template, params)
  112
+        self.js = render_to_string(self.template, params)
73 113
 
74 114
     @property
75 115
     def body(self):
76 116
         "Returns HTML body tag for loading and unloading Google Maps javascript."
77  
-        return '<body %s %s>' % (self.onload, self.onunload)
  117
+        return mark_safe('<body %s %s>' % (self.onload, self.onunload))
78 118
 
79 119
     @property
80 120
     def onload(self):
81 121
         "Returns the `onload` HTML <body> attribute."
82  
-        return 'onload="%s()"' % self.load_func
  122
+        return mark_safe('onload="%s()"' % self.load_func)
83 123
 
84 124
     @property
85 125
     def api_script(self):
86 126
         "Returns the <script> tag for the Google Maps API javascript."
87  
-        return '<script src="%s%s" type="text/javascript"></script>' % (self._url, self._key)
  127
+        return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key))
88 128
 
89 129
     @property
90 130
     def scripts(self):
91 131
         "Returns all <script></script> tags required for Google Maps JavaScript."
92  
-        return '%s\n  <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n  </script>' % (self.api_script, self.js)
  132
+        return mark_safe('%s\n  <script type="text/javascript">\n//<![CDATA[\n%s//]]>\n  </script>' % (self.api_script, self.js))
93 133
 
94 134
     @property
95 135
     def style(self):
96 136
         "Returns additional CSS styling needed for Google Maps on IE."
97  
-        return '<style type="text/css">%s</style>' % self.vml_css
  137
+        return mark_safe('<style type="text/css">%s</style>' % self.vml_css)
98 138
 
99 139
     @property
100 140
     def xhtml(self):
101 141
         "Returns XHTML information needed for IE VML overlays."
102  
-        return '<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns
  142
+        return mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" %s>' % self.xmlns)
101  django/contrib/gis/maps/google/overlays.py
... ...
@@ -0,0 +1,101 @@
  1
+from django.contrib.gis.geos import LineString, LinearRing, Polygon
  2
+from django.utils.safestring import mark_safe
  3
+
  4
+class GOverlayBase(object):
  5
+    def latlng_from_coords(self, coords):
  6
+        return '[%s]' % ','.join(['new GLatLng(%s,%s)' % (y, x) for x, y in coords])
  7
+
  8
+    def __unicode__(self):
  9
+        "The string representation is the JavaScript API call."
  10
+        return mark_safe('%s(%s)' % (self.__class__.__name__, self.js_params))
  11
+
  12
+class GPolygon(GOverlayBase):
  13
+    """
  14
+    A Python wrapper for the Google GPolygon object.  For more information
  15
+    please see the Google Maps API Reference:
  16
+     http://code.google.com/apis/maps/documentation/reference.html#GPolygon
  17
+    """
  18
+    def __init__(self, poly, 
  19
+                 stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1,
  20
+                 fill_color='#0000ff', fill_opacity=0.4):
  21
+        """
  22
+        The GPolygon object initializes on a GEOS Polygon.  Please note that
  23
+        this will not depict Polygons with internal rings.
  24
+
  25
+        Keyword Options:
  26
+
  27
+          stroke_color:
  28
+            The color of the polygon outline. Defaults to '#0000ff' (blue).
  29
+
  30
+          stroke_weight:
  31
+            The width of the polygon outline, in pixels.  Defaults to 2.
  32
+
  33
+          stroke_opacity:
  34
+            The opacity of the polygon outline, between 0 and 1.  Defaults to 1.
  35
+
  36
+          fill_color:
  37
+            The color of the polygon fill.  Defaults to '#0000ff' (blue).
  38
+
  39
+          fill_opacity:
  40
+            The opacity of the polygon fill.  Defaults to 0.4.
  41
+        """
  42
+
  43
+        # TODO: Take other types of geometries.
  44
+        if not isinstance(poly, Polygon): 
  45
+            raise TypeError('GPolygon may only initialize on GEOS Polygons.')
  46
+
  47
+        # Getting the envelope of the input polygon (used for automatically
  48
+        # determining the zoom level).
  49
+        self.envelope = poly.envelope
  50
+
  51
+        # Translating the coordinates into a JavaScript array of 
  52
+        # Google `GLatLng` objects.
  53
+        self.points = self.latlng_from_coords(poly.shell.coords)
  54
+
  55
+        # Stroke settings.
  56
+        self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight
  57
+      
  58
+        # Fill settings.
  59
+        self.fill_color, self.fill_opacity = fill_color, fill_opacity
  60
+        
  61
+    @property
  62
+    def js_params(self):
  63
+        return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity,
  64
+                                               self.fill_color, self.fill_opacity)
  65
+
  66
+class GPolyline(GOverlayBase):
  67
+    """
  68
+    A Python wrapper for the Google GPolyline object.  For more information
  69
+    please see the Google Maps API Reference:
  70
+     http://code.google.com/apis/maps/documentation/reference.html#GPolyline
  71
+    """
  72
+    def __init__(self, geom, color='#0000ff', weight=2, opacity=1):
  73
+        """
  74
+        The GPolyline object may initialize on GEOS LineStirng, LinearRing,
  75
+        and Polygon objects (internal rings not supported).  
  76
+
  77
+        Keyword Options:
  78
+          
  79
+          color:
  80
+            The color to use for the polyline.  Defaults to '#0000ff' (blue).
  81
+  
  82
+          weight:
  83
+            The width of the polyline, in pixels.  Defaults to 2.
  84
+
  85
+          opacity:
  86
+            The opacity of the polyline, between 0 and 1.  Defaults to 1.
  87
+        """
  88
+        if isinstance(geom, (LineString, LinearRing)):
  89
+            self.latlngs = self.latlng_from_coords(geom.coords)
  90
+        elif isinstance(geom, Polygon):
  91
+            self.latlngs = self.latlng_from_coords(geom.shell.coords)
  92
+        else:
  93
+            raise TypeError('GPolyline may only initialize on GEOS LineString, LinearRing, and/or Polygon geometries.')
  94
+
  95
+        # Getting the envelope for automatic zoom determination.
  96
+        self.envelope = geom.envelope
  97
+        self.color, self.weight, self.opacity = color, weight, opacity
  98
+        
  99
+    @property
  100
+    def js_params(self):
  101
+        return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)
10  django/contrib/gis/maps/google/zoom.py
@@ -99,7 +99,7 @@ def lonlat_to_pixel(self, lonlat, zoom):
99 99
     def pixel_to_lonlat(self, px, zoom):
100 100
         "Converts a pixel to a longitude, latitude pair at the given zoom level."
101 101
         if len(px) != 2:
102  
-            raise TypeError, 'Pixel should be a sequence of two elements.'
  102
+            raise TypeError('Pixel should be a sequence of two elements.')
103 103
 
104 104
         # Getting the number of pixels for the given zoom level.
105 105
         npix = self._npix[zoom]
@@ -138,8 +138,8 @@ def get_zoom(self, geom):
138 138
         "Returns the optimal Zoom level for the given geometry."
139 139
 
140 140
         # Checking the input type.
141  
-        if not isinstance(geom, GEOSGeometry) and geom.srid == 4326:
142  
-            raise TypeError, 'get_zoom() expects a GEOS Geometry with an SRID of 4326.'
  141
+        if not isinstance(geom, GEOSGeometry) or geom.srid != 4326:
  142
+            raise TypeError('get_zoom() expects a GEOS Geometry with an SRID of 4326.')
143 143
 
144 144
         # Getting the envelope for the geometry, and its associated width, height
145 145
         #  and centroid.
@@ -156,9 +156,9 @@ def get_zoom(self, geom):
156 156
             #  zoom level.
157 157
             if (env_w > tile_w) or (env_h > tile_h):
158 158
                 if z == 0: 
159  
-                    raise GoogleMapException, \
160  
-                        'Geometry width and height should not exceed that of the Earth.'
  159
+                    raise GoogleMapException('Geometry width and height should not exceed that of the Earth.')
161 160
                 return z-1
162 161
         
163 162
         # Otherwise, we've zoomed in to the max.
164 163
         return self._nzoom-1
  164
+
16  django/contrib/gis/templates/gis/google/js/google-map.js
... ...
@@ -1,16 +1,20 @@
1  
-{% block vars %}var map;{% for kml_url in kml_urls %}var kml{{ forloop.counter }};{% endfor %}{% endblock %}
  1
+{% autoescape off %}{% block vars %}var map;{% endblock %}
2 2
 {% block functions %}{% endblock %}
3 3
 {% block load %}function {{ load_func }}(){
4 4
   if (GBrowserIsCompatible()) {
5 5
     map = new GMap2(document.getElementById("{{ dom_id }}"));
6  
-    map.addControl(new GSmallMapControl());
7  
-    map.addControl(new GMapTypeControl());
8  
-    map.setCenter(new GLatLng({{ center_lat }}, {{ center_lon }}), {{ zoom }});
9  
-    {% for kml_url in kml_urls %}kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
  6
+    {% block controls %}map.addControl(new GSmallMapControl());
  7
+    map.addControl(new GMapTypeControl());{% endblock %}
  8
+    map.setCenter(new GLatLng({{ center.1 }}, {{ center.0 }}), {{ zoom }});
  9
+    {% for kml_url in kml_urls %}var kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
10 10
     map.addOverlay(kml{{ forloop.counter }});{% endfor %}
  11
+    {% for polygon in polygons %}var poly{{ forloop.counter }} = new {{ polygon }};
  12
+    map.addOverlay(poly{{ forloop.counter }});{% endfor %}
  13
+    {% for polyline in polylines %}var polyline{{ forloop.counter }} = new {{ polyline }};
  14
+    map.addOverlay(polyline{{ forloop.counter }});{% endfor %}
11 15
     {% block load_extra %}{% endblock %}
12 16
   }else {
13 17
     alert("Sorry, the Google Maps API is not compatible with this browser.");
14 18
   }
15 19
 }
16  
-{% endblock %}
  20
+{% endblock %}{% endautoescape %}

0 notes on commit ae0e583

Please sign in to comment.
Something went wrong with that request. Please try again.