9c3ee984ffe0b29defec964719a4d7d38ffb9133
[freeside.git] / httemplate / search / tower-map.html
1 <& /elements/header.html, '' &>
2
3 <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&key=<% $apikey %>"></script>
4   
5 <style>
6 html { height: 100% }
7 #map_canvas { height: 100%;  }
8 span.is_up { font-weight: bold; color: green }
9 span.is_down { font-weight: bold; color: red }
10 </style>
11
12 <div id="map_canvas"></div>
13 <input type="text" id="search_location" style="width: 180px">
14
15
16 <script type="text/javascript">
17
18 var baseMarkerStyle = {
19   clickable: true,
20   icon: {
21     path: google.maps.SymbolPath.CIRCLE,
22     scale: 4,
23     fillColor: 'black',
24     fillOpacity: 1,
25     strokeColor: 'black',
26     strokeWeight: 1,
27   },
28 };
29 var featureStyle = function(feature) {
30   return $.extend(true, {}, baseMarkerStyle, feature.getProperty('style'));
31 }
32
33 var map;
34 var infoWindow = new google.maps.InfoWindow; // shared among all users
35
36 var clickHandler = function(ev) {
37   var feature = ev.feature;
38   if ( feature.getGeometry().getType() == 'Point' ) {
39     // then pop up an info box with the feature content
40     infoWindow.close();
41     infoWindow.setPosition(feature.getGeometry().get());
42
43     if ( feature.getProperty('content') ) {
44       infoWindow.setContent(feature.getProperty('content'));
45     } else {
46       infoWindow.setContent('');
47     }
48
49     if ( feature.getProperty('url') ) {
50       $.ajax({
51         url: feature.getProperty('url'),
52         success: function(data) {
53           infoWindow.setContent(data);
54         }
55       });
56       infoWindow.open(map);
57     } else {
58       infoWindow.open(map);
59     }
60   }
61 };
62
63 var zoomLayer = function(layer) {
64   // takes a google.maps.Data object
65   var bounds = new google.maps.LatLngBounds;
66   layer.forEach(function(feature) {
67     var g = feature.getGeometry();
68     if (g.getType() == 'Point') {
69       bounds.extend(g.get());
70     } else if (g.getArray) {
71       g.getArray().forEach(function(point) { bounds.extend(point); });
72     }
73   });
74
75   map.fitBounds(bounds);
76 };
77
78 // set up the main layer
79 var tower_data = new google.maps.Data;
80 tower_data.addGeoJson(<% encode_json($tower_data) %>);
81 tower_data.setStyle(featureStyle);
82 tower_data.addListener('click', clickHandler);
83
84 var towernums = <% encode_json(\@towernums) %>;
85 var tower_svc_data = {};
86
87 var revertLayerStyles = function() {
88   // mostly, just to re-hide all connecting lines when something is hidden
89   for (var t in tower_svc_data) {
90     tower_svc_data[t].revertStyle();
91   }
92 };
93
94 towernums.forEach(function(towernum) {
95   var layer = new google.maps.Data;
96   tower_svc_data[towernum] = layer;
97   layer.loadGeoJson(
98     '<% $fsurl %>search/svc_broadband-json.cgi?towernum=' + towernum
99   );
100   layer.setStyle(featureStyle);
101   layer.addListener('click', clickHandler);
102   layer.addListener('click', function(ev) { // show connecting line
103     var id = ev.feature.getId();
104     var f_line = layer.getFeatureById(id + '/line');
105     layer.overrideStyle(f_line, { visible: true});
106   });
107 });
108
109 function show_svc_data(towernum, show) {
110   if (show) {
111     tower_svc_data[towernum].setMap(window.map);
112   } else {
113     tower_svc_data[towernum].setMap(null);
114   }
115 };
116
117 // toggle visibility of the services
118 infoWindow.addListener('domready', function(ev) {
119   var show_services_box = $('input[name=show_services]');
120   var towernum = show_services_box.val();
121   var is_shown = tower_svc_data[towernum].getMap() == map;
122   show_services_box.prop('checked', is_shown);
123   show_services_box.on('click', function(clickev) {
124     show_svc_data(towernum, this.checked);
125   });
126 });
127
128 infoWindow.addListener('closeclick', revertLayerStyles);
129 infoWindow.addListener('position_changed', revertLayerStyles);
130
131 var initMap = function() {
132   var canvas = $('#map_canvas');
133
134   // set window height correctly
135   canvas.css('height', window.innerHeight - (canvas.offset().top) - 1);
136   canvas.css('width', window.innerWidth - (canvas.offset().left) * 2);
137
138   map = new google.maps.Map(canvas[0], { zoom: 6 });
139
140   //set up search box
141   var searchbox_input = $('#search_location')[0];
142   var searchbox = new google.maps.places.SearchBox(searchbox_input);
143   map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchbox_input);
144
145   map.addListener('bounds_changed', function() {
146     searchbox.setBounds(map.getBounds());
147   });
148
149   searchbox.addListener('places_changed', function() {
150     var places = searchbox.getPlaces();
151     // xxx fancy mode: find the nearest tower and estimate signal strength
152     if (places[0]) {
153       if (places[0].geometry.viewport) {
154         map.fitBounds(places[0].geometry.viewport);
155       } else {
156         map.setCenter(places[0].geometry.location);
157         map.setZoom(13);
158       }
159     }
160   });
161
162   // put tower locations on map
163   tower_data.setMap(map);
164   zoomLayer(tower_data);
165 };
166
167 $().ready(initMap);
168
169 </script>
170
171 <& /elements/footer.html &>
172 <%init>
173
174 die "access denied" unless
175   $FS::CurrentUser::CurrentUser->access_right('List services');
176
177 my $conf = new FS::Conf;
178
179 my $apikey = $conf->config('google_maps_api_key');
180
181 my @features; # geoJSON structure
182
183 my @towers = qsearch('tower', {
184   'latitude'  => { op=>'!=', value=>''},
185   'longitude' => { op=>'!=', value=>''},
186 });
187 my %sectors; # towernum => arrayref
188 my @overlays;
189 my @towernums;
190
191 foreach my $tower (@towers) {
192   my $towernum = $tower->towernum;
193   push @towernums, $towernum;
194   my @coord = (
195     $tower->longitude + 0,
196     $tower->latitude + 0,
197   );
198   push @features,
199   {
200     type      => 'Feature',
201     id        => 'tower/'.$towernum,
202     geometry  => {
203       type        => 'Point',
204       coordinates => \@coord,
205     },
206     properties => {
207       style     => {
208         icon => {
209           path        => undef,
210           url         => $fsurl.'images/antenna-square-21x51.png',
211           anchor      => { x => 10, y => 4 },
212           strokeColor => ($tower->color || 'black'),
213         },
214       },
215       content   => include('.tower', $tower),
216     },
217   };
218
219   $sectors{$towernum} = [ $tower->tower_sector ];
220   foreach my $sector (@{ $sectors{$towernum} }) {
221     if ( length($sector->image) > 0 ) {
222       my $o = {
223         url => $fsurl.'view/sector_map-png.cgi?' . $sector->sectornum
224       };
225       foreach (qw(south north west east)) {
226         $o->{$_} = $sector->get($_) + 0;
227       }
228       push @overlays, $o;
229     };
230   };
231
232 } # foreach $tower
233
234 my $tower_data = {
235   type => 'FeatureCollection',
236   features => \@features
237 };
238
239 </%init>
240 <%def .tower>
241 % my $tower = shift;
242 % my $can_edit = $FS::CurrentUser::CurrentUser->access_right('Configuration');
243 <H3>
244 % if ( $can_edit ) {
245   <a target="_blank" href="<% $fsurl %>edit/tower.html?<% $tower->towernum %>">
246 % }
247 Tower #<% $tower->towernum %> | <% $tower->towername %>
248 % if ( $can_edit ) {
249   </a>
250 % }
251 </H3>
252 % my $count_query = 'SELECT COUNT(*) FROM svc_broadband LEFT JOIN addr_status using (ip_addr) JOIN tower_sector USING (sectornum) WHERE tower_sector.towernum = '.$tower->towernum;
253 % my $num_down = FS::Record->scalar_sql("$count_query AND addr_status.up IS NULL AND addr_status._date IS NOT NULL");
254 % my $num_up = FS::Record->scalar_sql("$count_query AND addr_status.up IS NOT NULL");
255 <input type="checkbox" name="show_services" value="<% $tower->towernum %>">
256 <% emt('Show services') %>
257 ( <% $num_up %> <SPAN CLASS="is_up"><% emt('UP') %></SPAN>
258 <% $num_down %> <SPAN CLASS="is_down"><% emt('DOWN') %></SPAN> )
259 </%def>