overlays.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. from __future__ import unicode_literals
  2. from django.contrib.gis.geos import fromstr, Point, LineString, LinearRing, Polygon
  3. from django.utils.functional import total_ordering
  4. from django.utils.safestring import mark_safe
  5. from django.utils import six
  6. from django.utils.encoding import python_2_unicode_compatible
  7. @python_2_unicode_compatible
  8. class GEvent(object):
  9. """
  10. A Python wrapper for the Google GEvent object.
  11. Events can be attached to any object derived from GOverlayBase with the
  12. add_event() call.
  13. For more information please see the Google Maps API Reference:
  14. http://code.google.com/apis/maps/documentation/reference.html#GEvent
  15. Example:
  16. from django.shortcuts import render_to_response
  17. from django.contrib.gis.maps.google import GoogleMap, GEvent, GPolyline
  18. def sample_request(request):
  19. polyline = GPolyline('LINESTRING(101 26, 112 26, 102 31)')
  20. event = GEvent('click',
  21. 'function() { location.href = "http://www.google.com"}')
  22. polyline.add_event(event)
  23. return render_to_response('mytemplate.html',
  24. {'google' : GoogleMap(polylines=[polyline])})
  25. """
  26. def __init__(self, event, action):
  27. """
  28. Initializes a GEvent object.
  29. Parameters:
  30. event:
  31. string for the event, such as 'click'. The event must be a valid
  32. event for the object in the Google Maps API.
  33. There is no validation of the event type within Django.
  34. action:
  35. string containing a Javascript function, such as
  36. 'function() { location.href = "newurl";}'
  37. The string must be a valid Javascript function. Again there is no
  38. validation fo the function within Django.
  39. """
  40. self.event = event
  41. self.action = action
  42. def __str__(self):
  43. "Returns the parameter part of a GEvent."
  44. return mark_safe('"%s", %s' % (self.event, self.action))
  45. @python_2_unicode_compatible
  46. class GOverlayBase(object):
  47. def __init__(self):
  48. self.events = []
  49. def latlng_from_coords(self, coords):
  50. "Generates a JavaScript array of GLatLng objects for the given coordinates."
  51. return '[%s]' % ','.join('new GLatLng(%s,%s)' % (y, x) for x, y in coords)
  52. def add_event(self, event):
  53. "Attaches a GEvent to the overlay object."
  54. self.events.append(event)
  55. def __str__(self):
  56. "The string representation is the JavaScript API call."
  57. return mark_safe('%s(%s)' % (self.__class__.__name__, self.js_params))
  58. class GPolygon(GOverlayBase):
  59. """
  60. A Python wrapper for the Google GPolygon object. For more information
  61. please see the Google Maps API Reference:
  62. http://code.google.com/apis/maps/documentation/reference.html#GPolygon
  63. """
  64. def __init__(self, poly,
  65. stroke_color='#0000ff', stroke_weight=2, stroke_opacity=1,
  66. fill_color='#0000ff', fill_opacity=0.4):
  67. """
  68. The GPolygon object initializes on a GEOS Polygon or a parameter that
  69. may be instantiated into GEOS Polygon. Please note that this will not
  70. depict a Polygon's internal rings.
  71. Keyword Options:
  72. stroke_color:
  73. The color of the polygon outline. Defaults to '#0000ff' (blue).
  74. stroke_weight:
  75. The width of the polygon outline, in pixels. Defaults to 2.
  76. stroke_opacity:
  77. The opacity of the polygon outline, between 0 and 1. Defaults to 1.
  78. fill_color:
  79. The color of the polygon fill. Defaults to '#0000ff' (blue).
  80. fill_opacity:
  81. The opacity of the polygon fill. Defaults to 0.4.
  82. """
  83. if isinstance(poly, six.string_types):
  84. poly = fromstr(poly)
  85. if isinstance(poly, (tuple, list)):
  86. poly = Polygon(poly)
  87. if not isinstance(poly, Polygon):
  88. raise TypeError('GPolygon may only initialize on GEOS Polygons.')
  89. # Getting the envelope of the input polygon (used for automatically
  90. # determining the zoom level).
  91. self.envelope = poly.envelope
  92. # Translating the coordinates into a JavaScript array of
  93. # Google `GLatLng` objects.
  94. self.points = self.latlng_from_coords(poly.shell.coords)
  95. # Stroke settings.
  96. self.stroke_color, self.stroke_opacity, self.stroke_weight = stroke_color, stroke_opacity, stroke_weight
  97. # Fill settings.
  98. self.fill_color, self.fill_opacity = fill_color, fill_opacity
  99. super(GPolygon, self).__init__()
  100. @property
  101. def js_params(self):
  102. return '%s, "%s", %s, %s, "%s", %s' % (self.points, self.stroke_color, self.stroke_weight, self.stroke_opacity,
  103. self.fill_color, self.fill_opacity)
  104. class GPolyline(GOverlayBase):
  105. """
  106. A Python wrapper for the Google GPolyline object. For more information
  107. please see the Google Maps API Reference:
  108. http://code.google.com/apis/maps/documentation/reference.html#GPolyline
  109. """
  110. def __init__(self, geom, color='#0000ff', weight=2, opacity=1):
  111. """
  112. The GPolyline object may be initialized on GEOS LineStirng, LinearRing,
  113. and Polygon objects (internal rings not supported) or a parameter that
  114. may instantiated into one of the above geometries.
  115. Keyword Options:
  116. color:
  117. The color to use for the polyline. Defaults to '#0000ff' (blue).
  118. weight:
  119. The width of the polyline, in pixels. Defaults to 2.
  120. opacity:
  121. The opacity of the polyline, between 0 and 1. Defaults to 1.
  122. """
  123. # If a GEOS geometry isn't passed in, try to construct one.
  124. if isinstance(geom, six.string_types):
  125. geom = fromstr(geom)
  126. if isinstance(geom, (tuple, list)):
  127. geom = Polygon(geom)
  128. # Generating the lat/lng coordinate pairs.
  129. if isinstance(geom, (LineString, LinearRing)):
  130. self.latlngs = self.latlng_from_coords(geom.coords)
  131. elif isinstance(geom, Polygon):
  132. self.latlngs = self.latlng_from_coords(geom.shell.coords)
  133. else:
  134. raise TypeError('GPolyline may only initialize on GEOS LineString, LinearRing, and/or Polygon geometries.')
  135. # Getting the envelope for automatic zoom determination.
  136. self.envelope = geom.envelope
  137. self.color, self.weight, self.opacity = color, weight, opacity
  138. super(GPolyline, self).__init__()
  139. @property
  140. def js_params(self):
  141. return '%s, "%s", %s, %s' % (self.latlngs, self.color, self.weight, self.opacity)
  142. @total_ordering
  143. class GIcon(object):
  144. """
  145. Creates a GIcon object to pass into a Gmarker object.
  146. The keyword arguments map to instance attributes of the same name. These,
  147. in turn, correspond to a subset of the attributes of the official GIcon
  148. javascript object:
  149. http://code.google.com/apis/maps/documentation/reference.html#GIcon
  150. Because a Google map often uses several different icons, a name field has
  151. been added to the required arguments.
  152. Required Arguments:
  153. varname:
  154. A string which will become the basis for the js variable name of
  155. the marker, for this reason, your code should assign a unique
  156. name for each GIcon you instantiate, otherwise there will be
  157. name space collisions in your javascript.
  158. Keyword Options:
  159. image:
  160. The url of the image to be used as the icon on the map defaults
  161. to 'G_DEFAULT_ICON'
  162. iconsize:
  163. a tuple representing the pixel size of the foreground (not the
  164. shadow) image of the icon, in the format: (width, height) ex.:
  165. GIcon('fast_food',
  166. image="/media/icon/star.png",
  167. iconsize=(15,10))
  168. Would indicate your custom icon was 15px wide and 10px height.
  169. shadow:
  170. the url of the image of the icon's shadow
  171. shadowsize:
  172. a tuple representing the pixel size of the shadow image, format is
  173. the same as ``iconsize``
  174. iconanchor:
  175. a tuple representing the pixel coordinate relative to the top left
  176. corner of the icon image at which this icon is anchored to the map.
  177. In (x, y) format. x increases to the right in the Google Maps
  178. coordinate system and y increases downwards in the Google Maps
  179. coordinate system.)
  180. infowindowanchor:
  181. The pixel coordinate relative to the top left corner of the icon
  182. image at which the info window is anchored to this icon.
  183. """
  184. def __init__(self, varname, image=None, iconsize=None,
  185. shadow=None, shadowsize=None, iconanchor=None,
  186. infowindowanchor=None):
  187. self.varname = varname
  188. self.image = image
  189. self.iconsize = iconsize
  190. self.shadow = shadow
  191. self.shadowsize = shadowsize
  192. self.iconanchor = iconanchor
  193. self.infowindowanchor = infowindowanchor
  194. def __eq__(self, other):
  195. return self.varname == other.varname
  196. def __lt__(self, other):
  197. return self.varname < other.varname
  198. def __hash__(self):
  199. # XOR with hash of GIcon type so that hash('varname') won't
  200. # equal hash(GIcon('varname')).
  201. return hash(self.__class__) ^ hash(self.varname)
  202. class GMarker(GOverlayBase):
  203. """
  204. A Python wrapper for the Google GMarker object. For more information
  205. please see the Google Maps API Reference:
  206. http://code.google.com/apis/maps/documentation/reference.html#GMarker
  207. Example:
  208. from django.shortcuts import render_to_response
  209. from django.contrib.gis.maps.google.overlays import GMarker, GEvent
  210. def sample_request(request):
  211. marker = GMarker('POINT(101 26)')
  212. event = GEvent('click',
  213. 'function() { location.href = "http://www.google.com"}')
  214. marker.add_event(event)
  215. return render_to_response('mytemplate.html',
  216. {'google' : GoogleMap(markers=[marker])})
  217. """
  218. def __init__(self, geom, title=None, draggable=False, icon=None):
  219. """
  220. The GMarker object may initialize on GEOS Points or a parameter
  221. that may be instantiated into a GEOS point. Keyword options map to
  222. GMarkerOptions -- so far only the title option is supported.
  223. Keyword Options:
  224. title:
  225. Title option for GMarker, will be displayed as a tooltip.
  226. draggable:
  227. Draggable option for GMarker, disabled by default.
  228. """
  229. # If a GEOS geometry isn't passed in, try to construct one.
  230. if isinstance(geom, six.string_types):
  231. geom = fromstr(geom)
  232. if isinstance(geom, (tuple, list)):
  233. geom = Point(geom)
  234. if isinstance(geom, Point):
  235. self.latlng = self.latlng_from_coords(geom.coords)
  236. else:
  237. raise TypeError('GMarker may only initialize on GEOS Point geometry.')
  238. # Getting the envelope for automatic zoom determination.
  239. self.envelope = geom.envelope
  240. # TODO: Add support for more GMarkerOptions
  241. self.title = title
  242. self.draggable = draggable
  243. self.icon = icon
  244. super(GMarker, self).__init__()
  245. def latlng_from_coords(self, coords):
  246. return 'new GLatLng(%s,%s)' % (coords[1], coords[0])
  247. def options(self):
  248. result = []
  249. if self.title:
  250. result.append('title: "%s"' % self.title)
  251. if self.icon:
  252. result.append('icon: %s' % self.icon.varname)
  253. if self.draggable:
  254. result.append('draggable: true')
  255. return '{%s}' % ','.join(result)
  256. @property
  257. def js_params(self):
  258. return '%s, %s' % (self.latlng, self.options())