summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Wells <mark@freeside.biz>2016-10-05 14:06:32 -0700
committerMark Wells <mark@freeside.biz>2016-10-05 14:06:32 -0700
commit07a1441c0fbce0a6ec76c7821e440444b4aec4f8 (patch)
treec368243803fc6b22db7004204204fce3241669da
parent4c072dbb00979f34b9855b792253b612cc8b226c (diff)
map selection of tower site
-rw-r--r--httemplate/browse/tower.html6
-rw-r--r--httemplate/docs/license.html4
-rw-r--r--httemplate/edit/tower.html1
-rw-r--r--httemplate/elements/jquery-gmaps-latlon-picker.js254
-rw-r--r--httemplate/elements/mapselect.html72
-rw-r--r--httemplate/elements/tower_sector.html69
-rw-r--r--httemplate/elements/tr-tower_sectors.html6
-rw-r--r--httemplate/misc/sector_coverage-json.cgi2
-rwxr-xr-xhttemplate/search/tower-map.html21
9 files changed, 344 insertions, 91 deletions
diff --git a/httemplate/browse/tower.html b/httemplate/browse/tower.html
index 16e44c6c0..555b8a39f 100644
--- a/httemplate/browse/tower.html
+++ b/httemplate/browse/tower.html
@@ -3,8 +3,10 @@
'name' => 'towers',
'menubar' => [ 'Add a new tower' =>
$p.'edit/tower.html',
- 'Sector coverage maps' =>
- $p.'search/sector.html',
+ #'Sector coverage maps' =>
+ # $p.'search/sector.html',
+ 'Tower map' =>
+ $p.'search/tower-map.html',
'Download CSV for towercoverage.com' =>
$p.'misc/tower-export.html?format=tc'
],
diff --git a/httemplate/docs/license.html b/httemplate/docs/license.html
index e5e3eab65..0f0a3c61d 100644
--- a/httemplate/docs/license.html
+++ b/httemplate/docs/license.html
@@ -134,6 +134,10 @@ under the terms of the MIT license.
Contains the XRegExp library by Steven Levithan, licensed under the terms
of the MIT license.
+<P>
+Contains the Google Maps Longitude and Latitude Picker, by Richard Dancsi,
+licensed under the terms of the MIT license.
+
<!-- artwork -->
<P>
diff --git a/httemplate/edit/tower.html b/httemplate/edit/tower.html
index dae5d5a89..946a1405e 100644
--- a/httemplate/edit/tower.html
+++ b/httemplate/edit/tower.html
@@ -2,6 +2,7 @@
name_singular => 'tower',
table => 'tower',
viewall_dir => 'browse',
+ html_init => include('/elements/mapselect.html'),
fields => [ 'towername',
{ field=>'disabled', type=>'checkbox', value=>'Y', },
{ field=>'color', type=>'pickcolor' },
diff --git a/httemplate/elements/jquery-gmaps-latlon-picker.js b/httemplate/elements/jquery-gmaps-latlon-picker.js
new file mode 100644
index 000000000..b839df75a
--- /dev/null
+++ b/httemplate/elements/jquery-gmaps-latlon-picker.js
@@ -0,0 +1,254 @@
+/**
+ *
+ * A JQUERY GOOGLE MAPS LATITUDE AND LONGITUDE LOCATION PICKER
+ * version 1.2
+ *
+ * Supports multiple maps. Works on touchscreen. Easy to customize markup and CSS.
+ *
+ * To see a live demo, go to:
+ * http://www.wimagguc.com/projects/jquery-latitude-longitude-picker-gmaps/
+ *
+ * by Richard Dancsi
+ * http://www.wimagguc.com/
+ *
+ */
+
+(function($) {
+
+// for ie9 doesn't support debug console >>>
+if (!window.console) window.console = {};
+if (!window.console.log) window.console.log = function () { };
+// ^^^
+
+/* local modification */
+window.gMapsLatLonPickerState = {};
+
+$.fn.gMapsLatLonPicker = (function() {
+
+ var _self = this;
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // PARAMETERS (MODIFY THIS PART) //////////////////////////////////////////////////////////////
+ _self.params = {
+ defLat : 0,
+ defLng : 0,
+ defZoom : 1,
+ queryLocationNameWhenLatLngChanges: true,
+ queryElevationWhenLatLngChanges: true,
+ mapOptions : {
+ mapTypeId: google.maps.MapTypeId.ROADMAP,
+ /* local modification */
+ //mapTypeControl: false,
+ disableDoubleClickZoom: true,
+ zoomControlOptions: true,
+ streetViewControl: false
+ },
+ strings : {
+ markerText : "Drag this Marker",
+ error_empty_field : "Couldn't find coordinates for this place",
+ error_no_results : "Couldn't find coordinates for this place"
+ }
+ };
+
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // VARIABLES USED BY THE FUNCTION (DON'T MODIFY THIS PART) ////////////////////////////////////
+ _self.vars = {
+ ID : null,
+ LATLNG : null,
+ map : null,
+ marker : null,
+ geocoder : null
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // PRIVATE FUNCTIONS FOR MANIPULATING DATA ////////////////////////////////////////////////////
+ var setPosition = function(position) {
+ _self.vars.marker.setPosition(position);
+ _self.vars.map.panTo(position);
+
+ $(_self.vars.cssID + ".gllpZoom").val( _self.vars.map.getZoom() );
+ $(_self.vars.cssID + ".gllpLongitude").val( position.lng() );
+ $(_self.vars.cssID + ".gllpLatitude").val( position.lat() );
+
+ $(_self.vars.cssID).trigger("location_changed", $(_self.vars.cssID));
+
+ if (_self.params.queryLocationNameWhenLatLngChanges) {
+ getLocationName(position);
+ }
+ if (_self.params.queryElevationWhenLatLngChanges) {
+ getElevation(position);
+ }
+ };
+
+ // for reverse geocoding
+ var getLocationName = function(position) {
+ var latlng = new google.maps.LatLng(position.lat(), position.lng());
+ _self.vars.geocoder.geocode({'latLng': latlng}, function(results, status) {
+ if (status == google.maps.GeocoderStatus.OK && results[1]) {
+ $(_self.vars.cssID + ".gllpLocationName").val(results[1].formatted_address);
+ } else {
+ $(_self.vars.cssID + ".gllpLocationName").val("");
+ }
+ $(_self.vars.cssID).trigger("location_name_changed", $(_self.vars.cssID));
+ });
+ };
+
+ // for getting the elevation value for a position
+ var getElevation = function(position) {
+ var latlng = new google.maps.LatLng(position.lat(), position.lng());
+
+ var locations = [latlng];
+
+ var positionalRequest = { 'locations': locations };
+
+ _self.vars.elevator.getElevationForLocations(positionalRequest, function(results, status) {
+ if (status == google.maps.ElevationStatus.OK) {
+ if (results[0]) {
+ $(_self.vars.cssID + ".gllpElevation").val( results[0].elevation.toFixed(3));
+ } else {
+ $(_self.vars.cssID + ".gllpElevation").val("");
+ }
+ } else {
+ $(_self.vars.cssID + ".gllpElevation").val("");
+ }
+ $(_self.vars.cssID).trigger("elevation_changed", $(_self.vars.cssID));
+ });
+ };
+
+ // search function
+ var performSearch = function(string, silent) {
+ if (string == "") {
+ if (!silent) {
+ displayError( _self.params.strings.error_empty_field );
+ }
+ return;
+ }
+ _self.vars.geocoder.geocode(
+ {"address": string},
+ function(results, status) {
+ if (status == google.maps.GeocoderStatus.OK) {
+ $(_self.vars.cssID + ".gllpZoom").val(11);
+ _self.vars.map.setZoom( parseInt($(_self.vars.cssID + ".gllpZoom").val()) );
+ setPosition( results[0].geometry.location );
+ } else {
+ if (!silent) {
+ displayError( _self.params.strings.error_no_results );
+ }
+ }
+ }
+ );
+ };
+
+ // error function
+ var displayError = function(message) {
+ alert(message);
+ };
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////
+ // PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////
+ var publicfunc = {
+
+ // INITIALIZE MAP ON DIV //////////////////////////////////////////////////////////////////
+ init : function(object) {
+
+ if ( !$(object).attr("id") ) {
+ if ( $(object).attr("name") ) {
+ $(object).attr("id", $(object).attr("name") );
+ } else {
+ $(object).attr("id", "_MAP_" + Math.ceil(Math.random() * 10000) );
+ }
+ }
+
+ _self.vars.ID = $(object).attr("id");
+ _self.vars.cssID = "#" + _self.vars.ID + " ";
+
+ _self.params.defLat = $(_self.vars.cssID + ".gllpLatitude").val() ? $(_self.vars.cssID + ".gllpLatitude").val() : _self.params.defLat;
+ _self.params.defLng = $(_self.vars.cssID + ".gllpLongitude").val() ? $(_self.vars.cssID + ".gllpLongitude").val() : _self.params.defLng;
+ _self.params.defZoom = $(_self.vars.cssID + ".gllpZoom").val() ? parseInt($(_self.vars.cssID + ".gllpZoom").val()) : _self.params.defZoom;
+
+ _self.vars.LATLNG = new google.maps.LatLng(_self.params.defLat, _self.params.defLng);
+
+ _self.vars.MAPOPTIONS = _self.params.mapOptions;
+ _self.vars.MAPOPTIONS.zoom = _self.params.defZoom;
+ _self.vars.MAPOPTIONS.center = _self.vars.LATLNG;
+
+ _self.vars.map = new google.maps.Map($(_self.vars.cssID + ".gllpMap").get(0), _self.vars.MAPOPTIONS);
+ _self.vars.geocoder = new google.maps.Geocoder();
+ _self.vars.elevator = new google.maps.ElevationService();
+
+ _self.vars.marker = new google.maps.Marker({
+ position: _self.vars.LATLNG,
+ map: _self.vars.map,
+ title: _self.params.strings.markerText,
+ draggable: true
+ });
+
+ // Set position on doubleclick
+ google.maps.event.addListener(_self.vars.map, 'dblclick', function(event) {
+ setPosition(event.latLng);
+ });
+
+ // Set position on marker move
+ google.maps.event.addListener(_self.vars.marker, 'dragend', function(event) {
+ setPosition(_self.vars.marker.position);
+ });
+
+ // Set zoom feld's value when user changes zoom on the map
+ google.maps.event.addListener(_self.vars.map, 'zoom_changed', function(event) {
+ $(_self.vars.cssID + ".gllpZoom").val( _self.vars.map.getZoom() );
+ $(_self.vars.cssID).trigger("location_changed", $(_self.vars.cssID));
+ });
+
+ // Update location and zoom values based on input field's value
+ $(_self.vars.cssID + ".gllpUpdateButton").bind("click", function() {
+ var lat = $(_self.vars.cssID + ".gllpLatitude").val();
+ var lng = $(_self.vars.cssID + ".gllpLongitude").val();
+ var latlng = new google.maps.LatLng(lat, lng);
+ _self.vars.map.setZoom( parseInt( $(_self.vars.cssID + ".gllpZoom").val() ) );
+ setPosition(latlng);
+ });
+
+ // Search function by search button
+ $(_self.vars.cssID + ".gllpSearchButton").bind("click", function() {
+ performSearch( $(_self.vars.cssID + ".gllpSearchField").val(), false );
+ });
+
+ // Search function by gllp_perform_search listener
+ $(document).bind("gllp_perform_search", function(event, object) {
+ performSearch( $(object).attr('string'), true );
+ });
+
+ // Zoom function triggered by gllp_perform_zoom listener
+ $(document).bind("gllp_update_fields", function(event) {
+ var lat = $(_self.vars.cssID + ".gllpLatitude").val();
+ var lng = $(_self.vars.cssID + ".gllpLongitude").val();
+ var latlng = new google.maps.LatLng(lat, lng);
+ _self.vars.map.setZoom( parseInt( $(_self.vars.cssID + ".gllpZoom").val() ) );
+ setPosition(latlng);
+ });
+
+ /* local modification */
+ window.gMapsLatLonPickerState[_self.vars.ID] =
+ {
+ vars : _self.vars,
+ params : _self.params
+ };
+ } // publicfunc
+
+ }
+
+ return publicfunc;
+});
+
+}(jQuery));
+
+$(document).ready( function() {
+ $(".gllpLatlonPicker").each(function() {
+ $(document).gMapsLatLonPicker().init( $(this) );
+ });
+});
+
+$(document).bind("location_changed", function(event, object) {
+ console.log("changed: " + $(object).attr('id') );
+});
diff --git a/httemplate/elements/mapselect.html b/httemplate/elements/mapselect.html
new file mode 100644
index 000000000..7d1447f98
--- /dev/null
+++ b/httemplate/elements/mapselect.html
@@ -0,0 +1,72 @@
+<%init>
+my $conf = new FS::Conf;
+my $apikey = $conf->config('google_maps_api_key');
+
+my %opt = @_;
+
+# Currently requires two fields named 'latitude' and 'longitude'.
+# Those should be in the edit form. This widget should NOT be in the
+# edit form (or it will submit a bunch of spurious fields, plus pressing
+# "enter" in the search box will submit the form).
+
+</%init>
+<script src="https://maps.googleapis.com/maps/api/js?v=3&libraries=places&key=<% $apikey %>"></script>
+<script src="<% $fsurl %>elements/jquery-gmaps-latlon-picker.js"></script>
+<style>
+ .gllpLatlonPicker, .gllpMap { width: 600px; height: 600px }
+ #search_location { width: 300px }
+</style>
+<fieldset id="latlonpicker" class="gllpLatlonPicker" style="float: right">
+ <input type="text" id="search_location">
+ <input type="hidden" class="gllpLatitude" id="map_lat">
+ <input type="hidden" class="gllpLongitude" id="map_lon">
+ <input type="hidden" class="gllpElevation" id="map_alt">
+ <input type="hidden" class="gllpZoom" id="map_zoom" value="12">
+ <div class="gllpMap"></div>
+</fieldset>
+<br/>
+
+<script>
+
+$(function() {
+ var container = $('#latlonpicker');
+ var map = gMapsLatLonPickerState['latlonpicker'].vars.map;
+
+ var lat = $('#latitude');
+ var lon = $('#longitude');
+ var alt = $('#altitude');
+ $('#map_lat').val(lat.val());
+ $('#map_lon').val(lon.val());
+ $('#map_alt').val(alt.val());
+ $(document).trigger('gllp_update_fields');
+
+ $(document).on('location_changed', function(ev, obj) {
+ lat.val($('#map_lat').val());
+ lon.val($('#map_lon').val());
+ });
+
+ // requires the Elevation API to be enabled
+ $(document).on('elevation_changed', function(ev, obj) {
+ alt.val($('#map_alt').val());
+ });
+
+ // bypass gllp's search mechanism, use the cooler Places search
+ var searchbox_input = $('#search_location')[0];
+ var searchbox = new google.maps.places.SearchBox(searchbox_input);
+ map.controls[google.maps.ControlPosition.TOP_RIGHT].push(searchbox_input);
+
+ map.addListener('bounds_changed', function() {
+ searchbox.setBounds(map.getBounds());
+ });
+
+ searchbox.addListener('places_changed', function() {
+ var places = searchbox.getPlaces();
+ if (places[0]) {
+ $('#map_lat').val( places[0].geometry.location.lat() );
+ $('#map_lon').val( places[0].geometry.location.lng() );
+ $('#map_zoom').val(12);
+ $(document).trigger('gllp_update_fields');
+ }
+ });
+});
+</script>
diff --git a/httemplate/elements/tower_sector.html b/httemplate/elements/tower_sector.html
deleted file mode 100644
index dacb5ba92..000000000
--- a/httemplate/elements/tower_sector.html
+++ /dev/null
@@ -1,69 +0,0 @@
-% unless ( $opt{'js_only'} ) {
-
- <INPUT TYPE="hidden" NAME="<%$name%>" ID="<%$id%>" VALUE="<% $curr_value %>">
-
- <TABLE>
- <TR>
-% foreach my $field ( @fields ) {
-
- <TD>
- <INPUT TYPE = "text"
- NAME = "<%$name%>_<%$field%>"
- ID = "<%$id%>_<%$field%>"
- SIZE = "<% $size{$field} || 15 %>"
- VALUE = "<% scalar($cgi->param($name."_$field"))
- || $tower_sector->get($field) |h %>"
- <% $onchange %>
- ><BR>
- <FONT SIZE="-1"><% $label{$field} %></FONT>
- </TD>
-% }
- </TR>
- </TABLE>
-
-
-% }
-<%init>
-
-my( %opt ) = @_;
-
-my $name = $opt{'element_name'} || $opt{'field'} || 'sectornum';
-my $id = $opt{'id'} || 'sectornum';
-
-my $curr_value = $opt{'curr_value'} || $opt{'value'};
-
-my $onchange = '';
-if ( $opt{'onchange'} ) {
- $onchange = $opt{'onchange'};
- $onchange .= '(this)' unless $onchange =~ /\(\w*\);?$/;
- $onchange =~ s/\(what\);/\(this\);/g; #ugh, terrible hack. all onchange
- #callbacks should act the same
- $onchange = 'onChange="'. $onchange. '"';
-}
-
-my $tower_sector;
-if ( $curr_value ) {
- $tower_sector = qsearchs('tower_sector', { 'sectornum' => $curr_value } );
-} else {
- $tower_sector = new FS::tower_sector {};
-}
-
-my %size = ( 'title' => 12 );
-
-tie my %label, 'Tie::IxHash',
- 'sectorname' => 'Name',
- 'ip_addr' => 'IP Address',
- 'height' => 'Height',
- 'freq_mhz' => 'Freq. (MHz)',
- 'direction' => 'Direction', # or a button to set these to 0 for omni
- 'downtilt' => 'Downtilt',
- 'width' => 'Horiz. width',
- 'v_width' => 'Vert. width',
- 'sector_range' => 'Range',
- 'db_high' => 'High quality margin (dB)',
- 'db_low' => 'Low quality margin (dB)',
-;
-
-my @fields = keys %label;
-
-</%init>
diff --git a/httemplate/elements/tr-tower_sectors.html b/httemplate/elements/tr-tower_sectors.html
index 5351becd8..4e8f3fb47 100644
--- a/httemplate/elements/tr-tower_sectors.html
+++ b/httemplate/elements/tr-tower_sectors.html
@@ -31,7 +31,7 @@ if ( $cgi->param('error') ) {
my $id = $opt{id} || $opt{field} || 'sectornum';
</%init>
-<& tablebreak-tr-title.html, %opt &>
+<& tablebreak-tr-title.html, value => 'Sectors' &>
<style>
.ui-tabs-nav a {
@@ -110,11 +110,11 @@ $(function() {
var tabs = $( '#'+id+'_tabs' ).tabs();
function changedSectorName() {
- var this_panel = $(this).parent();
+ var this_panel = $(this).closest('div');
var this_tab = tabs.find('#' + this_panel.prop('id') + '_tab');
// if this is the last panel, make a new one
if (this_panel.next().length == 0) {
- addSector(this_panel);
+ addSector();
}
// and update the current tab's text with the sector name
this_tab.find('a').text($(this).val());
diff --git a/httemplate/misc/sector_coverage-json.cgi b/httemplate/misc/sector_coverage-json.cgi
index 9fd08d7db..37595f5e2 100644
--- a/httemplate/misc/sector_coverage-json.cgi
+++ b/httemplate/misc/sector_coverage-json.cgi
@@ -17,7 +17,7 @@ foreach my $sector (@sectors) {
my $sectornum = $sector->sectornum;
my $low = $sector->db_low;
my $high = $sector->db_high;
- my $color = $sector->tower->color || 'green';
+ my $color = '#' . ($sector->tower->color || 'ffffff');
foreach my $coverage ( $sector->sector_coverage ) {
#note $coverage->geometry is already JSON
my $level = $coverage->db_loss;
diff --git a/httemplate/search/tower-map.html b/httemplate/search/tower-map.html
index 914457d89..4460db8fe 100755
--- a/httemplate/search/tower-map.html
+++ b/httemplate/search/tower-map.html
@@ -4,9 +4,10 @@
<style>
html { height: 100% }
-#map_canvas { height: 100%; }
+#map_canvas { margin: 0 auto; height: 100%; }
span.is_up { font-weight: bold; color: green }
span.is_down { font-weight: bold; color: red }
+#search_location { width: 300px }
</style>
<div id="map_canvas"></div>
@@ -172,8 +173,8 @@ var initMap = function() {
var canvas = $('#map_canvas');
// set window height correctly
- canvas.css('height', window.innerHeight - (canvas.offset().top) - 1);
- canvas.css('width', window.innerWidth - (canvas.offset().left) * 2);
+ canvas.css('height', window.innerHeight - (canvas.offset().top) - 30);
+ canvas.css('width', window.innerWidth - 30);
map = new google.maps.Map(canvas[0], { zoom: 6 });
@@ -194,7 +195,7 @@ var initMap = function() {
map.fitBounds(places[0].geometry.viewport);
} else {
map.setCenter(places[0].geometry.location);
- map.setZoom(13);
+ map.setZoom(14);
}
}
});
@@ -225,7 +226,6 @@ my @towers = qsearch('tower', {
'longitude' => { op=>'!=', value=>''},
});
my %sectors; # towernum => arrayref
-my @overlays;
my @towernums;
foreach my $tower (@towers) {
@@ -257,17 +257,6 @@ foreach my $tower (@towers) {
};
$sectors{$towernum} = [ $tower->tower_sector ];
- foreach my $sector (@{ $sectors{$towernum} }) {
- if ( length($sector->image) > 0 ) {
- my $o = {
- url => $fsurl.'view/sector_map-png.cgi?' . $sector->sectornum
- };
- foreach (qw(south north west east)) {
- $o->{$_} = $sector->get($_) + 0;
- }
- push @overlays, $o;
- };
- };
} # foreach $tower