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