1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

gis: Fixed #6746 by marking safe GoogleMap internal XHTML/JavaScript; 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
This commit is contained in:
Justin Bronn 2008-03-10 20:45:51 +00:00
parent 22e016ff6c
commit ae0e583510
5 changed files with 200 additions and 54 deletions

View File

@ -19,7 +19,7 @@
{{ google.scripts }} {{ google.scripts }}
</head> </head>
{{ google.body }} {{ google.body }}
<div id="{{ google.dom_id }}"></div> <div id="{{ google.dom_id }}" style="width:600px;height:400px;"></div>
</body> </body>
</html> </html>
@ -47,7 +47,7 @@
body tag to load the generated javascript. By default, returns: body tag to load the generated javascript. By default, returns:
<body onload="gmap_load()" onunload="GUnload()"> <body onload="gmap_load()" onunload="GUnload()">
- The `id` property returns the DOM id for the map. Defaults to "map". - The `dom_id` property returns the DOM id for the map. Defaults to "map".
The following attributes may be set or customized in your local settings: The following attributes may be set or customized in your local settings:
* GOOGLE_MAPS_API_KEY: String of your Google Maps API key. These are tied to * GOOGLE_MAPS_API_KEY: String of your Google Maps API key. These are tied to
@ -56,5 +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, GZOOM
from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline
from django.contrib.gis.maps.google.zoom import GoogleZoom from django.contrib.gis.maps.google.zoom import GoogleZoom

View File

@ -1,24 +1,32 @@
from django.conf import settings from django.conf import settings
from django.contrib.gis import geos
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.safestring import mark_safe
# Declaring the GoogleMapException prior to getting the
# default `GZOOM` GoogleZoom instance.
class GoogleMapException(Exception): pass
from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline
from django.contrib.gis.maps.google.zoom import GoogleZoom
GZOOM = GoogleZoom()
# The default Google Maps URL (for the API javascript) # The default Google Maps URL (for the API javascript)
# TODO: Internationalize for Japan, UK, etc. # TODO: Internationalize for Japan, UK, etc.
GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&amp;v=%s&amp;key=' GOOGLE_MAPS_URL='http://maps.google.com/maps?file=api&amp;v=%s&amp;key='
class GoogleMapException(Exception): pass
class GoogleMap(object): class GoogleMap(object):
"A class for generating Google Maps javascript." "A class for generating Google Maps JavaScript."
# String constants # String constants
onunload = 'onunload="GUnload()"' # Cleans up after Google Maps onunload = mark_safe('onunload="GUnload()"') # Cleans up after Google Maps
vml_css = 'v\:* {behavior:url(#default#VML);}' # CSS for IE VML vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML
xmlns = '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_lat=0.0, center_lon=0.0, zoom=1, center=None, center_lat=0.0, center_lon=0.0,
dom_id='map', load_func='gmap_load', zoom=None, dom_id='map', load_func='gmap_load',
kml_urls=[], template='gis/google/js/google-map.js', kml_urls=[], polygons=[], polylines=[],
template='gis/google/js/google-map.js',
extra_context={}): 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
@ -26,77 +34,109 @@ class GoogleMap(object):
# _required_. # _required_.
if not key: if not key:
try: try:
self._key = settings.GOOGLE_MAPS_API_KEY self.key = settings.GOOGLE_MAPS_API_KEY
except AttributeError: except AttributeError:
raise GoogleMapException, 'Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).' raise GoogleMapException('Google Maps API Key not found (try adding GOOGLE_MAPS_API_KEY to your settings).')
else: else:
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:
try: self.version = getattr(settings, 'GOOGLE_MAPS_API_VERSION', '2.x')
self._version = settings.GOOGLE_MAPS_API_VERSION
except AttributeError:
self._version = '2.x'
else: else:
self._version = version self.version = version
# Can specify the API URL in the `api_url` keyword. # Can specify the API URL in the `api_url` keyword.
if not api_url: if not api_url:
try: self.api_url = mark_safe(getattr(settings, 'GOOGLE_MAPS_URL', GOOGLE_MAPS_URL) % self.version)
self._url = settings.GOOGLE_MAPS_URL % self._version
except AttributeError:
self._url = GOOGLE_MAPS_URL % self._version
else: else:
self._url = api_url self.api_url = api_url
# Setting the DOM id of the map, the center lat/lon, the load function, # Setting the DOM id of the map, the load function, the JavaScript
# and the zoom. # template, and the KML URLs array.
self.dom_id = dom_id self.dom_id = dom_id
self.center_lat = center_lat
self.center_lon = center_lon
self.load_func = load_func self.load_func = load_func
self.template = template self.template = template
self.zoom = zoom self.kml_urls = kml_urls
# Does the user want any GPolygon or GPolyline overlays?
self.polygons, self.polylines = [], []
if polygons:
for poly in polygons:
if isinstance(poly, GPolygon):
self.polygons.append(poly)
else:
self.polygons.append(GPolygon(poly))
if polylines:
for pline in polylines:
if isinstance(pline, GPolyline):
self.polylines.append(pline)
else:
self.polylines.append(GPolyline(pline))
# Automatically determining the zoom level if there are
# GPolygon and/or GPolyline overlays.
if (self.polygons or self.polylines) and zoom is None:
envelopes = [p.envelope for p in self.polygons]
envelopes.extend([p.envelope for p in self.polylines])
# Creating a MultiPolygon of all the envelopes, this will
# be used in determining the zoom level.
zoom = geos.MultiPolygon(envelopes)
zoom.srid = 4326
# If a GEOSGeometry object is passed in for the `zoom` keyword
# argument, then try to automatically determine an appropriate
# zoom level.
if isinstance(zoom, geos.GEOSGeometry):
self.zoom = GZOOM.get_zoom(zoom)
else:
self.zoom = zoom
# The map center coordinate -- the `center_lon` and `center_lat` keyword
# are deprecated.
if not center:
center = (center_lon, center_lat)
self.center = center
# Setting the parameters for the javascript template. # Setting the parameters for the javascript template.
params = {'center_lat' : center_lat, params = {'center' : self.center,
'center_lon' : center_lon, 'dom_id' : self.dom_id,
'dom_id' : dom_id, 'kml_urls' : self.kml_urls,
'kml_urls' : kml_urls, 'load_func' : self.load_func,
'load_func' : load_func, 'zoom' : self.zoom,
'zoom' : zoom, 'polygons' : self.polygons,
'polylines' : self.polylines,
} }
params.update(extra_context) params.update(extra_context)
self.js = render_to_string(template, params) self.js = render_to_string(self.template, params)
@property @property
def body(self): def body(self):
"Returns HTML body tag for loading and unloading Google Maps javascript." "Returns HTML body tag for loading and unloading Google Maps javascript."
return '<body %s %s>' % (self.onload, self.onunload) return mark_safe('<body %s %s>' % (self.onload, self.onunload))
@property @property
def onload(self): def onload(self):
"Returns the `onload` HTML <body> attribute." "Returns the `onload` HTML <body> attribute."
return 'onload="%s()"' % self.load_func return mark_safe('onload="%s()"' % self.load_func)
@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 '<script src="%s%s" type="text/javascript"></script>' % (self._url, self._key) return mark_safe('<script src="%s%s" type="text/javascript"></script>' % (self.api_url, self.key))
@property @property
def scripts(self): def scripts(self):
"Returns all <script></script> tags required for Google Maps JavaScript." "Returns all <script></script> tags required for Google Maps JavaScript."
return '%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
def style(self): def style(self):
"Returns additional CSS styling needed for Google Maps on IE." "Returns additional CSS styling needed for Google Maps on IE."
return '<style type="text/css">%s</style>' % self.vml_css return mark_safe('<style type="text/css">%s</style>' % self.vml_css)
@property @property
def xhtml(self): def xhtml(self):
"Returns XHTML information needed for IE VML overlays." "Returns XHTML information needed for IE VML overlays."
return '<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)

View File

@ -0,0 +1,101 @@
from django.contrib.gis.geos import LineString, LinearRing, Polygon
from django.utils.safestring import mark_safe
class GOverlayBase(object):
def latlng_from_coords(self, coords):
return '[%s]' % ','.join(['new GLatLng(%s,%s)' % (y, x) for x, y in coords])
def __unicode__(self):
"The string representation is the JavaScript API call."
return mark_safe('%s(%s)' % (self.__class__.__name__, self.js_params))
class GPolygon(GOverlayBase):
"""
A Python wrapper for the Google GPolygon object. For more information
please see the Google Maps API Reference:
http://code.google.com/apis/maps/documentation/reference.html#GPolygon
"""
def __init__(self, poly,
stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1,
fill_color='#0000ff', fill_opacity=0.4):
"""
The GPolygon object initializes on a GEOS Polygon. Please note that
this will not depict Polygons with internal rings.
Keyword Options:
stroke_color:
The color of the polygon outline. Defaults to '#0000ff' (blue).
stroke_weight:
The width of the polygon outline, in pixels. Defaults to 2.
stroke_opacity:
The opacity of the polygon outline, between 0 and 1. Defaults to 1.
fill_color:
The color of the polygon fill. Defaults to '#0000ff' (blue).
fill_opacity:
The opacity of the polygon fill. Defaults to 0.4.
"""
# TODO: Take other types of geometries.
if not isinstance(poly, Polygon):
raise TypeError('GPolygon may only initialize on GEOS Polygons.')
# Getting the envelope of the input polygon (used for automatically
# determining the zoom level).
self.envelope = poly.envelope
# Translating the coordinates into a JavaScript array of
# Google `GLatLng` objects.
self.points = self.latlng_from_coords(poly.shell.coords)
# Stroke settings.
self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight
# Fill settings.
self.fill_color, self.fill_opacity = fill_color, fill_opacity
@property
def js_params(self):
return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity,
self.fill_color, self.fill_opacity)
class GPolyline(GOverlayBase):
"""
A Python wrapper for the Google GPolyline object. For more information
please see the Google Maps API Reference:
http://code.google.com/apis/maps/documentation/reference.html#GPolyline
"""
def __init__(self, geom, color='#0000ff', weight=2, opacity=1):
"""
The GPolyline object may initialize on GEOS LineStirng, LinearRing,
and Polygon objects (internal rings not supported).
Keyword Options:
color:
The color to use for the polyline. Defaults to '#0000ff' (blue).
weight:
The width of the polyline, in pixels. Defaults to 2.
opacity:
The opacity of the polyline, between 0 and 1. Defaults to 1.
"""
if isinstance(geom, (LineString, LinearRing)):
self.latlngs = self.latlng_from_coords(geom.coords)
elif isinstance(geom, Polygon):
self.latlngs = self.latlng_from_coords(geom.shell.coords)
else:
raise TypeError('GPolyline may only initialize on GEOS LineString, LinearRing, and/or Polygon geometries.')
# Getting the envelope for automatic zoom determination.
self.envelope = geom.envelope
self.color, self.weight, self.opacity = color, weight, opacity
@property
def js_params(self):
return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)

View File

@ -99,7 +99,7 @@ class GoogleZoom(object):
def pixel_to_lonlat(self, px, zoom): def pixel_to_lonlat(self, px, zoom):
"Converts a pixel to a longitude, latitude pair at the given zoom level." "Converts a pixel to a longitude, latitude pair at the given zoom level."
if len(px) != 2: if len(px) != 2:
raise TypeError, 'Pixel should be a sequence of two elements.' raise TypeError('Pixel should be a sequence of two elements.')
# Getting the number of pixels for the given zoom level. # Getting the number of pixels for the given zoom level.
npix = self._npix[zoom] npix = self._npix[zoom]
@ -138,8 +138,8 @@ class GoogleZoom(object):
"Returns the optimal Zoom level for the given geometry." "Returns the optimal Zoom level for the given geometry."
# Checking the input type. # Checking the input type.
if not isinstance(geom, GEOSGeometry) and geom.srid == 4326: if not isinstance(geom, GEOSGeometry) or geom.srid != 4326:
raise TypeError, 'get_zoom() expects a GEOS Geometry with an SRID of 4326.' raise TypeError('get_zoom() expects a GEOS Geometry with an SRID of 4326.')
# Getting the envelope for the geometry, and its associated width, height # Getting the envelope for the geometry, and its associated width, height
# and centroid. # and centroid.
@ -156,9 +156,9 @@ class GoogleZoom(object):
# zoom level. # zoom level.
if (env_w > tile_w) or (env_h > tile_h): if (env_w > tile_w) or (env_h > tile_h):
if z == 0: if z == 0:
raise GoogleMapException, \ raise GoogleMapException('Geometry width and height should not exceed that of the Earth.')
'Geometry width and height should not exceed that of the Earth.'
return z-1 return z-1
# Otherwise, we've zoomed in to the max. # Otherwise, we've zoomed in to the max.
return self._nzoom-1 return self._nzoom-1

View File

@ -1,16 +1,20 @@
{% block vars %}var map;{% for kml_url in kml_urls %}var kml{{ forloop.counter }};{% endfor %}{% endblock %} {% autoescape off %}{% block vars %}var map;{% endblock %}
{% block functions %}{% endblock %} {% block functions %}{% endblock %}
{% block load %}function {{ load_func }}(){ {% block load %}function {{ load_func }}(){
if (GBrowserIsCompatible()) { if (GBrowserIsCompatible()) {
map = new GMap2(document.getElementById("{{ dom_id }}")); map = new GMap2(document.getElementById("{{ dom_id }}"));
map.addControl(new GSmallMapControl()); {% block controls %}map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl()); map.addControl(new GMapTypeControl());{% endblock %}
map.setCenter(new GLatLng({{ center_lat }}, {{ center_lon }}), {{ zoom }}); map.setCenter(new GLatLng({{ center.1 }}, {{ center.0 }}), {{ zoom }});
{% for kml_url in kml_urls %}kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}"); {% for kml_url in kml_urls %}var kml{{ forloop.counter }} = new GGeoXml("{{ kml_url }}");
map.addOverlay(kml{{ forloop.counter }});{% endfor %} map.addOverlay(kml{{ forloop.counter }});{% endfor %}
{% for polygon in polygons %}var poly{{ forloop.counter }} = new {{ polygon }};
map.addOverlay(poly{{ forloop.counter }});{% endfor %}
{% for polyline in polylines %}var polyline{{ forloop.counter }} = new {{ polyline }};
map.addOverlay(polyline{{ forloop.counter }});{% endfor %}
{% block load_extra %}{% endblock %} {% block load_extra %}{% endblock %}
}else { }else {
alert("Sorry, the Google Maps API is not compatible with this browser."); alert("Sorry, the Google Maps API is not compatible with this browser.");
} }
} }
{% endblock %} {% endblock %}{% endautoescape %}