1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 try:
17 from cStringIO import StringIO
18 except ImportError:
19 from StringIO import StringIO
20
21 from muntjac.ui.abstract_component \
22 import AbstractComponent
23
24 from muntjac.terminal.download_stream \
25 import DownloadStream
26
27 from muntjac.terminal.application_resource \
28 import IApplicationResource
29
30 from muntjac.addon.google_maps.overlay.polygon \
31 import Polygon
32
33 from muntjac.addon.google_maps.overlay.basic_marker_source \
34 import BasicMarkerSource
47
50 """Server side component for the VGoogleMap widget."""
51
52 CLIENT_WIDGET = None
53
54 TYPE_MAPPING = 'org.vaadin.hezamu.googlemapwidget.GoogleMap'
55
56 - def __init__(self, application, apiKey_or_center=None, zoom=None,
57 apiKey=None):
58 """Construct a new instance of the map with given parameters.
59
60 @param application:
61 L{Application} owning this instance.
62 @param apiKey_or_center:
63 the API key to be used for Google Maps or the center of
64 the map as a 2-tuple
65 @param zoom:
66 initial zoom level of the map
67 @param apiKey:
68 the API key to be used for Google Maps
69 """
70 super(GoogleMap, self).__init__()
71
72 self._center = None
73
74 self._boundsNE = None
75
76 self._boundsSW = None
77
78 self._zoom = None
79
80 self._moveListeners = list()
81
82 self._mapClickListeners = list()
83
84 self._markerListeners = list()
85
86 self._markerMovedListeners = list()
87
88 self._markerSource = None
89
90 self._clickedMarker = None
91
92 self._closeInfoWindow = False
93
94 self._overlays = dict()
95
96 self._scrollWheelZoomEnabled = True
97
98 self._clearMapTypes = False
99
100 self._controls = list()
101
102 self._mapTypes = list()
103
104 self._mapTypesChanged = False
105
106 self._reportMapBounds = False
107
108 self._markerResource = \
109 MarkerApplicationResource(self, self._markerSource)
110
111 self._apiKey = ''
112 self._clientLogLevel = 0
113
114 application.addResource(self._markerResource)
115
116 if apiKey_or_center is None:
117
118 self._center = (-0.001475, 51.477811)
119 self._zoom = 14
120 elif zoom is None:
121 self._apiKey = apiKey_or_center
122
123 self._center = (-0.001475, 51.477811)
124 self._zoom = 14
125 elif apiKey is None:
126 self._center = apiKey_or_center
127 self._zoom = zoom
128 else:
129 self._apiKey = apiKey
130 self._center = apiKey_or_center
131 self._zoom = zoom
132
133
134 - def paintContent(self, target):
135 super(GoogleMap, self).paintContent(target)
136 target.addVariable(self, 'center_lat', self._center[1])
137 target.addVariable(self, 'center_lng', self._center[0])
138 target.addVariable(self, 'zoom', self._zoom)
139 target.addVariable(self, 'swze', self._scrollWheelZoomEnabled)
140 target.addAttribute('apikey', self._apiKey)
141 target.addAttribute('loglevel', self._clientLogLevel)
142
143 for control in self._controls:
144 target.addAttribute(control, True)
145
146
147 if self._clickedMarker is not None:
148 target.addAttribute('marker', str(self._clickedMarker.getId()))
149 target.startTag('tabs')
150 tabs = self._clickedMarker.getInfoWindowContent()
151 for i in range(len(tabs)):
152 target.startTag('tab')
153 if len(tabs) > 1:
154 target.addAttribute('selected', tabs[i].isSelected())
155 target.addAttribute('label', tabs[i].getLabel())
156
157 tabs[i].getContent().paint(target)
158
159 target.endTag('tab')
160
161 target.endTag('tabs')
162
163 self._clickedMarker = None
164 elif self._markerSource is not None:
165 target.addAttribute('markerRes', self._markerResource)
166
167 if self._closeInfoWindow:
168 target.addAttribute('closeInfoWindow', True)
169 self._closeInfoWindow = False
170
171 target.startTag('overlays')
172
173 for poly in self._overlays.values():
174 target.startTag('o')
175 target.addAttribute('id', poly.getId())
176
177
178 sb = StringIO()
179 points = poly.getPoints()
180 for i in range(len(points)):
181 if i > 0:
182 sb.write(' ')
183 sb.write(str(points[i][1]) + ',' + str(points[i][0]))
184
185 target.addAttribute('points', sb.getvalue())
186 sb.close()
187
188 target.addAttribute('color', poly.getColor())
189 target.addAttribute('weight', poly.getWeight())
190 target.addAttribute('opacity', poly.getOpacity())
191 target.addAttribute('clickable', poly.isClickable())
192
193 if isinstance(poly, Polygon):
194 polygon = poly
195 target.addAttribute('fillcolor', polygon.getFillColor())
196 target.addAttribute('fillopacity', polygon.getFillOpacity())
197
198 target.endTag('o')
199
200 target.endTag('overlays')
201
202 if self._clearMapTypes:
203 target.addAttribute('clearMapTypes', True)
204 self._clearMapTypes = False
205
206 if self._mapTypesChanged:
207 target.startTag('mapTypes')
208
209 for mapType in self._mapTypes:
210 mapType.paintContent(target)
211
212 target.endTag('mapTypes')
213
214 self._mapTypesChanged = False
215
216 if self._reportMapBounds:
217 target.addAttribute('reportBounds', True)
218 self._reportMapBounds = False
219
220
222 """Receive and handle events and other variable changes from the
223 client.
224 """
225 super(GoogleMap, self).changeVariables(source, variables)
226
227 if 'click_pos' in variables:
228 self.fireClickEvent(variables.get('click_pos'))
229 self.requestRepaint()
230
231 moveEvent = False
232 intVar = variables.get('zoom')
233 if intVar is not None:
234 self._zoom = intVar
235 moveEvent = True
236
237 stringVar = variables.get('center')
238 if stringVar is not None and not (stringVar.strip() == ''):
239 self._center = GoogleMap.strToLL(stringVar)
240 moveEvent = True
241
242 stringVar = variables.get('bounds_ne')
243 if stringVar is not None and not (stringVar.strip() == ''):
244 self._boundsNE = GoogleMap.strToLL(stringVar)
245 moveEvent = True
246
247 stringVar = variables.get('bounds_sw')
248 if stringVar is not None and not (stringVar.strip() == ''):
249 self._boundsSW = GoogleMap.strToLL(stringVar)
250 moveEvent = True
251
252 if moveEvent:
253 self.fireMoveEvent()
254
255 if 'marker' in variables:
256 self._clickedMarker = self._markerSource.getMarker(
257 str(variables['marker']))
258 if self._clickedMarker is not None:
259 self.fireMarkerClickedEvent(self._clickedMarker)
260 if self._clickedMarker.getInfoWindowContent() is not None:
261 self.requestRepaint()
262
263 if 'markerMovedId' in variables:
264 markerID = str(variables['markerMovedId']).replace('\"', '')
265 markers = self._markerSource.getMarkers()
266
267 for mark in markers:
268 if mark.getId() == int(markerID):
269
270 lat = float(variables['markerMovedLat'])
271 lng = float(variables['markerMovedLong'])
272 mark.setLatLng((lng, lat))
273
274 self.fireMarkerMovedEvent(mark)
275 break
276
277
279 for listener in self._moveListeners:
280 listener.mapMoved(self._zoom, self._center, self._boundsNE,
281 self._boundsSW)
282
283
288
289
291 for m in self._markerListeners:
292 m.markerClicked(clickedMarker)
293
294
296 for m in self._markerMovedListeners:
297 m.markerMoved(movedMarker)
298
299
300
302 """Register a new map listener.
303
304 @param listener:
305 new L{IMapClickListener}, L{IMapMoveListener},
306 L{IMarkerMovedListener} or L{IMarkerClickListener}
307 to register
308
309 NOTE!! The marker that is clicked MUST have some information window
310 content! This is due to the implementation of the Widget, as the marker
311 click events do not propagate if there is not a information window
312 opened.
313 """
314 if (isinstance(listener, IMapClickListener) and
315 (iface is None or issubclass(iface, IMapClickListener))):
316 if listener not in self._mapClickListeners.contains():
317 self._mapClickListeners.append(listener)
318 elif (isinstance(listener, IMapMoveListener) and
319 (iface is None or issubclass(iface, IMapMoveListener))):
320 if listener not in self._moveListeners:
321 self._moveListeners.append(listener)
322 elif (isinstance(listener, IMarkerClickListener) and
323 (iface is None or issubclass(iface, IMarkerClickListener))):
324 if listener not in self._markerListeners:
325 self._markerListeners.append(listener)
326 elif (isinstance(listener, IMarkerMovedListener) and
327 (iface is None or issubclass(iface, IMarkerMovedListener))):
328 if listener not in self._markerMovedListeners:
329 self._markerMovedListeners.append(listener)
330
331 super(GoogleMap, self).addListener(listener, iface)
332
333
334
335
336
338 """Remove a map listener.
339
340 @param listener:
341 L{IMapClickListener}, L{IMapMoveListener},
342 L{IMarkerMovedListener} or L{IMarkerClickListener}
343 to remove
344 """
345 if (isinstance(listener, IMapClickListener) and
346 (iface is None or issubclass(iface, IMapClickListener))):
347 if listener in self._mapClickListeners:
348 self._mapClickListeners.remove(listener)
349 elif (isinstance(listener, IMapMoveListener) and
350 (iface is None or issubclass(iface, IMapMoveListener))):
351 if listener in self._moveListeners:
352 self._moveListeners.remove(listener)
353 elif (isinstance(listener, IMarkerClickListener) and
354 (iface is None or issubclass(iface, IMarkerClickListener))):
355 if listener in self._markerListeners:
356 self._markerListeners.remove(listener)
357 elif (isinstance(listener, IMarkerMovedListener) and
358 (iface is None or issubclass(iface, IMarkerMovedListener))):
359 if listener in self._markerMovedListeners:
360 self._markerMovedListeners.remove(listener)
361
362 super(GoogleMap, self).removeListener(listener, iface)
363
364
366 """Get current center coordinates of the map.
367 """
368 return self._center
369
370
372 """Set the current center coordinates of the map. This method can be
373 used to pan the map programmatically.
374
375 @param center:
376 the new center coordinates
377 """
378 self._center = center
379 self.requestRepaint()
380
381
383 """Get the current zoom level of the map.
384
385 @return: the current zoom level
386 """
387 return self._zoom
388
389
391 """Set the zoom level of the map. This method can be used to zoom the
392 map programmatically.
393 """
394 self._zoom = zoom
395 self.requestRepaint()
396
397
399 """Set the level of verbosity the client side uses for tracing or
400 displaying error messages.
401 """
402 self._clientLogLevel = level
403 self.requestRepaint()
404
405
407 """Get the level of verbosity the client side uses for tracing or
408 displaying error messages.
409 """
410 return self._clientLogLevel
411
412
414 """Get the coordinates of the north-east corner of the map.
415 """
416 return self._boundsNE
417
418
420 """Get the coordinates of the south-west corner of the map.
421 """
422 return self._boundsSW
423
424
426 """Set the L{MarkerSource} for the map.
427 """
428 self._markerSource = markerSource
429 self._markerResource._markerSource = markerSource
430
431
433 """Close the currently open info window, if any."""
434 self._closeInfoWindow = True
435 self.requestRepaint()
436
437
439 """Add a new {@link PolyOverlay} to the map. Does nothing if the
440 overlay already exist on the map.
441
442 @param overlay:
443 L{PolyOverlay} to add
444
445 @return: True if the overlay was added.
446 """
447 if not (overlay.getId() in self._overlays):
448 self._overlays[overlay.getId()] = overlay
449 self.requestRepaint()
450 return True
451 return False
452
453
455 """Update a L{PolyOverlay} on the map. Does nothing if the overlay
456 does not exist on the map.
457
458 @param overlay:
459 L{PolyOverlay} to update
460
461 @return: True if the overlay was updated.
462 """
463 if overlay.getId() in self._overlays:
464 self._overlays[overlay.getId()] = overlay
465 self.requestRepaint()
466 return True
467 return False
468
469
471 """Remove a L{PolyOverlay} from the map. Does nothing if the overlay
472 does not exist on the map.
473
474 @param overlay:
475 L{PolyOverlay} to remove
476
477 @return: True if the overlay was removed.
478 """
479 if overlay.getId() in self._overlays:
480 del self._overlays[overlay.getId()]
481 self.requestRepaint()
482 return True
483 return False
484
485
487 """Get the collection of L{PolyOverlay}s currently in the map.
488
489 @return: a list of overlays.
490 """
491 return self._overlays.values()
492
493
494 @classmethod
496 if latLngStr is None:
497 return None
498
499 nums = latLngStr.split(', ')
500 if len(nums) != 2:
501 return None
502
503 lat = float(nums[0][1:])
504 lng = float(nums[1][:len(nums[1]) - 1])
505 return (lng, lat)
506
507
509 """Add a Marker to the current MarkerSource. If the map has no marker
510 source a new L{BasicMarkerSource} is created.
511
512 @param marker:
513 Marker to add
514 """
515 if self._markerSource is None:
516 self.setMarkerSource(BasicMarkerSource())
517 self._markerSource.addMarker(marker)
518 self.requestRepaint()
519
520
528
529
534
535
538
539
542
543
545 if control not in self._controls:
546 self._controls.append(control)
547 return True
548 return False
549
550
552 return control in self._controls
553
554
556 if control in self._controls:
557 self._controls.remove(control)
558 return True
559 return False
560
561
562 - def addMapType(self, name, minZoom, maxZoom, copyright_, tileUrl, isPng,
563 opacity):
568
569
574
575
579
582
583 - def __init__(self, gmap, markerSource):
584 self._gmap = gmap
585 self._markerSource = markerSource
586
589
592
595
597 return "markersource.txt"
598
602
605
609 """Interface for listening map move and zoom events.
610
611 @author: Henri Muurimaa
612 @author: Richard Lincoln
613 """
614
615 - def mapMoved(self, newZoomLevel, newCenter, boundsNE, boundsSW):
616 """Handle a MapMoveEvent.
617
618 @param newZoomLevel:
619 New zoom level
620 @param newCenter:
621 New center coordinates
622 @param boundsNE:
623 Coordinates of the north-east corner of the map
624 @param boundsSW:
625 Coordinates of the south-west corner of the map
626 """
627 raise NotImplementedError
628
631 """Interface for listening map click events.
632
633 @author Henri Muurimaa
634 """
635
637 """Handle a MapClickEvent.
638
639 @param clickPos:
640 coordinates of the click event.
641 """
642 raise NotImplementedError
643
646 """Interface for listening marker click events.
647 """
648
650 """Handle a MarkerClickEvent.
651
652 @param clickedMarker:
653 the marker that was clicked.
654 """
655 raise NotImplementedError
656
659 """Interface for listening marker move events.
660 """
661
663 """Handle a MarkerMovedEvent.
664
665 @param movedMarker:
666 the marker that was moved.
667 """
668 raise NotImplementedError
669
672
673 - def __init__(self, name, minZoom, maxZoom, copyright_, tileUrl, isPng,
674 opacity):
675 self._name = name
676 self._minZoom = minZoom
677 self._maxZoom = maxZoom
678 self._copyright = copyright_
679 self._tileUrl = tileUrl
680 self._isPng = isPng
681 self._opacity = opacity
682
683
684 - def paintContent(self, target):
685 target.startTag('maptype')
686 target.addAttribute('name', self._name)
687 target.addAttribute('minZoom', self._minZoom)
688 target.addAttribute('maxZoom', self._maxZoom)
689 target.addAttribute('copyright', self._copyright)
690 target.addAttribute('tileUrl', self._tileUrl)
691 target.addAttribute('isPng', self._isPng)
692 target.addAttribute('opacity', self._opacity)
693 target.endTag('maptype')
694
695
698
699
702
703
706
707
710
711
714
715
717 return self._copyright
718