1 <& /elements/header.html, '' &>
3 <script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&key=<% $apikey %>"></script>
7 #map_canvas { margin: 0 auto; height: 100%; }
8 span.is_up { font-weight: bold; color: green }
9 span.is_down { font-weight: bold; color: red }
10 #search_location { width: 300px }
13 <div id="map_canvas"></div>
14 <input type="text" id="search_location" style="width: 180px">
17 <script type="text/javascript">
19 var baseMarkerStyle = {
22 path: google.maps.SymbolPath.CIRCLE,
31 var baseCoverageStyle = {
36 var coverageStyle = function(feature) {
37 var s = $.extend(true, {}, baseCoverageStyle, feature.getProperty('style'));
38 if ( feature.getProperty('low') ) {
40 } else if ( feature.getProperty('high') ) {
46 var markerStyle = function(feature) {
47 return $.extend(true, {}, baseMarkerStyle, feature.getProperty('style'));
51 var infoWindow = new google.maps.InfoWindow; // shared among all users
53 var clickHandler = function(ev) {
54 var feature = ev.feature;
55 if ( feature.getGeometry().getType() == 'Point' ) {
56 // then pop up an info box with the feature content
58 infoWindow.setPosition(feature.getGeometry().get());
60 if ( feature.getProperty('content') ) {
61 infoWindow.setContent(feature.getProperty('content'));
63 infoWindow.setContent('');
66 if ( feature.getProperty('url') ) {
68 url: feature.getProperty('url'),
69 success: function(data) {
70 infoWindow.setContent(data);
80 var zoomLayer = function(layer) {
81 // takes a google.maps.Data object
82 var bounds = new google.maps.LatLngBounds;
83 layer.forEach(function(feature) {
84 var g = feature.getGeometry();
85 if (g.getType() == 'Point') {
86 bounds.extend(g.get());
87 } else if (g.getArray) {
88 g.getArray().forEach(function(point) { bounds.extend(point); });
92 map.fitBounds(bounds);
95 // set up the main layer
96 var tower_data = new google.maps.Data;
97 tower_data.addGeoJson(<% encode_json($tower_data) %>);
98 tower_data.setStyle(markerStyle);
99 tower_data.addListener('click', clickHandler);
101 var towernums = <% encode_json(\@towernums) %>;
102 var tower_svc_data = {};
103 var tower_coverage_data = {};
105 var revertLayerStyles = function() {
106 // mostly, just to re-hide all connecting lines when something is hidden
107 for (var t in tower_svc_data) {
108 tower_svc_data[t].revertStyle();
112 towernums.forEach(function(towernum) {
113 var layer = new google.maps.Data;
114 tower_svc_data[towernum] = layer;
116 '<% $fsurl %>search/svc_broadband-json.cgi?towernum=' + towernum
118 layer.setStyle(markerStyle);
119 layer.addListener('click', clickHandler);
120 layer.addListener('click', function(ev) { // show connecting line
121 var id = ev.feature.getId();
122 var f_line = layer.getFeatureById(id + '/line');
123 layer.overrideStyle(f_line, { visible: true});
126 layer = new google.maps.Data;
128 '<% $fsurl %>misc/sector_coverage-json.cgi?towernum=' + towernum
130 layer.setStyle(coverageStyle);
131 tower_coverage_data[towernum] = layer;
134 function show_svc_data(towernum, show) {
136 tower_svc_data[towernum].setMap(window.map);
138 tower_svc_data[towernum].setMap(null);
142 function show_coverage_data(towernum, show) {
144 tower_coverage_data[towernum].setMap(window.map);
146 tower_coverage_data[towernum].setMap(null);
150 // toggle visibility of the services
151 infoWindow.addListener('domready', function(ev) {
152 var show_services_box = $('input[name=show_services]');
153 var towernum = show_services_box.val();
154 var is_shown = tower_svc_data[towernum].getMap() == map;
155 show_services_box.prop('checked', is_shown);
156 show_services_box.on('click', function(clickev) {
157 show_svc_data(towernum, this.checked);
160 var show_coverage_box = $('input[name=show_coverage]');
161 var towernum = show_coverage_box.val();
162 var is_shown = tower_coverage_data[towernum].getMap() == map;
163 show_coverage_box.prop('checked', is_shown);
164 show_coverage_box.on('click', function(clickev) {
165 show_coverage_data(towernum, this.checked);
169 infoWindow.addListener('closeclick', revertLayerStyles);
170 infoWindow.addListener('position_changed', revertLayerStyles);
172 var initMap = function() {
173 var canvas = $('#map_canvas');
175 // set window height correctly
176 canvas.css('height', window.innerHeight - (canvas.offset().top) - 30);
177 canvas.css('width', window.innerWidth - 30);
179 map = new google.maps.Map(canvas[0], { zoom: 6 });
182 var searchbox_input = $('#search_location')[0];
183 var searchbox = new google.maps.places.SearchBox(searchbox_input);
184 map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchbox_input);
186 map.addListener('bounds_changed', function() {
187 searchbox.setBounds(map.getBounds());
190 searchbox.addListener('places_changed', function() {
191 var places = searchbox.getPlaces();
192 // xxx fancy mode: find the nearest tower and estimate signal strength
194 if (places[0].geometry.viewport) {
195 map.fitBounds(places[0].geometry.viewport);
197 map.setCenter(places[0].geometry.location);
203 // put tower locations on map
204 tower_data.setMap(map);
205 zoomLayer(tower_data);
212 <& /elements/footer.html &>
215 die "access denied" unless
216 $FS::CurrentUser::CurrentUser->access_right('List services');
218 my $conf = new FS::Conf;
220 my $apikey = $conf->config('google_maps_api_key');
222 my @features; # geoJSON structure
224 my @towers = qsearch('tower', {
225 'latitude' => { op=>'!=', value=>''},
226 'longitude' => { op=>'!=', value=>''},
228 my %sectors; # towernum => arrayref
231 foreach my $tower (@towers) {
232 my $towernum = $tower->towernum;
233 push @towernums, $towernum;
235 $tower->longitude + 0,
236 $tower->latitude + 0,
241 id => 'tower/'.$towernum,
244 coordinates => \@coord,
250 url => $fsurl.'images/antenna-square-21x51.png',
251 anchor => { x => 10, y => 4 },
252 strokeColor => ($tower->color || 'black'),
255 content => include('.tower', $tower),
259 $sectors{$towernum} = [ $tower->tower_sector ];
264 type => 'FeatureCollection',
265 features => \@features
271 % my $can_edit = $FS::CurrentUser::CurrentUser->access_right('Configuration');
274 <a target="_blank" href="<% $fsurl %>edit/tower.html?<% $tower->towernum %>">
276 Tower #<% $tower->towernum %> | <% $tower->towername %>
281 % 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;
282 % my $num_down = FS::Record->scalar_sql("$count_query AND addr_status.up IS NULL AND addr_status._date IS NOT NULL");
283 % my $num_up = FS::Record->scalar_sql("$count_query AND addr_status.up IS NOT NULL");
284 <input type="checkbox" name="show_services" value="<% $tower->towernum %>">
285 <% emt('Show services') %>
286 ( <% $num_up %> <SPAN CLASS="is_up"><% emt('UP') %></SPAN>
287 <% $num_down %> <SPAN CLASS="is_down"><% emt('DOWN') %></SPAN> )
289 <input type="checkbox" name="show_coverage" value="<% $tower->towernum %>">
290 <% emt('Show coverage') %>