diff --git a/django/contrib/gis/maps/google/__init__.py b/django/contrib/gis/maps/google/__init__.py index 351962d05b8..e1e38a9aff8 100644 --- a/django/contrib/gis/maps/google/__init__.py +++ b/django/contrib/gis/maps/google/__init__.py @@ -57,5 +57,5 @@ version. """ 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 diff --git a/django/contrib/gis/maps/google/gmap.py b/django/contrib/gis/maps/google/gmap.py index 87734af5fc4..15bb802c171 100644 --- a/django/contrib/gis/maps/google/gmap.py +++ b/django/contrib/gis/maps/google/gmap.py @@ -4,7 +4,7 @@ from django.template.loader import render_to_string from django.utils.safestring import mark_safe 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) # 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 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', - kml_urls=[], polygons=[], polylines=[], markers=[], + kml_urls=[], polylines=None, polygons=None, markers=None, 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 # 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).') else: self.key = key - + # Getting the Google Maps API version, defaults to using the latest ("2.x"), # this is not necessarily the most stable. if not version: @@ -55,28 +56,24 @@ class GoogleMap(object): self.js_module = js_module self.template = template self.kml_urls = kml_urls - + # Does the user want any GMarker, GPolygon, and/or GPolyline overlays? - self.polygons, self.polylines, self.markers = [], [], [] - if markers: - for point in markers: - if isinstance(point, GMarker): - self.markers.append(point) - else: - self.markers.append(GMarker(point)) - 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)) - + overlay_info = [[GMarker, markers, 'markers'], + [GPolygon, polygons, 'polygons'], + [GPolyline, polylines, 'polylines']] + + for overlay_class, overlay_list, varname in overlay_info: + setattr(self, varname, []) + if overlay_list: + for overlay in overlay_list: + if isinstance(overlay, overlay_class): + getattr(self, varname).append(overlay) + else: + getattr(self, varname).append(overlay_class(overlay)) + + # Pulling any icons from the markers. + self.icons = [marker.icon for marker in self.markers if marker.icon] + # If GMarker, GPolygons, and/or GPolylines are used the zoom will be # automatically calculated via the Google Maps API. If both a zoom # 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 center is None or zoom is None: self.calc_zoom = True - + # Defaults for the zoom level and center coordinates if the zoom # is not automatically calculated. if zoom is None: zoom = 4 @@ -105,6 +102,7 @@ class GoogleMap(object): 'zoom' : self.zoom, 'polygons' : self.polygons, 'polylines' : self.polylines, + 'icons': self.icons, 'markers' : self.markers, } params.update(self.extra_context) @@ -174,7 +172,12 @@ class GoogleMapSet(GoogleMap): self.maps = args[0] else: 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. 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, 'dom_ids' : self.dom_ids, 'load_map_js' : self.load_map_js(), + 'icons' : self.icons, } params.update(self.extra_context) return render_to_string(self.template, params) diff --git a/django/contrib/gis/maps/google/overlays.py b/django/contrib/gis/maps/google/overlays.py index 37dd717b50e..4f1247d0a45 100644 --- a/django/contrib/gis/maps/google/overlays.py +++ b/django/contrib/gis/maps/google/overlays.py @@ -18,28 +18,28 @@ class GEvent(object): def sample_request(request): polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)') - event = GEvent('click', + event = GEvent('click', 'function() { location.href = "http://www.google.com"}') polyline.add_event(event) - return render_to_response('mytemplate.html', + return render_to_response('mytemplate.html', {'google' : GoogleMap(polylines=[polyline])}) """ def __init__(self, event, action): """ - Initializes a GEvent object. - + Initializes a GEvent object. + Parameters: - event: + event: 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. action: - string containing a Javascript function, such as + string containing a Javascript function, such as '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. """ self.event = event @@ -71,7 +71,7 @@ class GPolygon(GOverlayBase): please see the Google Maps API Reference: 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, fill_color='#0000ff', fill_opacity=0.4): """ @@ -98,25 +98,25 @@ class GPolygon(GOverlayBase): """ if isinstance(poly, basestring): poly = fromstr(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.') # 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 + # 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 - + super(GPolygon, self).__init__() - + @property def js_params(self): 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. 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. @@ -160,11 +160,77 @@ class GPolyline(GOverlayBase): self.envelope = geom.envelope self.color, self.weight, self.opacity = color, weight, opacity super(GPolyline, self).__init__() - + @property def js_params(self): 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): """ 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.contrib.gis.maps.google.overlays import GMarker, GEvent - + def sample_request(request): marker = GMarker('POINT(101 26)') - event = GEvent('click', + event = GEvent('click', 'function() { location.href = "http://www.google.com"}') marker.add_event(event) - return render_to_response('mytemplate.html', + return render_to_response('mytemplate.html', {'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 that may be instantiated into a GEOS point. Keyword options map to GMarkerOptions -- so far only the title option is supported. Keyword Options: - title: + title: Title option for GMarker, will be displayed as a tooltip. - + draggable: Draggable option for GMarker, disabled by default. """ @@ -209,15 +275,17 @@ class GMarker(GOverlayBase): # TODO: Add support for more GMarkerOptions self.title = title self.draggable = draggable + self.icon = icon super(GMarker, self).__init__() def latlng_from_coords(self, coords): return 'new GLatLng(%s,%s)' %(coords[1], coords[0]) - + def options(self): result = [] 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) @property diff --git a/django/contrib/gis/templates/gis/google/google-base.js b/django/contrib/gis/templates/gis/google/google-base.js index 3fee82242f8..f3a91edbc42 100644 --- a/django/contrib/gis/templates/gis/google/google-base.js +++ b/django/contrib/gis/templates/gis/google/google-base.js @@ -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 %} \ No newline at end of file