can pass starting lat/lon/zoom to googlemap
[unfold.git] / plugins / googlemap / googlemap.js
1 /**
2  * Description: display a query result in a googlemap
3  * Copyright (c) 2012 UPMC Sorbonne Universite - INRIA
4  * License: GPLv3
5  */
6
7 // xxx TODO -- this one could use a bit of cleaning like what was done for the first plugins
8 // especially wrt using 'instance' and 'data' in such a confusing way
9
10 (function( jQuery ){
11
12   var methods = {
13      init : function( options ) {
14
15        return this.each(function(){
16          
17          var $this = jQuery(this),
18              data = $this.data('GoogleMap'), GoogleMap = jQuery('<div />', {text : $this.attr('title')});
19          
20          // If the plugin hasn't been initialized yet
21          if ( ! data ) {
22          
23            /* Plugin initialization */
24
25             //google.load('maps', '3', { other_params: 'sensor=false' });
26             //google.setOnLoadCallback(initialize);
27
28             $this.data('map', null);
29             $this.data('markerCluster', null);
30             $this.data('markers', []);
31
32              console.log ("lat=" + options.latitude + " long=" + options.longitude + " and zoom=" + options.zoom);
33             var myLatlng = new google.maps.LatLng(options.latitude, options.longitude);
34             var myOptions = {
35               zoom: options.zoom,
36               center: myLatlng,
37               mapTypeId: google.maps.MapTypeId.ROADMAP
38             }
39       
40             var map = new google.maps.Map(document.getElementById("map"), myOptions);
41             $this.data('map', map);
42
43             /* End of plugin initialization */
44
45             jQuery(this).data('GoogleMap', {
46                                 plugin_uuid: options.plugin_uuid,
47                                 query_uuid: options.query_uuid,
48                 target : $this,
49                 current_resources: Array(),
50                 GoogleMap : GoogleMap
51             });
52
53             /* Subscribe to query updates */
54             jQuery.subscribe('/results/' + options.query_uuid + '/changed', {instance: $this}, update_map);
55             jQuery.subscribe('/update-set/' + options.query_uuid, {instance: $this}, on_resource_changed);
56             jQuery.subscribe('/query/' + options.query_uuid + '/changed', {instance: $this}, query_changed);
57             
58             //data = jQuery(this).data();
59             
60             // TODO: Change the status of a node based on the actions in GoogleMap plugin or in other plugins (e.g. DataTables)
61             // Can we publish a value in results row['sliver'] ???
62             // Today, the value is attached or undefined
63             // But can we think about a added/removed status ???
64             // This plugin would update the map based on the results published
65
66          }
67        });
68      },
69     destroy : function( ) {
70
71         return this.each(function(){
72             var $this = jQuery(this), data = $this.data('GoogleMap');
73                         jQuery(window).unbind('GoogleMap');
74                         data.GoogleMap.remove();
75                         $this.removeData('GoogleMap');
76                 })
77
78     },
79 /*
80     reposition : function( ) { // ... },
81     update : function( ) { // ... },
82     hide : function( ) { // ... },
83 */
84     show : function( content ) {
85         google.maps.event.trigger(map, 'resize');
86     }
87   };
88
89     jQuery.fn.GoogleMap = function( method ) {
90                 /* Method calling logic */
91                 if ( methods[method] ) {
92                         return methods[ method ].apply( this, Array.prototype.slice.call( arguments, 1 ));
93                 } else if ( typeof method === 'object' || ! method ) {
94                         return methods.init.apply( this, arguments );
95                 } else {
96                         jQuery.error( 'Method ' +  method + ' does not exist on jQuery.GoogleMap' );
97                 }    
98
99     };
100
101     /* Private methods */
102         function query_changed(e,query){
103             var data = e.data.instance.data();
104             /* Compare current and advertised query to get added and removed fields */
105             previous_query=data.current_query;
106             /* Save the query as the current query */
107             data.current_query=query;
108             
109             var rows=[];
110             if(typeof(data.results)!="undefined" && data.results.length>0){
111                 jQuery.each(data.results, function(i, row){
112                     jQuery.each(query.filter, function (idx, filter){
113                         if(get_value(row[filter[0]])==filter[2]){
114                             rows.push(row);
115                         }
116                     });
117                 });
118                 data.markerCluster=[];
119                 data.markers=[];
120                 var myLatlng = new google.maps.LatLng(34.397, 150.644);
121                 var myOptions = {
122                     zoom: 2,
123                     center: myLatlng,
124                     mapTypeId: google.maps.MapTypeId.ROADMAP
125                 }
126                 map = new google.maps.Map(jQuery('#map')[0],myOptions);
127                 data.map=map;
128                 //map.clearMarkers();
129                 update_map(e, rows);
130             }
131         }
132         function update_map(e, rows) {            
133             var data = e.data.instance.data();
134             var instance_ = e.data.instance;
135             //$plugindiv.closest('.need-spin').spin(false);
136             instance_.closest('.need-spin').spin(false);
137
138
139             if (rows === undefined || !rows || rows.length==0) {
140                 messages.warning ("Empty result in googlemap.update_map - nothing to show");
141                 return;
142             }
143
144             if(rows.length==0) {
145                 rows=data.results;
146             }
147
148             if(typeof(data.results)=="undefined" || data.results==null){
149                 data.results=rows;
150             }
151             var map = data.map;
152             var markerCluster = data.markerCluster;            
153             var markers = data.markers;
154             var coords = new Array();
155             var infowindow = new google.maps.InfoWindow();
156             /*
157             if(typeof(markers)!="undefined" && markers.length>0){
158                 map.clearMarkers();
159             }*/
160
161             data.current_resources = Array();
162
163             jQuery.each(data.results, function(i, result){
164                 // get the coordinates
165                 var latitude=get_value(result['latitude']);
166                 var longitude=get_value(result['longitude']);
167                 var hash = latitude + longitude;
168
169                 // check to see if we've seen this hash before
170                 if(coords[hash] == null) {
171                     // get coordinate object
172                     var myLatlng = new google.maps.LatLng(latitude, longitude);
173                     // store an indicator that we've seen this point before
174                     coords[hash] = 1;
175                 } else {
176                     // add some randomness to this point 1500 = 100 meters, 15000 = 10 meters
177                     var lat = latitude + (Math.random() -.5) / 1500; 
178                     var lng = longitude + (Math.random() -.5) / 1500; 
179
180                     // get the coordinate object
181                     var myLatlng = new google.maps.LatLng(lat, lng);
182                 }
183                 // If the node is attached to the slice, action will be Remove; else action will be add to slice
184                 if (typeof(result['sliver']) != 'undefined') {
185                     data.current_resources.push(result['urn']);
186                     action="del";
187                     action_class="ui-icon-circle-minus";
188                     action_message="Remove from slice";
189                 }else{
190                     action="add";
191                     action_class="ui-icon-circle-plus";
192                     action_message="Add to slice";
193                 }
194                 // XXX not working
195                 if (!(result['latitude'])) {
196                     return true;
197                 }
198
199                 //jQuery(".map-button").click(button_click);
200                 if(jQuery.inArray(result,rows)>-1){
201                     var marker = new google.maps.Marker({
202                         position: myLatlng,
203                         title: get_value(result['ip']),
204                         // This should be done by the rendering
205                         content: '<p>Agent: ' + get_value(result['ip']) + ' (' + get_value(result['urn']) + ')<br/>Platform: ' + get_value(result['platform'])+'</p>' +
206                                 '<div class="map-button" id="'+action+'/'+get_value(result['urn'])+'" style="cursor:pointer;">'+
207                                 '<span class="ui-icon '+action_class+'" style="clear:both;float:left;"></span>'+action_message+
208                                 '</div>'
209                     }); 
210
211                     google.maps.event.addListener(marker, 'click', function() {
212                             infowindow.content = this.content;
213                             infowindow.open(map, this);
214                             // onload of the infowindow on the map, bind a click on a button
215                             google.maps.event.addListener(infowindow, 'domready', function() {
216                                 jQuery('.map-button').unbind('click');
217                                 jQuery(".map-button").click({instance: instance_}, button_click);
218                             });
219                     });
220                     markers.push(marker);
221                 }
222             });
223             markerCluster = new MarkerClusterer(map, markers, {zoomOnClick: false});
224             google.maps.event.addListener(markerCluster, "clusterclick", function (cluster) {
225                 var markers = cluster.getMarkers();
226                 var bounds  = new google.maps.LatLngBounds();
227               /* 
228               * date: 24/05/2012
229               * author: lbaron
230               * Firefox JS Error - replaced $.each by JQuery.each
231               */                  
232                 jQuery.each(markers, function(i, marker){
233                     bounds.extend(marker.getPosition()); 
234                     });
235                 //map.setCenter(bounds.getCenter(), map.getBoundsZoomLevel(bounds));
236                 map.fitBounds(bounds);
237             });
238             data.markerCluster=markerCluster;
239         }
240         function button_click(e){
241             var data = e.data.instance.data().GoogleMap;
242             var op_value=this.id.split("/");
243             if(op_value.length>0){
244                 jQuery.publish('selected', op_value[0]+'/'+op_value[1]);
245                 var value = op_value[1];
246
247                 if (op_value[0] == 'add') {
248                     data.current_resources.push(value);
249                 } else {
250                     tmp = jQuery.grep(data.current_resources, function(x) { return x != value; });
251                     data.current_resources = tmp;
252                 }
253
254                 /* inform slice that our selected resources have changed */
255                 jQuery.publish('/update-set/' + data.query_uuid, [data.current_resources, true, e.data.instance]);
256             }
257         }
258
259     function on_resource_changed(e, resources, instance)
260     {
261         /* TODO OPENLAB : this query determines which checkboxes must be checked */
262         if (instance == e.data.instance)
263             return;
264         data = e.data.instance.data().GoogleMap;
265
266         previous_resources = data.current_resources;
267         data.current_resources = resources;
268
269         /* TODO We uncheck all checkboxes ... */
270         //jQuery('datatables-checkbox-' + data.options.plugin_uuid).attr('checked', false);
271         /* ... and check the ones specified in the resource list */
272         //jQuery.each(data.current_resources, function(index, urn) {
273         //    jQuery('#datatables-checkbox-' + data.options.plugin_uuid + "-" + urn).attr('checked', true)
274         //});
275         
276     }
277
278 })( jQuery );