Fixed #9204 -- Added `GIcon` overlay, allowing customization for icons of `GMarker` objects. Thanks to qingfeng for initial ticket/patch, and to prairiedogg for final implementation.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@10021 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2009-03-10 18:03:35 +00:00
parent 326c5813b9
commit 6aa5aacc86
4 changed files with 132 additions and 55 deletions

View File

@ -57,5 +57,5 @@
version. version.
""" """
from django.contrib.gis.maps.google.gmap import GoogleMap, GoogleMapSet 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, GIcon, GMarker, GPolygon, GPolyline
from django.contrib.gis.maps.google.zoom import GoogleZoom from django.contrib.gis.maps.google.zoom import GoogleZoom

View File

@ -4,7 +4,7 @@ from django.template.loader import render_to_string
from django.utils.safestring import mark_safe from django.utils.safestring import mark_safe
class GoogleMapException(Exception): pass class GoogleMapException(Exception): pass
from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker from django.contrib.gis.maps.google.overlays import GPolygon, GPolyline, GMarker, GIcon
# 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.
@ -18,11 +18,12 @@ class GoogleMap(object):
vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML vml_css = mark_safe('v\:* {behavior:url(#default#VML);}') # CSS for IE VML
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', center=None, zoom=None, dom_id='map',
kml_urls=[], polygons=[], polylines=[], markers=[], kml_urls=[], polylines=None, polygons=None, markers=None,
template='gis/google/google-single.js', template='gis/google/google-single.js',
js_module='geodjango',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
@ -34,7 +35,7 @@ class GoogleMap(object):
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:
@ -55,28 +56,24 @@ class GoogleMap(object):
self.js_module = js_module self.js_module = js_module
self.template = template self.template = template
self.kml_urls = kml_urls self.kml_urls = kml_urls
# Does the user want any GMarker, GPolygon, and/or GPolyline overlays? # Does the user want any GMarker, GPolygon, and/or GPolyline overlays?
self.polygons, self.polylines, self.markers = [], [], [] overlay_info = [[GMarker, markers, 'markers'],
if markers: [GPolygon, polygons, 'polygons'],
for point in markers: [GPolyline, polylines, 'polylines']]
if isinstance(point, GMarker):
self.markers.append(point) for overlay_class, overlay_list, varname in overlay_info:
else: setattr(self, varname, [])
self.markers.append(GMarker(point)) if overlay_list:
if polygons: for overlay in overlay_list:
for poly in polygons: if isinstance(overlay, overlay_class):
if isinstance(poly, GPolygon): getattr(self, varname).append(overlay)
self.polygons.append(poly) else:
else: getattr(self, varname).append(overlay_class(overlay))
self.polygons.append(GPolygon(poly))
if polylines: # Pulling any icons from the markers.
for pline in polylines: self.icons = [marker.icon for marker in self.markers if marker.icon]
if isinstance(pline, GPolyline):
self.polylines.append(pline)
else:
self.polylines.append(GPolyline(pline))
# If GMarker, GPolygons, and/or GPolylines are used the zoom will be # If GMarker, GPolygons, and/or GPolylines are used the zoom will be
# automatically calculated via the Google Maps API. If both a zoom # automatically calculated via the Google Maps API. If both a zoom
# level and a center coordinate are provided with polygons/polylines, # level and a center coordinate are provided with polygons/polylines,
@ -85,7 +82,7 @@ class GoogleMap(object):
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:
self.calc_zoom = True self.calc_zoom = True
# Defaults for the zoom level and center coordinates if the zoom # Defaults for the zoom level and center coordinates if the zoom
# is not automatically calculated. # is not automatically calculated.
if zoom is None: zoom = 4 if zoom is None: zoom = 4
@ -105,6 +102,7 @@ class GoogleMap(object):
'zoom' : self.zoom, 'zoom' : self.zoom,
'polygons' : self.polygons, 'polygons' : self.polygons,
'polylines' : self.polylines, 'polylines' : self.polylines,
'icons': self.icons,
'markers' : self.markers, 'markers' : self.markers,
} }
params.update(self.extra_context) params.update(self.extra_context)
@ -174,7 +172,12 @@ class GoogleMapSet(GoogleMap):
self.maps = args[0] self.maps = args[0]
else: else:
self.maps = args self.maps = args
# Creating the icons sequence from every map in this set.
self.icons = []
for map in self.maps:
self.icons.extend(map.icons)
# Generating DOM ids for each of the maps in the set. # Generating DOM ids for each of the maps in the set.
self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))] self.dom_ids = ['map%d' % i for i in xrange(len(self.maps))]
@ -205,6 +208,7 @@ class GoogleMapSet(GoogleMap):
params = {'js_module' : self.js_module, params = {'js_module' : self.js_module,
'dom_ids' : self.dom_ids, 'dom_ids' : self.dom_ids,
'load_map_js' : self.load_map_js(), 'load_map_js' : self.load_map_js(),
'icons' : self.icons,
} }
params.update(self.extra_context) params.update(self.extra_context)
return render_to_string(self.template, params) return render_to_string(self.template, params)

View File

@ -18,28 +18,28 @@ class GEvent(object):
def sample_request(request): def sample_request(request):
polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)') polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)')
event = GEvent('click', event = GEvent('click',
'function() { location.href = "http://www.google.com"}') 'function() { location.href = "http://www.google.com"}')
polyline.add_event(event) polyline.add_event(event)
return render_to_response('mytemplate.html', return render_to_response('mytemplate.html',
{'google' : GoogleMap(polylines=[polyline])}) {'google' : GoogleMap(polylines=[polyline])})
""" """
def __init__(self, event, action): def __init__(self, event, action):
""" """
Initializes a GEvent object. Initializes a GEvent object.
Parameters: Parameters:
event: event:
string for the event, such as 'click'. The event must be a valid string for the event, such as 'click'. The event must be a valid
event for the object in the Google Maps API. event for the object in the Google Maps API.
There is no validation of the event type within Django. There is no validation of the event type within Django.
action: action:
string containing a Javascript function, such as string containing a Javascript function, such as
'function() { location.href = "newurl";}' 'function() { location.href = "newurl";}'
The string must be a valid Javascript function. Again there is no The string must be a valid Javascript function. Again there is no
validation fo the function within Django. validation fo the function within Django.
""" """
self.event = event self.event = event
@ -71,7 +71,7 @@ class GPolygon(GOverlayBase):
please see the Google Maps API Reference: please see the Google Maps API Reference:
http://code.google.com/apis/maps/documentation/reference.html#GPolygon http://code.google.com/apis/maps/documentation/reference.html#GPolygon
""" """
def __init__(self, poly, def __init__(self, poly,
stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1, stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1,
fill_color='#0000ff', fill_opacity=0.4): fill_color='#0000ff', fill_opacity=0.4):
""" """
@ -98,25 +98,25 @@ class GPolygon(GOverlayBase):
""" """
if isinstance(poly, basestring): poly = fromstr(poly) if isinstance(poly, basestring): poly = fromstr(poly)
if isinstance(poly, (tuple, list)): poly = Polygon(poly) if isinstance(poly, (tuple, list)): poly = Polygon(poly)
if not isinstance(poly, Polygon): if not isinstance(poly, Polygon):
raise TypeError('GPolygon may only initialize on GEOS Polygons.') raise TypeError('GPolygon may only initialize on GEOS Polygons.')
# Getting the envelope of the input polygon (used for automatically # Getting the envelope of the input polygon (used for automatically
# determining the zoom level). # determining the zoom level).
self.envelope = poly.envelope self.envelope = poly.envelope
# Translating the coordinates into a JavaScript array of # Translating the coordinates into a JavaScript array of
# Google `GLatLng` objects. # Google `GLatLng` objects.
self.points = self.latlng_from_coords(poly.shell.coords) self.points = self.latlng_from_coords(poly.shell.coords)
# Stroke settings. # Stroke settings.
self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight
# Fill settings. # Fill settings.
self.fill_color, self.fill_opacity = fill_color, fill_opacity self.fill_color, self.fill_opacity = fill_color, fill_opacity
super(GPolygon, self).__init__() super(GPolygon, self).__init__()
@property @property
def js_params(self): def js_params(self):
return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity, return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity,
@ -135,10 +135,10 @@ class GPolyline(GOverlayBase):
may instantiated into one of the above geometries. may instantiated into one of the above geometries.
Keyword Options: Keyword Options:
color: color:
The color to use for the polyline. Defaults to '#0000ff' (blue). The color to use for the polyline. Defaults to '#0000ff' (blue).
weight: weight:
The width of the polyline, in pixels. Defaults to 2. The width of the polyline, in pixels. Defaults to 2.
@ -160,11 +160,77 @@ class GPolyline(GOverlayBase):
self.envelope = geom.envelope self.envelope = geom.envelope
self.color, self.weight, self.opacity = color, weight, opacity self.color, self.weight, self.opacity = color, weight, opacity
super(GPolyline, self).__init__() super(GPolyline, self).__init__()
@property @property
def js_params(self): def js_params(self):
return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity) return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)
class GIcon(object):
"""
Creates a GIcon object to pass into a Gmarker object.
The keyword arguments map to instance attributes of the same name. These,
in turn, correspond to a subset of the attributes of the official GIcon
javascript object:
http://code.google.com/apis/maps/documentation/reference.html#GIcon
Because a Google map often uses several different icons, a name field has
been added to the required arguments.
Required Arguments:
varname:
A string which will become the basis for the js variable name of
the marker, for this reason, your code should assign a unique
name for each GIcon you instantiate, otherwise there will be
name space collisions in your javascript.
Keyword Options:
image:
The url of the image to be used as the icon on the map defaults
to 'G_DEFAULT_ICON'
iconsize:
a tuple representing the pixel size of the foreground (not the
shadow) image of the icon, in the format: (width, height) ex.:
GIcon('fast_food',
image="/media/icon/star.png",
iconsize=(15,10))
Would indicate your custom icon was 15px wide and 10px height.
shadow:
the url of the image of the icon's shadow
shadowsize:
a tuple representing the pixel size of the shadow image, format is
the same as ``iconsize``
iconanchor:
a tuple representing the pixel coordinate relative to the top left
corner of the icon image at which this icon is anchored to the map.
In (x, y) format. x increases to the right in the Google Maps
coordinate system and y increases downwards in the Google Maps
coordinate system.)
infowindowanchor:
The pixel coordinate relative to the top left corner of the icon
image at which the info window is anchored to this icon.
"""
def __init__(self, varname, image=None, iconsize=None,
shadow=None, shadowsize=None, iconanchor=None,
infowindowanchor=None):
self.varname = varname
self.image = image
self.iconsize = iconsize
self.shadow = shadow
self.shadowsize = shadowsize
self.iconanchor = iconanchor
self.infowindowanchor = infowindowanchor
class GMarker(GOverlayBase): class GMarker(GOverlayBase):
""" """
A Python wrapper for the Google GMarker object. For more information A Python wrapper for the Google GMarker object. For more information
@ -175,25 +241,25 @@ class GMarker(GOverlayBase):
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.contrib.gis.maps.google.overlays import GMarker, GEvent from django.contrib.gis.maps.google.overlays import GMarker, GEvent
def sample_request(request): def sample_request(request):
marker = GMarker('POINT(101 26)') marker = GMarker('POINT(101 26)')
event = GEvent('click', event = GEvent('click',
'function() { location.href = "http://www.google.com"}') 'function() { location.href = "http://www.google.com"}')
marker.add_event(event) marker.add_event(event)
return render_to_response('mytemplate.html', return render_to_response('mytemplate.html',
{'google' : GoogleMap(markers=[marker])}) {'google' : GoogleMap(markers=[marker])})
""" """
def __init__(self, geom, title=None, draggable=False): def __init__(self, geom, title=None, draggable=False, icon=None):
""" """
The GMarker object may initialize on GEOS Points or a parameter The GMarker object may initialize on GEOS Points or a parameter
that may be instantiated into a GEOS point. Keyword options map to that may be instantiated into a GEOS point. Keyword options map to
GMarkerOptions -- so far only the title option is supported. GMarkerOptions -- so far only the title option is supported.
Keyword Options: Keyword Options:
title: title:
Title option for GMarker, will be displayed as a tooltip. Title option for GMarker, will be displayed as a tooltip.
draggable: draggable:
Draggable option for GMarker, disabled by default. Draggable option for GMarker, disabled by default.
""" """
@ -209,15 +275,17 @@ class GMarker(GOverlayBase):
# TODO: Add support for more GMarkerOptions # TODO: Add support for more GMarkerOptions
self.title = title self.title = title
self.draggable = draggable self.draggable = draggable
self.icon = icon
super(GMarker, self).__init__() super(GMarker, self).__init__()
def latlng_from_coords(self, coords): def latlng_from_coords(self, coords):
return 'new GLatLng(%s,%s)' %(coords[1], coords[0]) return 'new GLatLng(%s,%s)' %(coords[1], coords[0])
def options(self): def options(self):
result = [] result = []
if self.title: result.append('title: "%s"' % self.title) if self.title: result.append('title: "%s"' % self.title)
if self.draggable: result.append('draggable: true') if self.icon: result.append('icon: %s' % self.icon.varname)
if self.draggable: result.append('draggable: true')
return '{%s}' % ','.join(result) return '{%s}' % ','.join(result)
@property @property

View File

@ -1,2 +1,7 @@
{% block vars %}var geodjango = {};{% endblock %} {% block vars %}var geodjango = {};{% for icon in icons %}
var {{ icon.varname }} = new GIcon(G_DEFAULT_ICON);
{% if icon.image %}{{ icon.varname }}.image = "{{ icon.image }}";{% endif %}
{% if icon.shadow %}{{ icon.varname }}.shadow = "{{ icon.shadow }}";{% endif %} {% if icon.shadowsize %}{{ icon.varname }}.shadowSize = new GSize({{ icon.shadowsize.0 }}, {{ icon.shadowsize.1 }});{% endif %}
{% if icon.iconanchor %}{{ icon.varname }}.iconAnchor = new GPoint({{ icon.iconanchor.0 }}, {{ icon.iconanchor.1 }});{% endif %} {% if icon.iconsize %}{{ icon.varname }}.iconSize = new GSize({{ icon.iconsize.0 }}, {{ icon.iconsize.1 }});{% endif %}
{% if icon.infowindowanchor %}{{ icon.varname }}.infoWindowAnchor = new GPoint({{ icon.infowindowanchor.0 }}, {{ icon.infowindowanchor.1 }});{% endif %}{% endfor %}{% endblock %}
{% block functions %}{% endblock %} {% block functions %}{% endblock %}