kopia lustrzana https://github.com/glidernet/ogn-python
Merge branch 'master' of http://github.com/Meisterschueler/ogn-python
commit
df0f32dc30
|
@ -3,7 +3,7 @@ from datetime import datetime, timedelta
|
|||
|
||||
from sqlalchemy import func, case
|
||||
from sqlalchemy.sql.expression import label
|
||||
from ogn_python.model import Receiver
|
||||
from ogn_python.model import Receiver, ReceiverCoverage
|
||||
|
||||
from ogn_python import db
|
||||
|
||||
|
@ -19,20 +19,36 @@ def alchemyencoder(obj):
|
|||
return float(obj)
|
||||
|
||||
|
||||
def stations2_filtered_pl():
|
||||
def stations2_filtered_pl(start, end):
|
||||
last_10_minutes = datetime.utcnow() - timedelta(minutes=10)
|
||||
|
||||
query = db.session.query(
|
||||
Receiver.name.label('s'),
|
||||
label('lt', func.round(func.ST_Y(Receiver.location_wkt) * 10000) / 10000),
|
||||
label('lg', func.round(func.ST_X(Receiver.location_wkt) * 10000) / 10000),
|
||||
case([(Receiver.lastseen > last_10_minutes, "U")],
|
||||
else_="D").label('u'),
|
||||
Receiver.lastseen.label('ut'),
|
||||
label('v', Receiver.version + '.' + Receiver.platform)) \
|
||||
.order_by(Receiver.lastseen)
|
||||
Receiver.name.label('s'),
|
||||
label('lt', func.round(func.ST_Y(Receiver.location_wkt) * 10000) / 10000),
|
||||
label('lg', func.round(func.ST_X(Receiver.location_wkt) * 10000) / 10000),
|
||||
case([(Receiver.lastseen > last_10_minutes, "U")],
|
||||
else_="D").label('u'),
|
||||
Receiver.lastseen.label('ut'),
|
||||
label('v', Receiver.version + '.' + Receiver.platform)) \
|
||||
.order_by(Receiver.lastseen) \
|
||||
.filter(db.or_(db.and_(start < Receiver.firstseen, end > Receiver.firstseen),
|
||||
db.and_(start < Receiver.lastseen, end > Receiver.lastseen)))
|
||||
|
||||
res = db.session.execute(query)
|
||||
stations = json.dumps({'stations': [dict(r) for r in res]}, default=alchemyencoder)
|
||||
|
||||
return stations
|
||||
|
||||
|
||||
def max_tile_mgrs_pl(station, start, end, squares):
|
||||
query = db.session.query(
|
||||
func.right(ReceiverCoverage.location_mgrs_short, 4),
|
||||
func.count(ReceiverCoverage.location_mgrs_short)
|
||||
) \
|
||||
.filter(db.and_(Receiver.id == ReceiverCoverage.receiver_id, Receiver.name == station)) \
|
||||
.filter(ReceiverCoverage.location_mgrs_short.like(squares + '%')) \
|
||||
.group_by(func.right(ReceiverCoverage.location_mgrs_short, 4))
|
||||
|
||||
res = {'t': squares,
|
||||
'p': ['{}/{}'.format(r[0], r[1]) for r in query.all()]}
|
||||
return json.dumps(res)
|
Plik binarny nie jest wyświetlany.
Po Szerokość: | Wysokość: | Rozmiar: 5.3 KiB |
|
@ -0,0 +1,20 @@
|
|||
Bootstrap Color Picker Sliders
|
||||
|
||||
A Bootstrap 3 optimized advanced responsive color selector with color swatches and support for human perceived lightness. Works in all modern browsers and on touch devices.
|
||||
|
||||
- https://github.com/istvan-meszaros/bootstrap-colorpickersliders
|
||||
- http://www.virtuosoft.eu/code/bootstrap-colorpickersliders/
|
||||
|
||||
Copyright 2013 István Ujj-Mészáros
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
|
@ -0,0 +1,144 @@
|
|||
.cp-container {
|
||||
width: auto;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.cp-container .cp-transparency,
|
||||
.cp-container .cp-swatches .cp-swatch {
|
||||
background:url(data:image/gif;base64,R0lGODlhBgAGAIABAMPDw////yH5BAEKAAEALAAAAAAGAAYAAAIKhIMGGMrs0pGgAAA7) repeat;
|
||||
}
|
||||
|
||||
.cp-container .cp-slider,
|
||||
.cp-container .cp-preview {
|
||||
width: auto;
|
||||
height: 26px;
|
||||
border: 1px solid #dadada;
|
||||
margin: 0 4px;
|
||||
margin-bottom: 5pt;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.cp-container .cp-slider {
|
||||
cursor: ew-resize;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.cp-container .cp-preview {
|
||||
height: 39px;
|
||||
}
|
||||
|
||||
.cp-container .cp-slider span,
|
||||
.cp-container .cp-preview input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
font-family: sans-serif;
|
||||
line-height: 26px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.cp-container .cp-preview input {
|
||||
line-height: 39px;
|
||||
padding: 0;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.cp-container .cp-marker {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 11px;
|
||||
height: 10px;
|
||||
margin-left: -5px;
|
||||
top: -2px;
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAMAAABVLlSxAAAAtFBMVEUAAAB1dXVsbGxwcHBqampoaGj+/v7r6+vw8PBmZmb6+vp/f39kZGTh4eGxsbHT09NlZWXX19dgYGBfX1/W1tb7+/vx8fHZ2dna2tqBgYFnZ2fg4ODe3t7j4+Pi4uL39/eampro6Ojl5eXy8vLt7e3p6emgoKCmpqatra1ra2v19fXm5ubz8/Pv7+9hYWFjY2N5eXlvb29zc3Ps7Ozd3d2ysrKCgoKDg4P9/f2zs7P4+Pj///83Bdu/AAAAAXRSTlMAQObYZgAAAGlJREFUeNo9ylcbgWAAhuG3ZZQoEkkhDe099P3//9Xg6j56Dh6cjkm623IsnTNIDsqlEsN1HBVI5evdDMtN0FOQKt14lWrArzLcnpZjvx9nshcA342jviO1hlHuTdlgVnzIt8VfNubiNwzmhAsoIAkssgAAAABJRU5ErkJggg==);
|
||||
}
|
||||
|
||||
.cp-container.cp-unconvertible-cie-color .cp-slider.cp-cielightness .cp-marker,
|
||||
.cp-container.cp-unconvertible-cie-color .cp-slider.cp-ciechroma .cp-marker,
|
||||
.cp-container.cp-unconvertible-cie-color .cp-slider.cp-ciehue .cp-marker {
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAMAAABVLlSxAAAAvVBMVEUAAADLy8u8vLzZ2dlgYGDe3t5sbGxoaGjr6+vb29tqamrCwsJmZmZkZGTW1tbExMTw8PDj4+Pa2tpfX1/z8/PU1NTp6enX19fg4OBwcHDc3NzJycn4+Pj7+/v6+vrd3d3h4eH+/v6lpaWmpqaampqgoKBlZWWzs7Orq6tra2uxsbGBgYF1dXVnZ2d/f3/T09PMzMxhYWFjY2P19fXv7+9vb2/l5eVzc3P////n5+eysrKDg4OCgoJ5eXnoAACu7h75AAAAAXRSTlMAQObYZgAAAG1JREFUeNo1ytsaQkAAReGdkpRKRUTofEANBoOY93+sfMp/tS4WNqM4mSvSdJztEJPFkETyJNUYEmMtzvhApfsQN89cCXxJ3WOD1yXfRlx3Cv8NPK+pZedFFaCVnQ+8rGp02Kl8fPDX3Nvs/YYvAnQLhiGwga0AAAAASUVORK5CYII=);
|
||||
}
|
||||
|
||||
.cp-container .cp-swatches ul {
|
||||
width: auto;
|
||||
margin: -7px -7px 0 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.cp-container .cp-swatches li {
|
||||
float: left;
|
||||
margin: 7px 7px 0 0;
|
||||
}
|
||||
|
||||
.cp-swatches button {
|
||||
float: left;
|
||||
margin: 7px 7px 0 0;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cp-container .cp-swatch {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
border-color: #ccc;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.cp-container .cp-swatch span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.cp-container .cp-swatches .cp-swatch.actual {
|
||||
border-color: #777;
|
||||
-webkit-box-shadow: 0 0 5px rgb(250, 128, 0);
|
||||
-moz-box-shadow: 0 0 5px rgb(250, 128, 0);
|
||||
box-shadow: 0 0 5px rgb(250, 128, 0);
|
||||
}
|
||||
|
||||
.cp-popover-container .popover {
|
||||
max-width: 1000px;
|
||||
}
|
||||
|
||||
.popover-content .cp-container {
|
||||
width: 263px;
|
||||
}
|
||||
|
||||
.popover-content .cp-container.cp-container-sm {
|
||||
width: 208px;
|
||||
}
|
||||
|
||||
.popover-content .cp-container.cp-container-sm .cp-swatch,
|
||||
.popover-content .cp-container.cp-container-sm .cp-swatches button {
|
||||
width: 23px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
.popover-content .cp-container.cp-container-lg {
|
||||
width: 369px;
|
||||
}
|
||||
|
||||
.popover-content .cp-container.cp-container-lg .cp-swatch,
|
||||
.popover-content .cp-container.cp-container-lg .cp-swatches button {
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
}
|
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
Plik diff jest za duży
Load Diff
|
@ -0,0 +1,960 @@
|
|||
|
||||
var map;
|
||||
var map_id = 0;
|
||||
var position;
|
||||
var lastPosition;
|
||||
var stations = {};
|
||||
var station_markers = {};
|
||||
var labelled_markers = {};
|
||||
var timeoutId;
|
||||
var coverageOverlay;
|
||||
var start = '2018-01-01';
|
||||
var end = '2100-01-01';
|
||||
var lasthash = '';
|
||||
var maxTimeout, minTimeout;
|
||||
var processingUrl = true;
|
||||
var defaultPointer = 'crosshair'; // help, cursor, crosshair, text, wait, pointer, progress
|
||||
//var defaultColor = '#00990000:#009900ff';
|
||||
var defaultColour = '#80000040:#008000ff';
|
||||
var minZoomLevel = 8;
|
||||
var maxZoomLevel = 11;
|
||||
|
||||
var selected = { 'what':'max', 'when':'lastweek', 'station':'', 'center':'', 'zoom':'', 'airspace':'', 'airports':'', 'circles':'', 'ambiguity':'', 'colour': defaultColour };
|
||||
|
||||
function initialize() {
|
||||
|
||||
airspaceLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
tileUrlFunction: function(tileCoord, projection) {
|
||||
var z = tileCoord[0];
|
||||
var x = tileCoord[1];
|
||||
var y = (1 << z) + tileCoord[2];
|
||||
return 'http://1.tile.maps.openaip.net/geowebcache/service/tms/1.0.0/openaip_approved_airspaces_geometries@png/' + z + '/' + x + '/' + y + '.png';
|
||||
},
|
||||
wrapx: false
|
||||
}),
|
||||
opacity: 1,
|
||||
visible: false
|
||||
});
|
||||
|
||||
airportsLayer = new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
tileUrlFunction: function(tileCoord, projection) {
|
||||
var z = tileCoord[0];
|
||||
var x = tileCoord[1];
|
||||
var y = (1 << z) + tileCoord[2];
|
||||
return 'http://1.tile.maps.openaip.net/geowebcache/service/tms/1.0.0/openaip_approved_airports@png/' + z + '/' + x + '/' + y + '.png';
|
||||
},
|
||||
wrapx: false
|
||||
}),
|
||||
opacity: 1,
|
||||
visible: false
|
||||
});
|
||||
|
||||
OSMLayer = new ol.layer.Tile({
|
||||
source: new ol.source.OSM(),
|
||||
visible: true
|
||||
});
|
||||
|
||||
stationLayerSource = new ol.source.Vector({
|
||||
features: []
|
||||
});
|
||||
|
||||
stationLayer = new ol.layer.Vector({
|
||||
source: stationLayerSource,
|
||||
zIndex: 50,
|
||||
opacity: 1,
|
||||
visible: true
|
||||
});
|
||||
|
||||
circleLayerSource = new ol.source.Vector({
|
||||
features: []
|
||||
});
|
||||
|
||||
circleLayer = new ol.layer.Vector({
|
||||
source: circleLayerSource,
|
||||
zIndex: 50,
|
||||
opacity: 1,
|
||||
visible: false
|
||||
});
|
||||
|
||||
AmbiguitySquareLayerSource = new ol.source.Vector({
|
||||
features: []
|
||||
});
|
||||
|
||||
AmbiguitySquareLayer = new ol.layer.Vector({
|
||||
source: AmbiguitySquareLayerSource,
|
||||
zIndex: 51,
|
||||
opacity: 1,
|
||||
visible: true
|
||||
});
|
||||
|
||||
ambiguityOverlay = new AmbiguityMapType();
|
||||
|
||||
// ratio: 1.2, +/-10% instead of default +/-25%
|
||||
ambiguityLayer = new ol.layer.Image({
|
||||
source: new ol.source.ImageCanvas({
|
||||
canvasFunction: ambiguityOverlay.canvasFunctionAmbiguity,
|
||||
projection: "EPSG:3857",
|
||||
ratio: 1.2,
|
||||
visible: false
|
||||
})
|
||||
});
|
||||
|
||||
// create overlay before reading hash
|
||||
coverageOverlay = new CoverageMapType();
|
||||
|
||||
// ratio: 1.2, +/-10% instead of default +/-25%
|
||||
coverageLayer = new ol.layer.Image({
|
||||
source: new ol.source.ImageCanvas({
|
||||
canvasFunction: coverageOverlay.canvasFunctionCoverage,
|
||||
projection: "EPSG:3857",
|
||||
ratio: 1.2
|
||||
})
|
||||
});
|
||||
|
||||
// read hash before creating map with intial settings
|
||||
if ( location.hash.substr(1) != '' ) {
|
||||
readhash();
|
||||
} else {
|
||||
setOptions('circles', 'circles'); // show by default
|
||||
setColour( defaultColour );
|
||||
processingUrl = false;
|
||||
}
|
||||
// re-readhash if user changed hash and then redraw
|
||||
$(window).bind( 'hashchange', reReadhash );
|
||||
|
||||
attribution = new ol.control.Attribution({
|
||||
collapsed: true,
|
||||
tipLabel: 'Show credits'
|
||||
});
|
||||
|
||||
scaleLineControl = new ol.control.ScaleLine();
|
||||
|
||||
map = new ol.Map({
|
||||
layers: [
|
||||
OSMLayer,
|
||||
circleLayer,
|
||||
airportsLayer,
|
||||
airspaceLayer,
|
||||
coverageLayer,
|
||||
ambiguityLayer,
|
||||
stationLayer,
|
||||
],
|
||||
target: 'map_canvas',
|
||||
controls: ol.control.defaults({
|
||||
attributionOptions: {
|
||||
collapsible: true
|
||||
}
|
||||
}).extend([
|
||||
scaleLineControl,
|
||||
attribution
|
||||
]),
|
||||
view: new ol.View({
|
||||
projection: 'EPSG:3857',
|
||||
center: ol.proj.fromLonLat([dLon,dLat]),
|
||||
zoom: dZoom
|
||||
}),
|
||||
});
|
||||
|
||||
myresize();
|
||||
|
||||
$(window).resize(function () {
|
||||
myresize();
|
||||
});
|
||||
|
||||
// displays credits over the list
|
||||
credits = document.getElementsByClassName('ol-attribution ol-unselectable ol-control')[0];
|
||||
credits.style.zIndex = "1000";
|
||||
|
||||
var tooltip = document.getElementById('tooltip');
|
||||
var overlay = new ol.Overlay({
|
||||
element: tooltip,
|
||||
offset: [10, 0],
|
||||
positioning: 'bottom-left'
|
||||
});
|
||||
map.addOverlay(overlay);
|
||||
|
||||
function genericDisplayTooltip(evt) {
|
||||
var pixel = evt.pixel;
|
||||
var feature = map.forEachFeatureAtPixel(pixel, function(feature) {
|
||||
return feature;
|
||||
});
|
||||
tooltip.style.display = feature ? '' : 'none';
|
||||
if (feature) {
|
||||
overlay.setPosition(evt.coordinate);
|
||||
tooltip.innerHTML = feature.get('info');
|
||||
}
|
||||
};
|
||||
|
||||
// marker tooltip
|
||||
function displayTooltip(event) {
|
||||
|
||||
var hit = map.hasFeatureAtPixel(event.pixel);
|
||||
|
||||
var pixel = event.pixel;
|
||||
tooltip.style.display = 'none';
|
||||
map.getTargetElement().style.cursor = defaultPointer;
|
||||
map.forEachFeatureAtPixel(pixel, function(feature) {
|
||||
if (typeof(feature.get('mark'))!='undefined'){
|
||||
if ( feature.get('mark').substring(0,1) == "S" ) {
|
||||
overlay.setPosition(event.coordinate);
|
||||
tooltip.innerText = feature.get('info');
|
||||
tooltip.style.display = '';
|
||||
// also change cursor
|
||||
map.getTargetElement().style.cursor = 'pointer';
|
||||
return;
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
map.on('pointermove', displayTooltip);
|
||||
|
||||
map.on('singleclick', function(event) {
|
||||
var stationName = '';
|
||||
map.forEachFeatureAtPixel(event.pixel, function(feature,layer) {
|
||||
if (typeof(feature.get('mark'))!='undefined'){
|
||||
if ( feature.get('mark').substring(0,1) == "S" ) {
|
||||
stationName = feature.get('name');
|
||||
}
|
||||
}
|
||||
});
|
||||
if (stationName != '') {
|
||||
getStationData( stationName );
|
||||
} else { // no 'S' feature
|
||||
// map click - display details
|
||||
updateDetails('<b><font color="red">loading...</font></b>');
|
||||
if ( timeoutId ) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
position = ol.proj.transform(event.coordinate, 'EPSG:3857', 'EPSG:4326');;
|
||||
timeoutId = setTimeout( displayDetails, 500 );
|
||||
}
|
||||
});
|
||||
|
||||
map.on('moveend', function(event) {
|
||||
var zoom = map.getView().getZoom();
|
||||
if (zoom != dZoom) {
|
||||
setZoom(zoom);
|
||||
updateURL( 'zoom' );
|
||||
}
|
||||
var nc = ol.proj.toLonLat( map.getView().getCenter() );
|
||||
if ( (dLon != round(nc[0],6)) || (dLat != round(nc[1],6)) ) {
|
||||
setCentre( nc );
|
||||
updateURL( 'center' );
|
||||
}
|
||||
});
|
||||
|
||||
// display the stations
|
||||
displayStations( true );
|
||||
|
||||
updateDetails("Blue icons represent UP old stations, Mauve ones DOWN<br/>Green is 0.2.1 or above and UP, Red is DOWN new stations");
|
||||
|
||||
timeoutId = null;
|
||||
};
|
||||
|
||||
function myresize() {
|
||||
var h = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
|
||||
var t = $('#top_row').height();
|
||||
var b = $('#bottom_row').height();
|
||||
$('#map_canvas').height((h - t - b) + 'px');
|
||||
map.updateSize();
|
||||
}
|
||||
|
||||
function addLabel( location, labelText ) {
|
||||
var marker = labelled_markers[location.s] =
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([location.lg,location.lt])),
|
||||
mark: "L"
|
||||
});
|
||||
|
||||
marker.setStyle(new ol.style.Style({
|
||||
text: new ol.style.Text({
|
||||
font: '12px Verdana',
|
||||
text: labelText,
|
||||
offsetX: 0,
|
||||
offsetY: -22,
|
||||
fill: new ol.style.Fill({color: 'black'}),
|
||||
stroke: new ol.style.Stroke({color: 'black', width: 0.5})
|
||||
})
|
||||
}));
|
||||
stationLayerSource.addFeature(marker);
|
||||
}
|
||||
|
||||
// custom marker
|
||||
var angleMarker = 30;
|
||||
var radiusMarker = 11;
|
||||
var sinMarker = Math.sin(degreesToRadians(angleMarker));
|
||||
var cosMarker = Math.cos(degreesToRadians(angleMarker));
|
||||
var offsetMarker = radiusMarker/sinMarker;
|
||||
var styleCache = {};
|
||||
var styleFunction = function(colour) {
|
||||
colour = '#'+colour;
|
||||
var style = styleCache[colour];
|
||||
if (!style) {
|
||||
canvasM = document.createElement("canvas");
|
||||
var canvasWidth = radiusMarker*2+3, canvasHeight = radiusMarker+offsetMarker+3;
|
||||
canvasM.setAttribute("width", canvasWidth);
|
||||
canvasM.setAttribute("height", canvasHeight);
|
||||
var contextM = canvasM.getContext("2d");
|
||||
// erase the canvas before re-drawing
|
||||
contextM.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
// Draw
|
||||
contextM.save();
|
||||
contextM.beginPath();
|
||||
var x1=radiusMarker*cosMarker, y2=radiusMarker+offsetMarker;
|
||||
var y1=offsetMarker-radiusMarker*sinMarker, x2=x1*y2/y1;
|
||||
contextM.moveTo(radiusMarker+1, canvasHeight-0);
|
||||
contextM.lineTo(radiusMarker+1-x1, canvasHeight-y1);
|
||||
contextM.arcTo(radiusMarker+1-x2, canvasHeight-y2, radiusMarker+1, canvasHeight-y2, radiusMarker);
|
||||
contextM.arcTo(radiusMarker+1+x2, canvasHeight-y2, radiusMarker+1+x1, canvasHeight-y1, radiusMarker);
|
||||
contextM.closePath();
|
||||
contextM.fillStyle = colour;
|
||||
contextM.fill();
|
||||
contextM.lineWidth = 1;
|
||||
contextM.strokeStyle = '#000000';
|
||||
contextM.stroke();
|
||||
contextM.restore();
|
||||
|
||||
style = new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
img: canvasM,
|
||||
imgSize: [canvasWidth, canvasHeight],
|
||||
anchor: [0.5, 1.0],
|
||||
})
|
||||
});
|
||||
styleCache[colour] = style;
|
||||
}
|
||||
return style;
|
||||
};
|
||||
|
||||
function addStation( location, colour ) {
|
||||
var marker = station_markers[location.s] =
|
||||
new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([location.lg,location.lt])),
|
||||
lat: location.lt,
|
||||
lon: location.lg,
|
||||
info: location.s + "\n" + (location.u == "U" ? "Last heartbeat at:\n" : "Last point at ") + location.ut + "Z\n" + "Version " + location.v,
|
||||
mark: "S",
|
||||
name: location.s,
|
||||
});
|
||||
|
||||
marker.setStyle(styleFunction(colour)); // custom
|
||||
/* marker.setStyle(new ol.style.Style({
|
||||
image: new ol.style.Icon({
|
||||
anchor: [0.5, 1],
|
||||
opacity: 1,
|
||||
src: "//www.googlemapsmarkers.com/v1/" + colour + "/",
|
||||
})
|
||||
})); */
|
||||
stationLayerSource.addFeature(marker);
|
||||
}
|
||||
|
||||
function addCircle( location, radius, colour ) {
|
||||
var rangeCircle =
|
||||
new ol.Feature({
|
||||
// geometry: new ol.geom.Circle(ol.proj.fromLonLat([location.lg, location.lt]), radius / Math.cos( degreesToRadians(location.lt)))
|
||||
geometry: new ol.geom.Polygon.circular(
|
||||
// WGS84 Sphere
|
||||
new ol.Sphere(6378137),
|
||||
[location.lg, location.lt],
|
||||
radius,
|
||||
// Number of verticies
|
||||
64).transform('EPSG:4326', 'EPSG:3857')
|
||||
});
|
||||
|
||||
rangeCircle.setStyle(new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: colour,
|
||||
width: 1
|
||||
}),
|
||||
}));
|
||||
circleLayerSource.addFeature(rangeCircle);
|
||||
}
|
||||
|
||||
var dsTimeOut = null;
|
||||
function displayStations(first) {
|
||||
// Ajax is async and timeout runs twice unless cleared until AJAX done
|
||||
// so need a clearTimeOut
|
||||
if (dsTimeOut) {
|
||||
clearTimeout(dsTimeOut);
|
||||
}
|
||||
|
||||
$.ajax( { type: "GET",
|
||||
url: OgR+"/perl/stations2-filtered.pl?start="+start+"&end="+end,
|
||||
timeout:20000,
|
||||
cache: true,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
},
|
||||
success: function(json) {
|
||||
var checked = selected['circles'] == 'circles';
|
||||
|
||||
stationLayerSource.clear()
|
||||
station_markers = {};
|
||||
stations = [];
|
||||
|
||||
circleLayerSource.clear()
|
||||
|
||||
// add markers
|
||||
json.stations.forEach( function(entry) {
|
||||
stations[entry.s] = entry;
|
||||
|
||||
var old = (entry.v == 'old' || entry.v == '?' || entry.v == "undefined" || entry.v == "" || entry.v == '0.1.3' );
|
||||
var colour = '00ff00';
|
||||
if ( entry.u == "U" ) {
|
||||
if ( old ) {
|
||||
colour = '0000ff';
|
||||
}
|
||||
} else {
|
||||
if ( old ) {
|
||||
colour = 'aa00aa';
|
||||
} else {
|
||||
colour = 'aa0000';
|
||||
}
|
||||
}
|
||||
addStation( entry, colour );
|
||||
|
||||
addCircle( entry, 10000, 'rgba(48, 48, 48, 0.7)' );
|
||||
addCircle( entry, 20000, 'rgba(48, 48, 48, 0.5)' );
|
||||
addCircle( entry, 30000, 'rgba(48, 48, 48, 0.3)' );
|
||||
} );
|
||||
|
||||
if ( first ) {
|
||||
setupSearch();
|
||||
var stationName = selected['station'];
|
||||
stationName = matchStation(stationName);
|
||||
if ( stationName != "" && stations[stationName] ) {
|
||||
setStation(stationName); // as per match
|
||||
setCentre( [ stations[stationName].lg, stations[stationName].lt ] );
|
||||
map.getView().setCenter(ol.proj.fromLonLat([dLon, dLat]));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// and update every 5 minutes
|
||||
dsTimeOut = setTimeout( displayStations, 5*60*1000 );
|
||||
}
|
||||
|
||||
function degreesToRadians(deg) {
|
||||
return deg * (Math.PI / 180);
|
||||
}
|
||||
|
||||
function radiansToDegrees(rad) {
|
||||
return rad / (Math.PI / 180);
|
||||
}
|
||||
|
||||
function round(value, decimals) {
|
||||
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
|
||||
}
|
||||
|
||||
const DEFAULT_RADIUS = 6371008.8;
|
||||
function getDistance(c1, c2, opt_radius) {
|
||||
const radius = opt_radius || DEFAULT_RADIUS;
|
||||
const lat1 = degreesToRadians(c1[1]);
|
||||
const lat2 = degreesToRadians(c2[1]);
|
||||
const deltaLatBy2 = (lat2 - lat1) / 2;
|
||||
const deltaLonBy2 = degreesToRadians(c2[0] - c1[0]) / 2;
|
||||
const a = Math.sin(deltaLatBy2) * Math.sin(deltaLatBy2) +
|
||||
Math.sin(deltaLonBy2) * Math.sin(deltaLonBy2) *
|
||||
Math.cos(lat1) * Math.cos(lat2);
|
||||
return 2 * radius * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
}
|
||||
|
||||
function clearLabels() {
|
||||
// remove old labels
|
||||
// use Object.keys for array with keys instead of numeric indexes
|
||||
Object.keys(labelled_markers).forEach(function(key, index) {
|
||||
stationLayerSource.removeFeature(labelled_markers[key]);
|
||||
});
|
||||
labelled_markers = [];
|
||||
}
|
||||
|
||||
function getStationData( stationName ) {
|
||||
clearLabels();
|
||||
updateDetails('');
|
||||
adjustMap( null, null, stationName );
|
||||
};
|
||||
|
||||
function updateDetails(newDetails) {
|
||||
if (newDetails != '') {
|
||||
$('#details').show();
|
||||
} else {
|
||||
$('#details').hide();
|
||||
}
|
||||
$('#details').html(newDetails);
|
||||
myresize(); // adjust, details DIV may change size
|
||||
}
|
||||
|
||||
function displayDetails() {
|
||||
if ( position && position != lastPosition ) {
|
||||
$.ajax( { type: "GET",
|
||||
url: OgR+"/perl/details-mgrs.pl",
|
||||
data: { start: start, end: end, position: forward([position[0],position[1]],2) },
|
||||
timeout:20000,
|
||||
cache: true,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
},
|
||||
success: function(json) {
|
||||
console.log( json.toString() );
|
||||
var pos = inverse( json.query.ref );
|
||||
var bounds = ol.extent.boundingExtent([[pos[0],pos[1]],[pos[2],pos[3]]])
|
||||
var point = ol.extent.getCenter(bounds);
|
||||
clearLabels();
|
||||
var stationList = "";
|
||||
var id = 1;
|
||||
var currentFilter = selected['station'];
|
||||
json.position.forEach( function(station) {
|
||||
var distance = stations[station.s] ? Math.round(
|
||||
getDistance([stations[station.s].lg, stations[station.s].lt],point)
|
||||
/100)/10 : '?';
|
||||
if ( currentFilter != '' && station.s != currentFilter ) {
|
||||
stationList += "<i class='subtle'>";
|
||||
}
|
||||
stationList += "<b>"+id+":"+station.s+"</b>: "+distance+"km, "+station.l+"-"+station.h+"m, Avg Max:"+(station.a/10)+"db, Samples:"+station.c + ", Gliders:"+station.g+ ' ('+station.first;
|
||||
|
||||
if ( station.first != station.last ) {
|
||||
stationList += " to " + station.last;
|
||||
}
|
||||
stationList += ')<br/>';
|
||||
|
||||
if ( currentFilter != '' && station.s != currentFilter ) {
|
||||
stationList += "</i>";
|
||||
}
|
||||
if ( station_markers[station.s] ) {
|
||||
addLabel( stations[station.s], id.toString() );
|
||||
}
|
||||
id++;
|
||||
});
|
||||
console.log( stationList );
|
||||
updateDetails(stationList);
|
||||
},
|
||||
});
|
||||
lastPosition = position;
|
||||
}
|
||||
timeoutId = null;
|
||||
}
|
||||
|
||||
var substringMatcher = function(strs) {
|
||||
return function findMatches(q, cb) {
|
||||
var matches, substringRegex;
|
||||
|
||||
matches = [];
|
||||
substrRegex = new RegExp(q, 'i');
|
||||
for( var key in stations ) {
|
||||
if (substrRegex.test(key)) {
|
||||
matches.push({ value: key });
|
||||
}
|
||||
}
|
||||
cb(matches.sort(function(a,b){ return a.value < b.value ? -1 : (a.value == b.value ? 0 : 1); }));
|
||||
cb(matches);
|
||||
};
|
||||
};
|
||||
|
||||
function setupSearch() {
|
||||
$('.stationlist #typeahead').typeahead({
|
||||
hint: true,
|
||||
highlight: true,
|
||||
minLength: 1
|
||||
},
|
||||
{
|
||||
name: 'stations',
|
||||
displayKey: 'value',
|
||||
source: substringMatcher('')
|
||||
});
|
||||
}
|
||||
|
||||
function toggleOptions(option) {
|
||||
var newOption = selected[option] ? '' : option;
|
||||
setOptions(option, newOption);
|
||||
updateURL(option);
|
||||
return false;
|
||||
}
|
||||
|
||||
function setOptions(option, newOption) {
|
||||
switch(option) {
|
||||
case 'circles':
|
||||
circleLayer.setVisible(newOption);
|
||||
break;
|
||||
case 'ambiguity':
|
||||
ambiguityLayer.setVisible(newOption);
|
||||
break;
|
||||
case 'airports':
|
||||
airportsLayer.setVisible(newOption);
|
||||
break;
|
||||
case 'airspace':
|
||||
airspaceLayer.setVisible(newOption);
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
tick( option, newOption );
|
||||
}
|
||||
|
||||
function setMinColour(c,a) {
|
||||
var s = selected['colour'].split(':');
|
||||
if ( s[0] != c ) {
|
||||
s[0] = c+pad2(a.toString(16));
|
||||
adjustMap( null, null, null, s[0]+':'+s[1] );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setMaxColour(c,a) {
|
||||
var s = selected['colour'].split(':');
|
||||
if ( s[1] != c ) {
|
||||
s[1] = c+pad2(a.toString(16));
|
||||
adjustMap( null, null, null, s[0]+':'+s[1] );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setZoom(mapZoom) {
|
||||
console.log( "Zoom level: "+mapZoom );
|
||||
dZoom = mapZoom;
|
||||
selected['zoom'] = mapZoom;
|
||||
if (mapZoom > maxZoomLevel) {
|
||||
$('#zoomOut_msg').show();
|
||||
}
|
||||
else {
|
||||
$('#zoomOut_msg').hide();
|
||||
}
|
||||
if (mapZoom < minZoomLevel) {
|
||||
$('#zoomIn_msg').show();
|
||||
}
|
||||
else {
|
||||
$('#zoomIn_msg').hide();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function setCentre(coords) {
|
||||
dLon = coords[0];
|
||||
dLat = coords[1];
|
||||
selected['center'] = coords[1].toFixed(5) + "_" + coords[0].toFixed(5);
|
||||
}
|
||||
|
||||
function setColour(colour) {
|
||||
selected['colour'] = colour;
|
||||
coverageOverlay.setColourScheme( colour );
|
||||
}
|
||||
|
||||
function setStation(where) {
|
||||
selected['station'] = where;
|
||||
coverageOverlay.setStation(where);
|
||||
$('#typeahead').val( where );
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
function setSource(what) {
|
||||
tick( 'what', what );
|
||||
coverageOverlay.setSource(what);
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
function setDates(when) {
|
||||
switch(when) {
|
||||
case 'today':
|
||||
start = end = new Date().toISOString().substr(0,10);
|
||||
break;
|
||||
case 'yesterday':
|
||||
start = new Date(new Date().getTime() - (24*3600*2*1000)).toISOString().substr(0,10);
|
||||
end = new Date(new Date().getTime() - (24*3600*1*1000)).toISOString().substr(0,10);
|
||||
break;
|
||||
case 'lastweek':
|
||||
start = new Date(new Date().getTime() - (24*3600*7*1000)).toISOString().substr(0,10);
|
||||
end = new Date().toISOString().substr(0,10);
|
||||
break;
|
||||
case 'recent':
|
||||
start = new Date(new Date().getFullYear(),0,1).toISOString().substr(0,10);
|
||||
end = new Date().toISOString().substr(0,10);
|
||||
break;
|
||||
case 'all':
|
||||
start = '2015-03-31';
|
||||
end = new Date().toISOString().substr(0,10);
|
||||
break;
|
||||
default: {
|
||||
switch(when.substr(0,1)) {
|
||||
case 'd':
|
||||
var ndays = parseInt(when.substr(1,10));
|
||||
start = new Date(new Date().getTime() - (24*3600*ndays*1000)).toISOString().substr(0,10);
|
||||
end = new Date().toISOString().substr(0,10);
|
||||
break;
|
||||
case 'D':
|
||||
var s = when.substr(1,21).split('#');
|
||||
start = s[0];
|
||||
end = s[1];
|
||||
break;
|
||||
default: {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tick( 'when', when );
|
||||
coverageOverlay.setDates( start, end );
|
||||
updateTitle();
|
||||
}
|
||||
|
||||
function setWhen(when) {
|
||||
adjustMap(null, when);
|
||||
return false;
|
||||
}
|
||||
|
||||
function setWhat(what) {
|
||||
adjustMap(what);
|
||||
return false;
|
||||
}
|
||||
|
||||
function matchStation(where) {
|
||||
if (where != '') { // match in list
|
||||
for( var key in stations ) {
|
||||
if (where.toUpperCase() == stations[key].s.toUpperCase()) {
|
||||
where = stations[key].s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return where;
|
||||
}
|
||||
|
||||
var nostation = { 'receivers':1, 'coverage':1 };
|
||||
function adjustMap( what, when, where, colour ) {
|
||||
|
||||
if ( what ) {
|
||||
setSource(what);
|
||||
if ( nostation[what] ) {
|
||||
where = '';
|
||||
}
|
||||
updateURL(what);
|
||||
}
|
||||
if ( when ) {
|
||||
setDates(when);
|
||||
updateURL(when);
|
||||
}
|
||||
if ( where != null && where != undefined ) {
|
||||
if (where == '') { // i.e all stations
|
||||
setStation(where);
|
||||
updateURL('station');
|
||||
} else {
|
||||
if (where.includes('%')) { // i.e contains wildcard
|
||||
setStation(where);
|
||||
updateURL('station');
|
||||
} else {
|
||||
where = matchStation(where);
|
||||
var LngLat;
|
||||
if ( stations[ where ] ) {
|
||||
LngLat = [stations[where].lg,stations[where].lt];
|
||||
if (map.getView().getZoom() < minZoomLevel ) {
|
||||
setZoom(minZoomLevel);
|
||||
map.getView().setZoom(dZoom);
|
||||
}
|
||||
setCentre( LngLat );
|
||||
map.getView().setCenter(ol.proj.fromLonLat([dLon, dLat]));
|
||||
|
||||
setStation(where);
|
||||
updateURL('station');
|
||||
} else {
|
||||
$('#typeahead').val('');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( colour != null && colour != '' ) {
|
||||
setColour(colour);
|
||||
updateURL('colour');
|
||||
}
|
||||
// update the coverage layer
|
||||
coverageLayer.getSource().changed();
|
||||
}
|
||||
|
||||
var hrefOrder = [ 'station', 'what', 'when', 'center', 'zoom', 'colour' ];
|
||||
var updateHistory = { 'what':1, 'when':1, 'station':1 };
|
||||
var titleOrder = [ 'what', 'station', 'when' ];
|
||||
var options = [ 'airports', 'airspace', 'circles', 'ambiguity' ];
|
||||
|
||||
function tick( whattype, newItem ) {
|
||||
// clear current tick
|
||||
if ( selected[whattype] && selected[whattype] !== '' && whattype !== 'station' ) {
|
||||
$('#'+selected[whattype]+' span').attr('class','');
|
||||
}
|
||||
selected[whattype] = newItem;
|
||||
// set new tick
|
||||
if ( selected[whattype] && selected[whattype] !== '' && whattype !== 'station' ) {
|
||||
$('#'+selected[whattype]+' span').attr('class','glyphicon glyphicon-ok');
|
||||
}
|
||||
}
|
||||
|
||||
function updateTitle() {
|
||||
// update the title to reflect the contents
|
||||
var title = '';
|
||||
for( var i = 0; i < titleOrder.length; i++ ) {
|
||||
var v = selected[titleOrder[i]];
|
||||
if ( v && v != '' ) {
|
||||
if ( titleOrder[i] == 'station' ) {
|
||||
title += ' - ' + v;
|
||||
} else {
|
||||
title += ' - ' + $('#'+v).text();
|
||||
}
|
||||
}
|
||||
}
|
||||
window.document.title = 'Onglide Range' + title;
|
||||
$('#description').html( title.substr(2) );
|
||||
}
|
||||
|
||||
var coloursInitialised = false;
|
||||
function initColour( newColour ) {
|
||||
|
||||
var s = newColour.split(':');
|
||||
|
||||
var startColor = tinycolor( s[1] );
|
||||
var endColor = tinycolor( s[0] );
|
||||
|
||||
if (coloursInitialised) { // update
|
||||
$("#maxc").trigger("colorpickersliders.updateColor", startColor.toRgbString());
|
||||
$("#minc").trigger("colorpickersliders.updateColor", endColor.toRgbString());
|
||||
return;
|
||||
}
|
||||
|
||||
$("#maxc").ColorPickerSliders({
|
||||
color: startColor.toRgbString(),
|
||||
flat: true,
|
||||
size: 'sm',
|
||||
placement: 'left',
|
||||
customswitches: false,
|
||||
order: {
|
||||
hsl: 1,
|
||||
opacity: 2
|
||||
},
|
||||
onchange: function( container, colour ) {
|
||||
if ( maxTimeout ) { clearTimeout( maxTimeout ); }
|
||||
maxTimeout = setTimeout( function() {
|
||||
setMaxColour( colour.tiny.toHexString(), Math.round(colour.rgba.a*255) );
|
||||
}, 500 );
|
||||
}
|
||||
});
|
||||
|
||||
$("#minc").ColorPickerSliders({
|
||||
color: endColor.toRgbString(),
|
||||
flat: true,
|
||||
size: 'sm',
|
||||
placement: 'left',
|
||||
customswitches: false,
|
||||
order: {
|
||||
hsl: 1,
|
||||
opacity: 2
|
||||
},
|
||||
onchange: function( container, colour ) {
|
||||
if ( minTimeout ) { clearTimeout( minTimeout ); }
|
||||
minTimeout = setTimeout( function() {
|
||||
setMinColour( colour.tiny.toHexString(), Math.round(colour.rgba.a*255) );
|
||||
}, 500 );
|
||||
}
|
||||
});
|
||||
coloursInitialised = true;
|
||||
}
|
||||
|
||||
function updateURL( whattype ) {
|
||||
// and if we aren't processing a URL hash at the moment then
|
||||
// update the URL as well
|
||||
if ( ! processingUrl ) {
|
||||
var url = '#';
|
||||
for( var i = 0; i < hrefOrder.length; i++, url += ',' ) {
|
||||
if ( selected[hrefOrder[i]] ) {
|
||||
url += selected[hrefOrder[i]];
|
||||
}
|
||||
}
|
||||
|
||||
for( i = 0; i < options.length; i++ ) {
|
||||
if ( selected[options[i]] ) {
|
||||
url += options[i] + ";";
|
||||
}
|
||||
}
|
||||
|
||||
// we changed it, this will stop us doing anything with it
|
||||
lasthash = url;
|
||||
// if ( history.pushState && updateHistory[whattype] ) {
|
||||
// history.pushState( null, null, url );
|
||||
// } else {
|
||||
location.replace( url );
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
function reReadhash() {
|
||||
// make sure the hash isn't the same as we think it is
|
||||
// this should stop us parsing our own changes
|
||||
if ( location.hash === lasthash ) {
|
||||
return;
|
||||
}
|
||||
readhash();
|
||||
// update map
|
||||
map.getView().setZoom(dZoom);
|
||||
map.getView().setCenter(ol.proj.fromLonLat([dLon, dLat]));
|
||||
// make sure canvas redrawn if zoom and center stays same !!!
|
||||
// update the coverage layer
|
||||
coverageLayer.getSource().changed();
|
||||
}
|
||||
|
||||
function readhash() {
|
||||
lasthash = location.hash;
|
||||
processingUrl = true;
|
||||
var vals = location.hash.substr(1).split(',');
|
||||
console.log( location.hash.substr(1) + "|%%%%|" + vals[0] );
|
||||
|
||||
// do options first as adjustMap will force the redraw
|
||||
if ( vals[6] ) {
|
||||
var userOptions = vals[6].split(';');
|
||||
var newOption = ~(userOptions.indexOf('airports')) ? 'airports' : '';
|
||||
setOptions('airports', newOption);
|
||||
newOption = ~(userOptions.indexOf('airspace')) ? 'airspace' : '';
|
||||
setOptions('airspace', newOption);
|
||||
newOption = ~(userOptions.indexOf('circles')) ? 'circles' : '';
|
||||
setOptions('circles', newOption);
|
||||
newOption = ~(userOptions.indexOf('ambiguity')) ? 'ambiguity' : '';
|
||||
setOptions('ambiguity', newOption);
|
||||
}
|
||||
|
||||
// set colour
|
||||
if ( vals[5] ) {
|
||||
setColour( vals[5] );
|
||||
} else {
|
||||
setColour( defaultColour );
|
||||
}
|
||||
initColour(selected['colour']);
|
||||
|
||||
// set date
|
||||
if (vals[2]) {
|
||||
setDates(vals[2]);
|
||||
} else {
|
||||
setDates('all');
|
||||
}
|
||||
|
||||
// set graph source
|
||||
if ( vals[1] ) {
|
||||
setSource(vals[1]);
|
||||
}
|
||||
|
||||
// set station
|
||||
if ( vals[0] ) {
|
||||
setStation(vals[0]);
|
||||
}
|
||||
|
||||
// set center (lat_lon)
|
||||
if ( vals[3] ) {
|
||||
var coords = vals[3].split('_');
|
||||
if ( coords.length ) {
|
||||
setCentre( [ parseFloat(coords[1]), parseFloat(coords[0]) ] );
|
||||
}
|
||||
}
|
||||
|
||||
// set zoom
|
||||
if ( vals[4] ) {
|
||||
setZoom(parseInt(vals[4]));
|
||||
}
|
||||
processingUrl = false;
|
||||
}
|
||||
|
||||
// Force a hex value to have 2 characters
|
||||
function pad2(c) {
|
||||
return c.length == 1 ? '0' + c : '' + c;
|
||||
}
|
|
@ -0,0 +1,289 @@
|
|||
|
||||
function CoverageMapType() {
|
||||
this.start = '2014-04-14';
|
||||
this.end = '2200-01-01';
|
||||
this.station = "";
|
||||
this.source = "max";
|
||||
this.colours = [];
|
||||
this.colour = '';
|
||||
}
|
||||
|
||||
var id = 0;
|
||||
var cache = {};
|
||||
var cacheUTM = {};
|
||||
|
||||
CoverageMapType.prototype.setStation = function(newStation) {
|
||||
this.station = newStation;
|
||||
}
|
||||
|
||||
CoverageMapType.prototype.setColourScheme = function(_colour) {
|
||||
this.colour = _colour;
|
||||
var s = _colour.split( ':' );
|
||||
|
||||
var start = tinycolor( s[1] ); start = start.toRgb();
|
||||
var end = tinycolor( s[0] ); end = end.toRgb();
|
||||
|
||||
console.log( _colour );
|
||||
console.log( "start:" );
|
||||
console.log( start );
|
||||
|
||||
for (i = 0; i < 25; i++) {
|
||||
var alphablend = 1-(i/24);
|
||||
var c = { r: start.r * alphablend + (1 - alphablend) * end.r,
|
||||
g: start.g * alphablend + (1 - alphablend) * end.g,
|
||||
b: start.b * alphablend + (1 - alphablend) * end.b,
|
||||
a: start.a * alphablend + (1 - alphablend) * end.a };
|
||||
|
||||
this.colours[i] = tinycolor(c).toRgbString();
|
||||
}
|
||||
}
|
||||
|
||||
CoverageMapType.prototype.setDates = function(_start,_end) {
|
||||
this.start = _start;
|
||||
this.end = _end;
|
||||
}
|
||||
|
||||
CoverageMapType.prototype.setSource = function(_source) {
|
||||
this.source = _source;
|
||||
}
|
||||
|
||||
let canvasC = null;
|
||||
CoverageMapType.prototype.canvasFunctionCoverage = function(
|
||||
extent,
|
||||
resolution,
|
||||
pixelRatio,
|
||||
size, // [canvasWidth, canvasHeight],
|
||||
projection
|
||||
) {
|
||||
if (!canvasC) {
|
||||
canvasC = document.createElement("canvas");
|
||||
canvasC.setAttribute("id", "canvasCoverage");
|
||||
}
|
||||
var canvasWidth = size[0], canvasHeight = size[1];
|
||||
canvasC.setAttribute("width", canvasWidth);
|
||||
canvasC.setAttribute("height", canvasHeight);
|
||||
var contextC = canvasC.getContext("2d");
|
||||
// erase the canvas before re-drawing
|
||||
contextC.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
|
||||
// do nothing if too much out zoom
|
||||
var zoom = map.getView().getZoom();
|
||||
if ( ( zoom < minZoomLevel && this.source != 'lowres-coverage' ) ||
|
||||
( zoom > maxZoomLevel ) ) {
|
||||
return canvasC;
|
||||
}
|
||||
|
||||
// determine coordinates corners TopLeft and BottomRight
|
||||
|
||||
// Canvas extent (larger) is different than map extent
|
||||
// need to compute delta between left-top of map and canvas extent.
|
||||
var mapExtent = map.getView().calculateExtent(map.getSize())
|
||||
var canvasOrigin = map.getPixelFromCoordinate([extent[0], extent[3]]);
|
||||
var mapOrigin = map.getPixelFromCoordinate([mapExtent[0], mapExtent[3]]);
|
||||
var delta = [ mapOrigin[0]-canvasOrigin[0], mapOrigin[1]-canvasOrigin[1] ];
|
||||
// var mapOpposite = map.getPixelFromCoordinate([mapExtent[2], mapExtent[1]]);
|
||||
|
||||
//convert from pixel to xy
|
||||
var xyTL = map.getCoordinateFromPixel([0,0]);
|
||||
var xyBR = map.getCoordinateFromPixel([canvasWidth,canvasHeight]);
|
||||
|
||||
// convert from xy to lat/long
|
||||
var latlonTL = ol.proj.transform(xyTL, 'EPSG:3857', 'EPSG:4326');
|
||||
var latlonBR = ol.proj.transform(xyBR, 'EPSG:3857', 'EPSG:4326');
|
||||
|
||||
// plot a square that has been fetched
|
||||
// each item is ~1 km square
|
||||
function plotRef(json) {
|
||||
|
||||
// convert from lat/long to canvas pixels
|
||||
var toScreen = function(latlonPoint) {
|
||||
var xyPoint = ol.proj.transform(latlonPoint, 'EPSG:4326', 'EPSG:3857');
|
||||
var pxPoint = map.getPixelFromCoordinate(xyPoint);
|
||||
return ( [ (pxPoint[0]+delta[0])*pixelRatio, (pxPoint[1]+delta[1])*pixelRatio ] );
|
||||
}
|
||||
|
||||
function fillBox(x) {
|
||||
var tl = toScreen([x[0],x[1]]); var br = toScreen([x[2],x[3]]);
|
||||
contextC.fillRect( tl[0], tl[1], br[0] - tl[0], br[1] - tl[1] );
|
||||
}
|
||||
|
||||
if ( coverageOverlay.colours.length > 0 && json.p ) {
|
||||
// expand out the compressed data - smaller over the network
|
||||
var points = [];
|
||||
json.p.forEach( function(position) {
|
||||
var data = position.split('/');
|
||||
var mgrs = json.t + data[0];
|
||||
points.push( { a: data[1], m: mgrs } );
|
||||
if ( ! cacheUTM[ mgrs ] ) {
|
||||
cacheUTM[ mgrs ] = inverse( mgrs );
|
||||
}
|
||||
} );
|
||||
|
||||
contextC.save();
|
||||
contextC.globalAlpha = 1;
|
||||
contextC.lineWidth = 0.2;
|
||||
for( var i = 250, p = 1000000, n = 0; i >= 10; p = i, i-=10, n++ ) {
|
||||
contextC.fillStyle = contextC.strokeStyle = coverageOverlay.colours[ n ];
|
||||
var t = i/5;
|
||||
|
||||
points.forEach( function(position) {
|
||||
if ( position.a >= i && position.a < p ) {
|
||||
fillBox( cacheUTM[ position.m ] );
|
||||
}
|
||||
});
|
||||
}
|
||||
contextC.restore();
|
||||
}
|
||||
map.render(); // redraw map after every plotref
|
||||
};
|
||||
|
||||
var tiles = {}; // 0.1 deg by 0.1 deg (~11 km square)
|
||||
for( var x = Math.min(latlonTL[0],latlonBR[0]), xm = Math.max(latlonTL[0],latlonBR[0]); x <= xm ;x+= 0.1 ) {
|
||||
for( var y = Math.min(latlonBR[1],latlonTL[1]), ym = Math.max(latlonBR[1],latlonTL[1]); y <= ym; y+= 0.1 ) {
|
||||
tiles[ forward([x,y],1).substr(0,5) ] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
var source = coverageOverlay.source;
|
||||
var station = coverageOverlay.station;
|
||||
var start = coverageOverlay.start;
|
||||
var end = coverageOverlay.end;
|
||||
|
||||
// fetch each square
|
||||
Object.keys(tiles).sort().forEach( function(tile) {
|
||||
var cacheKey = tile+source+station+start+end;
|
||||
if ( ! cache[cacheKey] ) {
|
||||
cache[cacheKey] = [ plotRef ];
|
||||
|
||||
$.ajax({ type: "GET",
|
||||
url: OgR+"/perl/"+source+"-tile-mgrs.pl",
|
||||
data: { station: station, start: start, end: end, squares: tile},
|
||||
timeout:20000,
|
||||
cache: true,
|
||||
error: function (xhr, ajaxOptions, thrownError) {
|
||||
},
|
||||
success: function(json) {
|
||||
cache[cacheKey].forEach( function(x) { x(json); } ); cache[cacheKey] = undefined;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
cache[cacheKey].push( plotRef );
|
||||
}
|
||||
});
|
||||
|
||||
$(document).ajaxStop(function () {
|
||||
// 0 === $.active
|
||||
});
|
||||
return canvasC;
|
||||
}
|
||||
|
||||
function bound(value, opt_min, opt_max) {
|
||||
if (opt_min != null) value = Math.max(value, opt_min);
|
||||
if (opt_max != null) value = Math.min(value, opt_max);
|
||||
return value;
|
||||
}
|
||||
|
||||
function AmbiguityMapType() {
|
||||
this.ambiguity = false;
|
||||
}
|
||||
|
||||
AmbiguityMapType.prototype.setAmbiguity = function(_ambiguity) {
|
||||
this.ambiguity = _ambiguity;
|
||||
}
|
||||
|
||||
let canvasA = null;
|
||||
AmbiguityMapType.prototype.canvasFunctionAmbiguity = function(
|
||||
extent,
|
||||
resolution,
|
||||
pixelRatio,
|
||||
size, // [canvasWidth, canvasHeight],
|
||||
projection
|
||||
) {
|
||||
if (!canvasA) {
|
||||
canvasA = document.createElement("canvas");
|
||||
canvasA.setAttribute("id", "canvasAmbiguity");
|
||||
}
|
||||
var canvasWidth = size[0], canvasHeight = size[1];
|
||||
canvasA.setAttribute("width", canvasWidth);
|
||||
canvasA.setAttribute("height", canvasHeight);
|
||||
var contextA = canvasA.getContext("2d");
|
||||
// erase the canvas before re-drawing
|
||||
contextA.clearRect(0, 0, canvasWidth, canvasHeight);
|
||||
|
||||
// do nothing if too much out zoom (<7)
|
||||
var zoom = map.getView().getZoom();
|
||||
if ( zoom < 7 && this.source != 'lowres-coverage' ) {
|
||||
return canvasA;
|
||||
}
|
||||
|
||||
// determine coordinates corners TopLeft and BottomRight
|
||||
|
||||
// Canvas extent (larger) is different than map extent
|
||||
// need to compute delta between left-top of map and canvas extent.
|
||||
var mapExtent = map.getView().calculateExtent(map.getSize())
|
||||
var canvasOrigin = map.getPixelFromCoordinate([extent[0], extent[3]]);
|
||||
var mapOrigin = map.getPixelFromCoordinate([mapExtent[0], mapExtent[3]]);
|
||||
var delta = [mapOrigin[0]-canvasOrigin[0], mapOrigin[1]-canvasOrigin[1]]
|
||||
// var mapOpposite = map.getPixelFromCoordinate([mapExtent[2], mapExtent[1]]);
|
||||
|
||||
//convert from pixel to xy
|
||||
var xyTL = map.getCoordinateFromPixel([0,0]);
|
||||
var xyBR = map.getCoordinateFromPixel([canvasWidth,canvasHeight]);
|
||||
|
||||
// convert from xy to lat/long
|
||||
var latlonTL = ol.proj.transform(xyTL, 'EPSG:3857', 'EPSG:4326');
|
||||
var latlonBR = ol.proj.transform(xyBR, 'EPSG:3857', 'EPSG:4326');
|
||||
|
||||
// TBD for V4, draw squares in transition zone properly instead of limiting to 45
|
||||
// draw ambiguity if selected and if latitude > 45 deg
|
||||
/* if ( ambiguity && latlonTL[1] > 45 && latlonBR[1] > 45 )*/ {
|
||||
|
||||
// convert from lat/long to canvas pixels
|
||||
var toScreen = function(latlonPoint) {
|
||||
var xyPoint = ol.proj.transform(latlonPoint, 'EPSG:4326', 'EPSG:3857');
|
||||
var pxPoint = map.getPixelFromCoordinate(xyPoint);
|
||||
return ( [ (pxPoint[0]+delta[0])*pixelRatio, (pxPoint[1]+delta[1])*pixelRatio ] );
|
||||
}
|
||||
|
||||
var shiftBits = 7;
|
||||
// var truncBits = 16; // V4
|
||||
var truncBits = 19; // V5
|
||||
var scaleBits = 1e7/(1<<(shiftBits+truncBits));
|
||||
|
||||
// lets draw the lines; horizontal first then vertical
|
||||
contextA.save();
|
||||
contextA.strokeStyle = 'red';
|
||||
|
||||
for( var t = (latlonBR[1] * scaleBits)|0,
|
||||
r = ((latlonTL[1] * scaleBits)|0)+1; t <= r; t++) {
|
||||
|
||||
var s = toScreen( [ latlonTL[0], t / scaleBits ] );
|
||||
var f = toScreen( [ latlonBR[0], t / scaleBits ] );
|
||||
|
||||
contextA.beginPath();
|
||||
contextA.moveTo(s[0],s[1]);
|
||||
contextA.lineTo(f[0],f[1]);
|
||||
contextA.stroke();
|
||||
}
|
||||
|
||||
// var shiftBits = (( (latlonTL[1] <= 45) && (latlonTL[1] >= -45) ? 7 : 8) ); // V4
|
||||
var shiftBits = 7; // V5
|
||||
// var truncBits = 16; // V4
|
||||
var truncBits = 20; // V5
|
||||
var scaleBits = 1e7/(1<<(shiftBits+truncBits));
|
||||
|
||||
for( var t = (latlonTL[0] * scaleBits)|0,
|
||||
r = ((latlonBR[0] * scaleBits)|0)+1; t <= r; t++) {
|
||||
|
||||
var s = toScreen( [ t / scaleBits, latlonTL[1] ] );
|
||||
var f = toScreen( [ t / scaleBits, latlonBR[1] ] );
|
||||
|
||||
contextA.beginPath();
|
||||
contextA.moveTo(s[0],s[1]);
|
||||
contextA.lineTo(f[0],f[1]);
|
||||
contextA.stroke();
|
||||
}
|
||||
contextA.restore();
|
||||
}
|
||||
return canvasA;
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,246 @@
|
|||
.ol-box {
|
||||
box-sizing:border-box;
|
||||
border-radius:2px;
|
||||
border:2px solid #00f
|
||||
}
|
||||
.ol-mouse-position {
|
||||
top:8px;
|
||||
right:8px;
|
||||
position:absolute
|
||||
}
|
||||
.ol-scale-line {
|
||||
background:rgba(0,60,136,.3);
|
||||
border-radius:4px;
|
||||
bottom:8px;
|
||||
left:8px;
|
||||
padding:2px;
|
||||
position:absolute
|
||||
}
|
||||
.ol-scale-line-inner {
|
||||
border:1px solid #eee;
|
||||
border-top:none;
|
||||
color:#eee;
|
||||
font-size:10px;
|
||||
text-align:center;
|
||||
margin:1px;
|
||||
will-change:contents,width
|
||||
}
|
||||
.ol-overlay-container {
|
||||
will-change:left,right,top,bottom
|
||||
}
|
||||
.ol-unsupported {
|
||||
display:none
|
||||
}
|
||||
.ol-unselectable,.ol-viewport {
|
||||
-webkit-touch-callout:none;
|
||||
-webkit-user-select:none;
|
||||
-moz-user-select:none;
|
||||
-ms-user-select:none;
|
||||
user-select:none;
|
||||
-webkit-tap-highlight-color:transparent
|
||||
}
|
||||
.ol-selectable {
|
||||
-webkit-touch-callout:default;
|
||||
-webkit-user-select:auto;
|
||||
-moz-user-select:auto;
|
||||
-ms-user-select:auto;
|
||||
user-select:auto
|
||||
}
|
||||
.ol-grabbing {
|
||||
cursor:-webkit-grabbing;
|
||||
cursor:-moz-grabbing;
|
||||
cursor:grabbing
|
||||
}
|
||||
.ol-grab {
|
||||
cursor:move;
|
||||
cursor:-webkit-grab;
|
||||
cursor:-moz-grab;
|
||||
cursor:grab
|
||||
}
|
||||
.ol-control {
|
||||
position:absolute;
|
||||
background-color:rgba(255,255,255,.4);
|
||||
border-radius:4px;
|
||||
padding:2px
|
||||
}
|
||||
.ol-control:hover {
|
||||
background-color:rgba(255,255,255,.6)
|
||||
}
|
||||
.ol-zoom {
|
||||
top:.5em;
|
||||
left:.5em
|
||||
}
|
||||
.ol-rotate {
|
||||
top:.5em;
|
||||
right:.5em;
|
||||
transition:opacity .25s linear,visibility 0s linear
|
||||
}
|
||||
.ol-rotate.ol-hidden {
|
||||
opacity:0;
|
||||
visibility:hidden;
|
||||
transition:opacity .25s linear,visibility 0s linear .25s
|
||||
}
|
||||
.ol-zoom-extent {
|
||||
top:4.643em;
|
||||
left:.5em
|
||||
}
|
||||
.ol-full-screen {
|
||||
right:.5em;
|
||||
top:.5em
|
||||
}
|
||||
@media print {
|
||||
.ol-control {
|
||||
display:none
|
||||
}
|
||||
}
|
||||
.ol-control button {
|
||||
display:block;
|
||||
margin:1px;
|
||||
padding:0;
|
||||
color:#fff;
|
||||
font-size:1.14em;
|
||||
font-weight:700;
|
||||
text-decoration:none;
|
||||
text-align:center;
|
||||
height:1.375em;
|
||||
width:1.375em;
|
||||
line-height:.4em;
|
||||
background-color:rgba(0,60,136,.5);
|
||||
border:none;
|
||||
border-radius:2px
|
||||
}
|
||||
.ol-control button::-moz-focus-inner {
|
||||
border:none;
|
||||
padding:0
|
||||
}
|
||||
.ol-zoom-extent button {
|
||||
line-height:1.4em
|
||||
}
|
||||
.ol-compass {
|
||||
display:block;
|
||||
font-weight:400;
|
||||
font-size:1.2em;
|
||||
will-change:transform
|
||||
}
|
||||
.ol-touch .ol-control button {
|
||||
font-size:1.5em
|
||||
}
|
||||
.ol-touch .ol-zoom-extent {
|
||||
top:5.5em
|
||||
}
|
||||
.ol-control button:focus,.ol-control button:hover {
|
||||
text-decoration:none;
|
||||
background-color:rgba(0,60,136,.7)
|
||||
}
|
||||
.ol-zoom .ol-zoom-in {
|
||||
border-radius:2px 2px 0 0
|
||||
}
|
||||
.ol-zoom .ol-zoom-out {
|
||||
border-radius:0 0 2px 2px
|
||||
}
|
||||
.ol-attribution {
|
||||
text-align:right;
|
||||
bottom:.5em;
|
||||
right:.5em;
|
||||
max-width:calc(100% - 1.3em)
|
||||
}
|
||||
.ol-attribution ul {
|
||||
margin:0;
|
||||
padding:0 .5em;
|
||||
font-size:.7rem;
|
||||
line-height:1.375em;
|
||||
color:#000;
|
||||
text-shadow:0 0 2px #fff
|
||||
}
|
||||
.ol-attribution li {
|
||||
display:inline;
|
||||
list-style:none;
|
||||
line-height:inherit
|
||||
}
|
||||
.ol-attribution li:not(:last-child):after {
|
||||
content:" "
|
||||
}
|
||||
.ol-attribution img {
|
||||
max-height:2em;
|
||||
max-width:inherit;
|
||||
vertical-align:middle
|
||||
}
|
||||
.ol-attribution button,.ol-attribution ul {
|
||||
display:inline-block
|
||||
}
|
||||
.ol-attribution.ol-collapsed ul {
|
||||
display:none
|
||||
}
|
||||
.ol-attribution.ol-logo-only ul {
|
||||
display:block
|
||||
}
|
||||
.ol-attribution:not(.ol-collapsed) {
|
||||
background:rgba(255,255,255,.8)
|
||||
}
|
||||
.ol-attribution.ol-uncollapsible {
|
||||
bottom:0;
|
||||
right:0;
|
||||
border-radius:4px 0 0;
|
||||
height:1.1em;
|
||||
line-height:1em
|
||||
}
|
||||
.ol-attribution.ol-logo-only {
|
||||
background:0 0;
|
||||
bottom:.4em;
|
||||
height:1.1em;
|
||||
line-height:1em
|
||||
}
|
||||
.ol-attribution.ol-uncollapsible img {
|
||||
margin-top:-.2em;
|
||||
max-height:1.6em
|
||||
}
|
||||
.ol-attribution.ol-logo-only button,.ol-attribution.ol-uncollapsible button {
|
||||
display:none
|
||||
}
|
||||
.ol-zoomslider {
|
||||
top:4.5em;
|
||||
left:.5em;
|
||||
height:200px
|
||||
}
|
||||
.ol-zoomslider button {
|
||||
position:relative;
|
||||
height:10px
|
||||
}
|
||||
.ol-touch .ol-zoomslider {
|
||||
top:5.5em
|
||||
}
|
||||
.ol-overviewmap {
|
||||
left:.5em;
|
||||
bottom:.5em
|
||||
}
|
||||
.ol-overviewmap.ol-uncollapsible {
|
||||
bottom:0;
|
||||
left:0;
|
||||
border-radius:0 4px 0 0
|
||||
}
|
||||
.ol-overviewmap .ol-overviewmap-map,.ol-overviewmap button {
|
||||
display:inline-block
|
||||
}
|
||||
.ol-overviewmap .ol-overviewmap-map {
|
||||
border:1px solid #7b98bc;
|
||||
height:150px;
|
||||
margin:2px;
|
||||
width:150px
|
||||
}
|
||||
.ol-overviewmap:not(.ol-collapsed) button {
|
||||
bottom:1px;
|
||||
left:2px;
|
||||
position:absolute
|
||||
}
|
||||
.ol-overviewmap.ol-collapsed .ol-overviewmap-map,.ol-overviewmap.ol-uncollapsible button {
|
||||
display:none
|
||||
}
|
||||
.ol-overviewmap:not(.ol-collapsed) {
|
||||
background:rgba(255,255,255,.8)
|
||||
}
|
||||
.ol-overviewmap-box {
|
||||
border:2px dotted rgba(0,60,136,.7)
|
||||
}
|
||||
.ol-overviewmap .ol-overviewmap-box:hover {
|
||||
cursor:move
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,129 @@
|
|||
|
||||
#map_canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
i.subtle {
|
||||
color: #888;
|
||||
}
|
||||
|
||||
.dropdown-submenu{position:relative;}
|
||||
.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-top:-6px;margin-left:-1px;-webkit-border-radius:0 6px 6px 6px;-moz-border-radius:0 6px 6px 6px;border-radius:0 6px 6px 6px;}
|
||||
.dropdown-submenu:hover>.dropdown-menu{display:block;}
|
||||
.dropdown-submenu>a:after{display:block;content:" ";float:right;width:0;height:0;border-color:transparent;border-style:solid;border-width:5px 0 5px 5px;border-left-color:#cccccc;margin-top:5px;margin-right:-10px;}
|
||||
.dropdown-submenu:hover>a:after{border-left-color:#ffffff;}
|
||||
.dropdown-submenu.pull-left{float:none;}.dropdown-submenu.pull-left>.dropdown-menu{left:-200%;right:+100%;margin-left:10px;-webkit-border-radius:6px 0 6px 6px;-moz-border-radius:6px 0 6px 6px;border-radius:6px 0 6px 6px;}
|
||||
|
||||
.tt-dropdown-menu {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
display: none;
|
||||
float: left;
|
||||
min-width: 160px;
|
||||
padding: 5px 0;
|
||||
margin: 2px 0 0;
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
background-clip: padding-box;
|
||||
}
|
||||
.tt-suggestion > p {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
color: #333333;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tt-suggestion > p:hover,
|
||||
.tt-suggestion > p:focus,
|
||||
.tt-suggestion.tt-cursor p {
|
||||
color: #ffffff;
|
||||
text-decoration: none;
|
||||
outline: 0;
|
||||
background-color: #428bca;
|
||||
}
|
||||
.tooltip {
|
||||
position: relative;
|
||||
padding: 3px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
opacity: 0.7;
|
||||
white-space: nowrap;
|
||||
font: 10pt sans-serif;
|
||||
}
|
||||
|
||||
.ol-zoom {
|
||||
left: 50%;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.ol-control button {
|
||||
float: left
|
||||
}
|
||||
|
||||
.ol-rotate {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.l-outer-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.l-table {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.l-table-row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.l-table-cell {
|
||||
display: table-cell;
|
||||
}
|
||||
|
||||
.l-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding-left : 0px;
|
||||
padding-right : 0px;
|
||||
}
|
||||
|
||||
.l-body-content-outer-wrapper {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.l-body-content-inner-wrapper {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.l-body-content {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.l-smaller {
|
||||
margin-right: 0px;
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
padding: 5px 5px";
|
||||
}
|
|
@ -0,0 +1,935 @@
|
|||
// TinyColor v0.9.16
|
||||
// https://github.com/bgrins/TinyColor
|
||||
// 2013-08-10, Brian Grinstead, MIT License
|
||||
|
||||
(function() {
|
||||
|
||||
var trimLeft = /^[\s,#]+/,
|
||||
trimRight = /\s+$/,
|
||||
tinyCounter = 0,
|
||||
math = Math,
|
||||
mathRound = math.round,
|
||||
mathMin = math.min,
|
||||
mathMax = math.max,
|
||||
mathRandom = math.random;
|
||||
|
||||
function tinycolor (color, opts) {
|
||||
|
||||
color = (color) ? color : '';
|
||||
opts = opts || { };
|
||||
|
||||
// If input is already a tinycolor, return itself
|
||||
if (typeof color == "object" && color.hasOwnProperty("_tc_id")) {
|
||||
return color;
|
||||
}
|
||||
|
||||
var rgb = inputToRGB(color);
|
||||
var r = rgb.r,
|
||||
g = rgb.g,
|
||||
b = rgb.b,
|
||||
a = rgb.a,
|
||||
roundA = mathRound(100*a) / 100,
|
||||
format = opts.format || rgb.format;
|
||||
|
||||
// Don't let the range of [0,255] come back in [0,1].
|
||||
// Potentially lose a little bit of precision here, but will fix issues where
|
||||
// .5 gets interpreted as half of the total, instead of half of 1
|
||||
// If it was supposed to be 128, this was already taken care of by `inputToRgb`
|
||||
if (r < 1) { r = mathRound(r); }
|
||||
if (g < 1) { g = mathRound(g); }
|
||||
if (b < 1) { b = mathRound(b); }
|
||||
|
||||
return {
|
||||
ok: rgb.ok,
|
||||
format: format,
|
||||
_tc_id: tinyCounter++,
|
||||
alpha: a,
|
||||
getAlpha: function() {
|
||||
return a;
|
||||
},
|
||||
setAlpha: function(value) {
|
||||
a = boundAlpha(value);
|
||||
roundA = mathRound(100*a) / 100;
|
||||
},
|
||||
toHsv: function() {
|
||||
var hsv = rgbToHsv(r, g, b);
|
||||
return { h: hsv.h * 360, s: hsv.s, v: hsv.v, a: a };
|
||||
},
|
||||
toHsvString: function() {
|
||||
var hsv = rgbToHsv(r, g, b);
|
||||
var h = mathRound(hsv.h * 360), s = mathRound(hsv.s * 100), v = mathRound(hsv.v * 100);
|
||||
return (a == 1) ?
|
||||
"hsv(" + h + ", " + s + "%, " + v + "%)" :
|
||||
"hsva(" + h + ", " + s + "%, " + v + "%, "+ roundA + ")";
|
||||
},
|
||||
toHsl: function() {
|
||||
var hsl = rgbToHsl(r, g, b);
|
||||
return { h: hsl.h * 360, s: hsl.s, l: hsl.l, a: a };
|
||||
},
|
||||
toHslString: function() {
|
||||
var hsl = rgbToHsl(r, g, b);
|
||||
var h = mathRound(hsl.h * 360), s = mathRound(hsl.s * 100), l = mathRound(hsl.l * 100);
|
||||
return (a == 1) ?
|
||||
"hsl(" + h + ", " + s + "%, " + l + "%)" :
|
||||
"hsla(" + h + ", " + s + "%, " + l + "%, "+ roundA + ")";
|
||||
},
|
||||
toHex: function(allow3Char) {
|
||||
return rgbToHex(r, g, b, allow3Char);
|
||||
},
|
||||
toHexString: function(allow3Char) {
|
||||
return '#' + rgbToHex(r, g, b, allow3Char);
|
||||
},
|
||||
toHexStringAlpha: function(allow3Char) {
|
||||
return '#' + rgbToHex(r, g, b, false) + roundA.toString(16);
|
||||
},
|
||||
toRgb: function() {
|
||||
return { r: mathRound(r), g: mathRound(g), b: mathRound(b), a: a };
|
||||
},
|
||||
toRgbString: function() {
|
||||
return (a == 1) ?
|
||||
"rgb(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ")" :
|
||||
"rgba(" + mathRound(r) + ", " + mathRound(g) + ", " + mathRound(b) + ", " + roundA + ")";
|
||||
},
|
||||
toPercentageRgb: function() {
|
||||
return { r: mathRound(bound01(r, 255) * 100) + "%", g: mathRound(bound01(g, 255) * 100) + "%", b: mathRound(bound01(b, 255) * 100) + "%", a: a };
|
||||
},
|
||||
toPercentageRgbString: function() {
|
||||
return (a == 1) ?
|
||||
"rgb(" + mathRound(bound01(r, 255) * 100) + "%, " + mathRound(bound01(g, 255) * 100) + "%, " + mathRound(bound01(b, 255) * 100) + "%)" :
|
||||
"rgba(" + mathRound(bound01(r, 255) * 100) + "%, " + mathRound(bound01(g, 255) * 100) + "%, " + mathRound(bound01(b, 255) * 100) + "%, " + roundA + ")";
|
||||
},
|
||||
toName: function() {
|
||||
if (a === 0) {
|
||||
return "transparent";
|
||||
}
|
||||
|
||||
return hexNames[rgbToHex(r, g, b, true)] || false;
|
||||
},
|
||||
toFilter: function(secondColor) {
|
||||
var hex = rgbToHex(r, g, b);
|
||||
var secondHex = hex;
|
||||
var alphaHex = Math.round(parseFloat(a) * 255).toString(16);
|
||||
var secondAlphaHex = alphaHex;
|
||||
var gradientType = opts && opts.gradientType ? "GradientType = 1, " : "";
|
||||
|
||||
if (secondColor) {
|
||||
var s = tinycolor(secondColor);
|
||||
secondHex = s.toHex();
|
||||
secondAlphaHex = Math.round(parseFloat(s.alpha) * 255).toString(16);
|
||||
}
|
||||
|
||||
return "progid:DXImageTransform.Microsoft.gradient("+gradientType+"startColorstr=#" + pad2(alphaHex) + hex + ",endColorstr=#" + pad2(secondAlphaHex) + secondHex + ")";
|
||||
},
|
||||
toString: function(format) {
|
||||
var formatSet = !!format;
|
||||
format = format || this.format;
|
||||
|
||||
var formattedString = false;
|
||||
var hasAlphaAndFormatNotSet = !formatSet && a < 1 && a > 0;
|
||||
var formatWithAlpha = hasAlphaAndFormatNotSet && (format === "hex" || format === "hex6" || format === "hex3" || format === "name");
|
||||
|
||||
if (format === "rgb") {
|
||||
formattedString = this.toRgbString();
|
||||
}
|
||||
if (format === "prgb") {
|
||||
formattedString = this.toPercentageRgbString();
|
||||
}
|
||||
if (format === "hex" || format === "hex6") {
|
||||
formattedString = this.toHexString();
|
||||
}
|
||||
if (format === "hex3") {
|
||||
formattedString = this.toHexString(true);
|
||||
}
|
||||
if (format === "name") {
|
||||
formattedString = this.toName();
|
||||
}
|
||||
if (format === "hsl") {
|
||||
formattedString = this.toHslString();
|
||||
}
|
||||
if (format === "hsv") {
|
||||
formattedString = this.toHsvString();
|
||||
}
|
||||
|
||||
if (formatWithAlpha) {
|
||||
return this.toRgbString();
|
||||
}
|
||||
|
||||
return formattedString || this.toHexString();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// If input is an object, force 1 into "1.0" to handle ratios properly
|
||||
// String input requires "1.0" as input, so 1 will be treated as 1
|
||||
tinycolor.fromRatio = function(color, opts) {
|
||||
if (typeof color == "object") {
|
||||
var newColor = {};
|
||||
for (var i in color) {
|
||||
if (color.hasOwnProperty(i)) {
|
||||
if (i === "a") {
|
||||
newColor[i] = color[i];
|
||||
}
|
||||
else {
|
||||
newColor[i] = convertToPercentage(color[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
color = newColor;
|
||||
}
|
||||
|
||||
return tinycolor(color, opts);
|
||||
};
|
||||
|
||||
// Given a string or object, convert that input to RGB
|
||||
// Possible string inputs:
|
||||
//
|
||||
// "red"
|
||||
// "#f00" or "f00"
|
||||
// "#ff0000" or "ff0000"
|
||||
// "rgb 255 0 0" or "rgb (255, 0, 0)"
|
||||
// "rgb 1.0 0 0" or "rgb (1, 0, 0)"
|
||||
// "rgba (255, 0, 0, 1)" or "rgba 255, 0, 0, 1"
|
||||
// "rgba (1.0, 0, 0, 1)" or "rgba 1.0, 0, 0, 1"
|
||||
// "hsl(0, 100%, 50%)" or "hsl 0 100% 50%"
|
||||
// "hsla(0, 100%, 50%, 1)" or "hsla 0 100% 50%, 1"
|
||||
// "hsv(0, 100%, 100%)" or "hsv 0 100% 100%"
|
||||
//
|
||||
function inputToRGB(color) {
|
||||
|
||||
var rgb = { r: 0, g: 0, b: 0 };
|
||||
var a = 1;
|
||||
var ok = false;
|
||||
var format = false;
|
||||
|
||||
if (typeof color == "string") {
|
||||
color = stringInputToObject(color);
|
||||
}
|
||||
|
||||
if (typeof color == "object") {
|
||||
if (color.hasOwnProperty("r") && color.hasOwnProperty("g") && color.hasOwnProperty("b")) {
|
||||
rgb = rgbToRgb(color.r, color.g, color.b);
|
||||
ok = true;
|
||||
format = String(color.r).substr(-1) === "%" ? "prgb" : "rgb";
|
||||
}
|
||||
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("v")) {
|
||||
color.s = convertToPercentage(color.s);
|
||||
color.v = convertToPercentage(color.v);
|
||||
rgb = hsvToRgb(color.h, color.s, color.v);
|
||||
ok = true;
|
||||
format = "hsv";
|
||||
}
|
||||
else if (color.hasOwnProperty("h") && color.hasOwnProperty("s") && color.hasOwnProperty("l")) {
|
||||
color.s = convertToPercentage(color.s);
|
||||
color.l = convertToPercentage(color.l);
|
||||
rgb = hslToRgb(color.h, color.s, color.l);
|
||||
ok = true;
|
||||
format = "hsl";
|
||||
}
|
||||
|
||||
if (color.hasOwnProperty("a")) {
|
||||
a = color.a;
|
||||
}
|
||||
}
|
||||
|
||||
a = boundAlpha(a);
|
||||
|
||||
return {
|
||||
ok: ok,
|
||||
format: color.format || format,
|
||||
r: mathMin(255, mathMax(rgb.r, 0)),
|
||||
g: mathMin(255, mathMax(rgb.g, 0)),
|
||||
b: mathMin(255, mathMax(rgb.b, 0)),
|
||||
a: a
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
// Conversion Functions
|
||||
// --------------------
|
||||
|
||||
// `rgbToHsl`, `rgbToHsv`, `hslToRgb`, `hsvToRgb` modified from:
|
||||
// <http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript>
|
||||
|
||||
// `rgbToRgb`
|
||||
// Handle bounds / percentage checking to conform to CSS color spec
|
||||
// <http://www.w3.org/TR/css3-color/>
|
||||
// *Assumes:* r, g, b in [0, 255] or [0, 1]
|
||||
// *Returns:* { r, g, b } in [0, 255]
|
||||
function rgbToRgb(r, g, b){
|
||||
return {
|
||||
r: bound01(r, 255) * 255,
|
||||
g: bound01(g, 255) * 255,
|
||||
b: bound01(b, 255) * 255
|
||||
};
|
||||
}
|
||||
|
||||
// `rgbToHsl`
|
||||
// Converts an RGB color value to HSL.
|
||||
// *Assumes:* r, g, and b are contained in [0, 255] or [0, 1]
|
||||
// *Returns:* { h, s, l } in [0,1]
|
||||
function rgbToHsl(r, g, b) {
|
||||
|
||||
r = bound01(r, 255);
|
||||
g = bound01(g, 255);
|
||||
b = bound01(b, 255);
|
||||
|
||||
var max = mathMax(r, g, b), min = mathMin(r, g, b);
|
||||
var h, s, l = (max + min) / 2;
|
||||
|
||||
if(max == min) {
|
||||
h = s = 0; // achromatic
|
||||
}
|
||||
else {
|
||||
var d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch(max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
|
||||
h /= 6;
|
||||
}
|
||||
|
||||
return { h: h, s: s, l: l };
|
||||
}
|
||||
|
||||
// `hslToRgb`
|
||||
// Converts an HSL color value to RGB.
|
||||
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and l are contained [0, 1] or [0, 100]
|
||||
// *Returns:* { r, g, b } in the set [0, 255]
|
||||
function hslToRgb(h, s, l) {
|
||||
var r, g, b;
|
||||
|
||||
h = bound01(h, 360);
|
||||
s = bound01(s, 100);
|
||||
l = bound01(l, 100);
|
||||
|
||||
function hue2rgb(p, q, t) {
|
||||
if(t < 0) t += 1;
|
||||
if(t > 1) t -= 1;
|
||||
if(t < 1/6) return p + (q - p) * 6 * t;
|
||||
if(t < 1/2) return q;
|
||||
if(t < 2/3) return p + (q - p) * (2/3 - t) * 6;
|
||||
return p;
|
||||
}
|
||||
|
||||
if(s === 0) {
|
||||
r = g = b = l; // achromatic
|
||||
}
|
||||
else {
|
||||
var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
|
||||
var p = 2 * l - q;
|
||||
r = hue2rgb(p, q, h + 1/3);
|
||||
g = hue2rgb(p, q, h);
|
||||
b = hue2rgb(p, q, h - 1/3);
|
||||
}
|
||||
|
||||
return { r: r * 255, g: g * 255, b: b * 255 };
|
||||
}
|
||||
|
||||
// `rgbToHsv`
|
||||
// Converts an RGB color value to HSV
|
||||
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
|
||||
// *Returns:* { h, s, v } in [0,1]
|
||||
function rgbToHsv(r, g, b) {
|
||||
|
||||
r = bound01(r, 255);
|
||||
g = bound01(g, 255);
|
||||
b = bound01(b, 255);
|
||||
|
||||
var max = mathMax(r, g, b), min = mathMin(r, g, b);
|
||||
var h, s, v = max;
|
||||
|
||||
var d = max - min;
|
||||
s = max === 0 ? 0 : d / max;
|
||||
|
||||
if(max == min) {
|
||||
h = 0; // achromatic
|
||||
}
|
||||
else {
|
||||
switch(max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return { h: h, s: s, v: v };
|
||||
}
|
||||
|
||||
// `hsvToRgb`
|
||||
// Converts an HSV color value to RGB.
|
||||
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
|
||||
// *Returns:* { r, g, b } in the set [0, 255]
|
||||
function hsvToRgb(h, s, v) {
|
||||
|
||||
h = bound01(h, 360) * 6;
|
||||
s = bound01(s, 100);
|
||||
v = bound01(v, 100);
|
||||
|
||||
var i = math.floor(h),
|
||||
f = h - i,
|
||||
p = v * (1 - s),
|
||||
q = v * (1 - f * s),
|
||||
t = v * (1 - (1 - f) * s),
|
||||
mod = i % 6,
|
||||
r = [v, q, p, p, t, v][mod],
|
||||
g = [t, v, v, q, p, p][mod],
|
||||
b = [p, p, t, v, v, q][mod];
|
||||
|
||||
return { r: r * 255, g: g * 255, b: b * 255 };
|
||||
}
|
||||
|
||||
// `rgbToHex`
|
||||
// Converts an RGB color to hex
|
||||
// Assumes r, g, and b are contained in the set [0, 255]
|
||||
// Returns a 3 or 6 character hex
|
||||
function rgbToHex(r, g, b, allow3Char) {
|
||||
|
||||
var hex = [
|
||||
pad2(mathRound(r).toString(16)),
|
||||
pad2(mathRound(g).toString(16)),
|
||||
pad2(mathRound(b).toString(16))
|
||||
];
|
||||
|
||||
// Return a 3 character hex if possible
|
||||
if (allow3Char && hex[0].charAt(0) == hex[0].charAt(1) && hex[1].charAt(0) == hex[1].charAt(1) && hex[2].charAt(0) == hex[2].charAt(1)) {
|
||||
return hex[0].charAt(0) + hex[1].charAt(0) + hex[2].charAt(0);
|
||||
}
|
||||
|
||||
return hex.join("");
|
||||
}
|
||||
|
||||
// `equals`
|
||||
// Can be called with any tinycolor input
|
||||
tinycolor.equals = function (color1, color2) {
|
||||
if (!color1 || !color2) { return false; }
|
||||
return tinycolor(color1).toRgbString() == tinycolor(color2).toRgbString();
|
||||
};
|
||||
tinycolor.random = function() {
|
||||
return tinycolor.fromRatio({
|
||||
r: mathRandom(),
|
||||
g: mathRandom(),
|
||||
b: mathRandom()
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
// Modification Functions
|
||||
// ----------------------
|
||||
// Thanks to less.js for some of the basics here
|
||||
// <https://github.com/cloudhead/less.js/blob/master/lib/less/functions.js>
|
||||
|
||||
tinycolor.desaturate = function (color, amount) {
|
||||
amount = (amount === 0) ? 0 : (amount || 10);
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
hsl.s -= amount / 100;
|
||||
hsl.s = clamp01(hsl.s);
|
||||
return tinycolor(hsl);
|
||||
};
|
||||
tinycolor.saturate = function (color, amount) {
|
||||
amount = (amount === 0) ? 0 : (amount || 10);
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
hsl.s += amount / 100;
|
||||
hsl.s = clamp01(hsl.s);
|
||||
return tinycolor(hsl);
|
||||
};
|
||||
tinycolor.greyscale = function(color) {
|
||||
return tinycolor.desaturate(color, 100);
|
||||
};
|
||||
tinycolor.lighten = function(color, amount) {
|
||||
amount = (amount === 0) ? 0 : (amount || 10);
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
hsl.l += amount / 100;
|
||||
hsl.l = clamp01(hsl.l);
|
||||
return tinycolor(hsl);
|
||||
};
|
||||
tinycolor.darken = function (color, amount) {
|
||||
amount = (amount === 0) ? 0 : (amount || 10);
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
hsl.l -= amount / 100;
|
||||
hsl.l = clamp01(hsl.l);
|
||||
return tinycolor(hsl);
|
||||
};
|
||||
tinycolor.complement = function(color) {
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
hsl.h = (hsl.h + 180) % 360;
|
||||
return tinycolor(hsl);
|
||||
};
|
||||
|
||||
|
||||
// Combination Functions
|
||||
// ---------------------
|
||||
// Thanks to jQuery xColor for some of the ideas behind these
|
||||
// <https://github.com/infusion/jQuery-xcolor/blob/master/jquery.xcolor.js>
|
||||
|
||||
tinycolor.triad = function(color) {
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
var h = hsl.h;
|
||||
return [
|
||||
tinycolor(color),
|
||||
tinycolor({ h: (h + 120) % 360, s: hsl.s, l: hsl.l }),
|
||||
tinycolor({ h: (h + 240) % 360, s: hsl.s, l: hsl.l })
|
||||
];
|
||||
};
|
||||
tinycolor.tetrad = function(color) {
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
var h = hsl.h;
|
||||
return [
|
||||
tinycolor(color),
|
||||
tinycolor({ h: (h + 90) % 360, s: hsl.s, l: hsl.l }),
|
||||
tinycolor({ h: (h + 180) % 360, s: hsl.s, l: hsl.l }),
|
||||
tinycolor({ h: (h + 270) % 360, s: hsl.s, l: hsl.l })
|
||||
];
|
||||
};
|
||||
tinycolor.splitcomplement = function(color) {
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
var h = hsl.h;
|
||||
return [
|
||||
tinycolor(color),
|
||||
tinycolor({ h: (h + 72) % 360, s: hsl.s, l: hsl.l}),
|
||||
tinycolor({ h: (h + 216) % 360, s: hsl.s, l: hsl.l})
|
||||
];
|
||||
};
|
||||
tinycolor.analogous = function(color, results, slices) {
|
||||
results = results || 6;
|
||||
slices = slices || 30;
|
||||
|
||||
var hsl = tinycolor(color).toHsl();
|
||||
var part = 360 / slices;
|
||||
var ret = [tinycolor(color)];
|
||||
|
||||
for (hsl.h = ((hsl.h - (part * results >> 1)) + 720) % 360; --results; ) {
|
||||
hsl.h = (hsl.h + part) % 360;
|
||||
ret.push(tinycolor(hsl));
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
tinycolor.monochromatic = function(color, results) {
|
||||
results = results || 6;
|
||||
var hsv = tinycolor(color).toHsv();
|
||||
var h = hsv.h, s = hsv.s, v = hsv.v;
|
||||
var ret = [];
|
||||
var modification = 1 / results;
|
||||
|
||||
while (results--) {
|
||||
ret.push(tinycolor({ h: h, s: s, v: v}));
|
||||
v = (v + modification) % 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
|
||||
// Readability Functions
|
||||
// ---------------------
|
||||
// <http://www.w3.org/TR/AERT#color-contrast>
|
||||
|
||||
// `readability`
|
||||
// Analyze the 2 colors and returns an object with the following properties:
|
||||
// `brightness`: difference in brightness between the two colors
|
||||
// `color`: difference in color/hue between the two colors
|
||||
tinycolor.readability = function(color1, color2) {
|
||||
var a = tinycolor(color1).toRgb();
|
||||
var b = tinycolor(color2).toRgb();
|
||||
var brightnessA = (a.r * 299 + a.g * 587 + a.b * 114) / 1000;
|
||||
var brightnessB = (b.r * 299 + b.g * 587 + b.b * 114) / 1000;
|
||||
var colorDiff = (
|
||||
Math.max(a.r, b.r) - Math.min(a.r, b.r) +
|
||||
Math.max(a.g, b.g) - Math.min(a.g, b.g) +
|
||||
Math.max(a.b, b.b) - Math.min(a.b, b.b)
|
||||
);
|
||||
|
||||
return {
|
||||
brightness: Math.abs(brightnessA - brightnessB),
|
||||
color: colorDiff
|
||||
};
|
||||
};
|
||||
|
||||
// `readable`
|
||||
// http://www.w3.org/TR/AERT#color-contrast
|
||||
// Ensure that foreground and background color combinations provide sufficient contrast.
|
||||
// *Example*
|
||||
// tinycolor.readable("#000", "#111") => false
|
||||
tinycolor.readable = function(color1, color2) {
|
||||
var readability = tinycolor.readability(color1, color2);
|
||||
return readability.brightness > 125 && readability.color > 500;
|
||||
};
|
||||
|
||||
// `mostReadable`
|
||||
// Given a base color and a list of possible foreground or background
|
||||
// colors for that base, returns the most readable color.
|
||||
// *Example*
|
||||
// tinycolor.mostReadable("#123", ["#fff", "#000"]) => "#000"
|
||||
tinycolor.mostReadable = function(baseColor, colorList) {
|
||||
var bestColor = null;
|
||||
var bestScore = 0;
|
||||
var bestIsReadable = false;
|
||||
for (var i=0; i < colorList.length; i++) {
|
||||
|
||||
// We normalize both around the "acceptable" breaking point,
|
||||
// but rank brightness constrast higher than hue.
|
||||
|
||||
var readability = tinycolor.readability(baseColor, colorList[i]);
|
||||
var readable = readability.brightness > 125 && readability.color > 500;
|
||||
var score = 3 * (readability.brightness / 125) + (readability.color / 500);
|
||||
|
||||
if ((readable && ! bestIsReadable) ||
|
||||
(readable && bestIsReadable && score > bestScore) ||
|
||||
((! readable) && (! bestIsReadable) && score > bestScore)) {
|
||||
bestIsReadable = readable;
|
||||
bestScore = score;
|
||||
bestColor = tinycolor(colorList[i]);
|
||||
}
|
||||
}
|
||||
return bestColor;
|
||||
};
|
||||
|
||||
|
||||
// Big List of Colors
|
||||
// ------------------
|
||||
// <http://www.w3.org/TR/css3-color/#svg-color>
|
||||
var names = tinycolor.names = {
|
||||
aliceblue: "f0f8ff",
|
||||
antiquewhite: "faebd7",
|
||||
aqua: "0ff",
|
||||
aquamarine: "7fffd4",
|
||||
azure: "f0ffff",
|
||||
beige: "f5f5dc",
|
||||
bisque: "ffe4c4",
|
||||
black: "000",
|
||||
blanchedalmond: "ffebcd",
|
||||
blue: "00f",
|
||||
blueviolet: "8a2be2",
|
||||
brown: "a52a2a",
|
||||
burlywood: "deb887",
|
||||
burntsienna: "ea7e5d",
|
||||
cadetblue: "5f9ea0",
|
||||
chartreuse: "7fff00",
|
||||
chocolate: "d2691e",
|
||||
coral: "ff7f50",
|
||||
cornflowerblue: "6495ed",
|
||||
cornsilk: "fff8dc",
|
||||
crimson: "dc143c",
|
||||
cyan: "0ff",
|
||||
darkblue: "00008b",
|
||||
darkcyan: "008b8b",
|
||||
darkgoldenrod: "b8860b",
|
||||
darkgray: "a9a9a9",
|
||||
darkgreen: "006400",
|
||||
darkgrey: "a9a9a9",
|
||||
darkkhaki: "bdb76b",
|
||||
darkmagenta: "8b008b",
|
||||
darkolivegreen: "556b2f",
|
||||
darkorange: "ff8c00",
|
||||
darkorchid: "9932cc",
|
||||
darkred: "8b0000",
|
||||
darksalmon: "e9967a",
|
||||
darkseagreen: "8fbc8f",
|
||||
darkslateblue: "483d8b",
|
||||
darkslategray: "2f4f4f",
|
||||
darkslategrey: "2f4f4f",
|
||||
darkturquoise: "00ced1",
|
||||
darkviolet: "9400d3",
|
||||
deeppink: "ff1493",
|
||||
deepskyblue: "00bfff",
|
||||
dimgray: "696969",
|
||||
dimgrey: "696969",
|
||||
dodgerblue: "1e90ff",
|
||||
firebrick: "b22222",
|
||||
floralwhite: "fffaf0",
|
||||
forestgreen: "228b22",
|
||||
fuchsia: "f0f",
|
||||
gainsboro: "dcdcdc",
|
||||
ghostwhite: "f8f8ff",
|
||||
gold: "ffd700",
|
||||
goldenrod: "daa520",
|
||||
gray: "808080",
|
||||
green: "008000",
|
||||
greenyellow: "adff2f",
|
||||
grey: "808080",
|
||||
honeydew: "f0fff0",
|
||||
hotpink: "ff69b4",
|
||||
indianred: "cd5c5c",
|
||||
indigo: "4b0082",
|
||||
ivory: "fffff0",
|
||||
khaki: "f0e68c",
|
||||
lavender: "e6e6fa",
|
||||
lavenderblush: "fff0f5",
|
||||
lawngreen: "7cfc00",
|
||||
lemonchiffon: "fffacd",
|
||||
lightblue: "add8e6",
|
||||
lightcoral: "f08080",
|
||||
lightcyan: "e0ffff",
|
||||
lightgoldenrodyellow: "fafad2",
|
||||
lightgray: "d3d3d3",
|
||||
lightgreen: "90ee90",
|
||||
lightgrey: "d3d3d3",
|
||||
lightpink: "ffb6c1",
|
||||
lightsalmon: "ffa07a",
|
||||
lightseagreen: "20b2aa",
|
||||
lightskyblue: "87cefa",
|
||||
lightslategray: "789",
|
||||
lightslategrey: "789",
|
||||
lightsteelblue: "b0c4de",
|
||||
lightyellow: "ffffe0",
|
||||
lime: "0f0",
|
||||
limegreen: "32cd32",
|
||||
linen: "faf0e6",
|
||||
magenta: "f0f",
|
||||
maroon: "800000",
|
||||
mediumaquamarine: "66cdaa",
|
||||
mediumblue: "0000cd",
|
||||
mediumorchid: "ba55d3",
|
||||
mediumpurple: "9370db",
|
||||
mediumseagreen: "3cb371",
|
||||
mediumslateblue: "7b68ee",
|
||||
mediumspringgreen: "00fa9a",
|
||||
mediumturquoise: "48d1cc",
|
||||
mediumvioletred: "c71585",
|
||||
midnightblue: "191970",
|
||||
mintcream: "f5fffa",
|
||||
mistyrose: "ffe4e1",
|
||||
moccasin: "ffe4b5",
|
||||
navajowhite: "ffdead",
|
||||
navy: "000080",
|
||||
oldlace: "fdf5e6",
|
||||
olive: "808000",
|
||||
olivedrab: "6b8e23",
|
||||
orange: "ffa500",
|
||||
orangered: "ff4500",
|
||||
orchid: "da70d6",
|
||||
palegoldenrod: "eee8aa",
|
||||
palegreen: "98fb98",
|
||||
paleturquoise: "afeeee",
|
||||
palevioletred: "db7093",
|
||||
papayawhip: "ffefd5",
|
||||
peachpuff: "ffdab9",
|
||||
peru: "cd853f",
|
||||
pink: "ffc0cb",
|
||||
plum: "dda0dd",
|
||||
powderblue: "b0e0e6",
|
||||
purple: "800080",
|
||||
red: "f00",
|
||||
rosybrown: "bc8f8f",
|
||||
royalblue: "4169e1",
|
||||
saddlebrown: "8b4513",
|
||||
salmon: "fa8072",
|
||||
sandybrown: "f4a460",
|
||||
seagreen: "2e8b57",
|
||||
seashell: "fff5ee",
|
||||
sienna: "a0522d",
|
||||
silver: "c0c0c0",
|
||||
skyblue: "87ceeb",
|
||||
slateblue: "6a5acd",
|
||||
slategray: "708090",
|
||||
slategrey: "708090",
|
||||
snow: "fffafa",
|
||||
springgreen: "00ff7f",
|
||||
steelblue: "4682b4",
|
||||
tan: "d2b48c",
|
||||
teal: "008080",
|
||||
thistle: "d8bfd8",
|
||||
tomato: "ff6347",
|
||||
turquoise: "40e0d0",
|
||||
violet: "ee82ee",
|
||||
wheat: "f5deb3",
|
||||
white: "fff",
|
||||
whitesmoke: "f5f5f5",
|
||||
yellow: "ff0",
|
||||
yellowgreen: "9acd32"
|
||||
};
|
||||
|
||||
// Make it easy to access colors via `hexNames[hex]`
|
||||
var hexNames = tinycolor.hexNames = flip(names);
|
||||
|
||||
|
||||
// Utilities
|
||||
// ---------
|
||||
|
||||
// `{ 'name1': 'val1' }` becomes `{ 'val1': 'name1' }`
|
||||
function flip(o) {
|
||||
var flipped = { };
|
||||
for (var i in o) {
|
||||
if (o.hasOwnProperty(i)) {
|
||||
flipped[o[i]] = i;
|
||||
}
|
||||
}
|
||||
return flipped;
|
||||
}
|
||||
|
||||
// Return a valid alpha value [0,1] with all invalid values being set to 1
|
||||
function boundAlpha(a) {
|
||||
a = parseFloat(a);
|
||||
|
||||
if (isNaN(a) || a < 0 || a > 1) {
|
||||
a = 1;
|
||||
}
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
// Take input from [0, n] and return it as [0, 1]
|
||||
function bound01(n, max) {
|
||||
if (isOnePointZero(n)) { n = "100%"; }
|
||||
|
||||
var processPercent = isPercentage(n);
|
||||
n = mathMin(max, mathMax(0, parseFloat(n)));
|
||||
|
||||
// Automatically convert percentage into number
|
||||
if (processPercent) {
|
||||
n = parseInt(n * max, 10) / 100;
|
||||
}
|
||||
|
||||
// Handle floating point rounding errors
|
||||
if ((math.abs(n - max) < 0.000001)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Convert into [0, 1] range if it isn't already
|
||||
return (n % max) / parseFloat(max);
|
||||
}
|
||||
|
||||
// Force a number between 0 and 1
|
||||
function clamp01(val) {
|
||||
return mathMin(1, mathMax(0, val));
|
||||
}
|
||||
|
||||
// Parse an integer into hex
|
||||
function parseHex(val) {
|
||||
return parseInt(val, 16);
|
||||
}
|
||||
|
||||
// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
|
||||
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
|
||||
function isOnePointZero(n) {
|
||||
return typeof n == "string" && n.indexOf('.') != -1 && parseFloat(n) === 1;
|
||||
}
|
||||
|
||||
// Check to see if string passed in is a percentage
|
||||
function isPercentage(n) {
|
||||
return typeof n === "string" && n.indexOf('%') != -1;
|
||||
}
|
||||
|
||||
// Force a hex value to have 2 characters
|
||||
function pad2(c) {
|
||||
return c.length == 1 ? '0' + c : '' + c;
|
||||
}
|
||||
|
||||
// Replace a decimal with it's percentage value
|
||||
function convertToPercentage(n) {
|
||||
if (n <= 1) {
|
||||
n = (n * 100) + "%";
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
var matchers = (function() {
|
||||
|
||||
// <http://www.w3.org/TR/css3-values/#integers>
|
||||
var CSS_INTEGER = "[-\\+]?\\d+%?";
|
||||
|
||||
// <http://www.w3.org/TR/css3-values/#number-value>
|
||||
var CSS_NUMBER = "[-\\+]?\\d*\\.\\d+%?";
|
||||
|
||||
// Allow positive/negative integer/number. Don't capture the either/or, just the entire outcome.
|
||||
var CSS_UNIT = "(?:" + CSS_NUMBER + ")|(?:" + CSS_INTEGER + ")";
|
||||
|
||||
// Actual matching.
|
||||
// Parentheses and commas are optional, but not required.
|
||||
// Whitespace can take the place of commas or opening paren
|
||||
var PERMISSIVE_MATCH3 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
||||
var PERMISSIVE_MATCH4 = "[\\s|\\(]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")[,|\\s]+(" + CSS_UNIT + ")\\s*\\)?";
|
||||
|
||||
return {
|
||||
rgb: new RegExp("rgb" + PERMISSIVE_MATCH3),
|
||||
rgba: new RegExp("rgba" + PERMISSIVE_MATCH4),
|
||||
hsl: new RegExp("hsl" + PERMISSIVE_MATCH3),
|
||||
hsla: new RegExp("hsla" + PERMISSIVE_MATCH4),
|
||||
hsv: new RegExp("hsv" + PERMISSIVE_MATCH3),
|
||||
hex3: /^([0-9a-fA-F]{1})([0-9a-fA-F]{1})([0-9a-fA-F]{1})$/,
|
||||
hex6: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/,
|
||||
hex8: /^([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$/
|
||||
};
|
||||
})();
|
||||
|
||||
// `stringInputToObject`
|
||||
// Permissive string parsing. Take in a number of formats, and output an object
|
||||
// based on detected format. Returns `{ r, g, b }` or `{ h, s, l }` or `{ h, s, v}`
|
||||
function stringInputToObject(color) {
|
||||
|
||||
color = color.replace(trimLeft,'').replace(trimRight, '').toLowerCase();
|
||||
var named = false;
|
||||
if (names[color]) {
|
||||
color = names[color];
|
||||
named = true;
|
||||
}
|
||||
else if (color == 'transparent') {
|
||||
return { r: 0, g: 0, b: 0, a: 0, format: "name" };
|
||||
}
|
||||
|
||||
// Try to match string input using regular expressions.
|
||||
// Keep most of the number bounding out of this function - don't worry about [0,1] or [0,100] or [0,360]
|
||||
// Just return an object and let the conversion functions handle that.
|
||||
// This way the result will be the same whether the tinycolor is initialized with string or object.
|
||||
var match;
|
||||
if ((match = matchers.rgb.exec(color))) {
|
||||
return { r: match[1], g: match[2], b: match[3] };
|
||||
}
|
||||
if ((match = matchers.rgba.exec(color))) {
|
||||
return { r: match[1], g: match[2], b: match[3], a: match[4] };
|
||||
}
|
||||
if ((match = matchers.hsl.exec(color))) {
|
||||
return { h: match[1], s: match[2], l: match[3] };
|
||||
}
|
||||
if ((match = matchers.hsla.exec(color))) {
|
||||
return { h: match[1], s: match[2], l: match[3], a: match[4] };
|
||||
}
|
||||
if ((match = matchers.hsv.exec(color))) {
|
||||
return { h: match[1], s: match[2], v: match[3] };
|
||||
}
|
||||
if ((match = matchers.hex6.exec(color))) {
|
||||
return {
|
||||
r: parseHex(match[1]),
|
||||
g: parseHex(match[2]),
|
||||
b: parseHex(match[3]),
|
||||
format: named ? "name" : "hex"
|
||||
};
|
||||
}
|
||||
if ((match = matchers.hex8.exec(color))) {
|
||||
return {
|
||||
r: parseHex(match[1]),
|
||||
g: parseHex(match[2]),
|
||||
b: parseHex(match[3]),
|
||||
a: parseHex(match[4])/255,
|
||||
format: named ? "name" : "hex"
|
||||
};
|
||||
}
|
||||
if ((match = matchers.hex3.exec(color))) {
|
||||
return {
|
||||
r: parseHex(match[1] + '' + match[1]),
|
||||
g: parseHex(match[2] + '' + match[2]),
|
||||
b: parseHex(match[3] + '' + match[3]),
|
||||
format: named ? "name" : "hex"
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Node: Export function
|
||||
if (typeof module !== "undefined" && module.exports) {
|
||||
module.exports = tinycolor;
|
||||
}
|
||||
// AMD/requirejs: Define the module
|
||||
else if (typeof define !== "undefined") {
|
||||
define(function () {return tinycolor;});
|
||||
}
|
||||
// Browser: Expose to window
|
||||
else {
|
||||
window.tinycolor = tinycolor;
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1 @@
|
|||
var url = "http://yourserver.com/OGNRANGE/";
|
|
@ -0,0 +1,189 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<meta name="author" content="">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}">
|
||||
|
||||
<title>Open Glider Network Range Tool</title>
|
||||
|
||||
<!-- Bootstrap core CSS -->
|
||||
<link href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link href="{{ url_for('static', filename='files/bootstrap/bootstrap.colorpickersliders.css') }}" rel="stylesheet">
|
||||
|
||||
<!-- Open Layers and OpenStreetMaps CSS -->
|
||||
<link href="{{ url_for('static', filename='files/ol/ol.css') }}" rel="stylesheet" type="text/css" />
|
||||
|
||||
<!-- On Glide Range overriding CSS -->
|
||||
<link href="{{ url_for('static', filename='files/style.css') }}" rel="stylesheet">
|
||||
|
||||
<script src="{{ url_for('static', filename='files/url.js') }}"></script>
|
||||
<script type="text/javascript">
|
||||
var OgR = url;
|
||||
var dLat = 52.1290225;
|
||||
var dLon = 6.8567554;
|
||||
var dZoom = 4;
|
||||
</script>
|
||||
|
||||
<script src="{{ url_for('static', filename='files/mgrs.min.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='files/maptiles2.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='files/heatmap2.js') }}"></script>
|
||||
</head>
|
||||
|
||||
<body onload="initialize()">
|
||||
|
||||
|
||||
<div style="position: absolute; top: 0px; left: 0px; width: 100%; height:100%">
|
||||
<div id="top_row" style="position: relative; top: 0px;">
|
||||
|
||||
<div class="navbar navbar-default" style="margin-bottom: 0px;">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#topNavbar">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">Open Glider Network Range</a>
|
||||
</div>
|
||||
|
||||
<div class="navbar-collapse collapse" id="topNavbar">
|
||||
<ul class="nav navbar-nav navbar-right" style="margin-bottom: 0px; margin-top: 0px;">
|
||||
|
||||
<form class="navbar-form navbar-right" role="search" style="margin-right: 0px;">
|
||||
<div class="xform-groupx stationlist" style="display: inline-block; vertical-align: middle;">
|
||||
<input type="text" id="typeahead" class="form-control" placeholder="Showing All, Choose" />
|
||||
</div>
|
||||
<button type="submit" class="btn" onclick="getStationData($('#typeahead').val())">Goto</button>
|
||||
<button type="submit" class="btn btn-default" onclick="getStationData('');">Show All</button>
|
||||
|
||||
<div class="btn-group nav ">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
When <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#" onclick="return setWhen('today')" id="today"><span class=""></span> Today</a></li>
|
||||
<li><a href="#" onclick="return setWhen('yesterday')" id="yesterday"><span class=""></span> Yesterday</a></li>
|
||||
<li><a href="#" onclick="return setWhen('lastweek')" id="lastweek"><span class=""></span> Last 7 Days</a></li>
|
||||
<li><a href="#" onclick="return setWhen('d30')" id="d30"><span class=""></span> Last 30 Days</a></li>
|
||||
<li><a href="#" onclick="return setWhen('d60')" id="d60"><span class=""></span> Last 60 Days</a></li>
|
||||
<li><a href="#" onclick="return setWhen('recent')" id="recent"><span class=""></span></span> Since 01/01/ThisYear</a></li>
|
||||
<li><a href="#" onclick="return setWhen('all')" id="all"><span class=""></span> Since 31/03/2015 (V6 Air Protocol)</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="btn-group nav ">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
What <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#" onclick="return setWhat('max')" id="max"><span class=""></span> Maximum Signal Strength</a></li>
|
||||
<li><a href="#" onclick="return setWhat('average')" id="average"><span class=""></span> Average Signal Strength</a></li>
|
||||
<li><a href="#" onclick="return setWhat('receivers')" id="receivers"><span class=""></span> Number of Receivers</a></li>
|
||||
<li><a href="#" onclick="return setWhat('coverage')" id="coverage"><span class=""></span> Likely Coverage (Based on Up Receivers)</a></li>
|
||||
<li><a href="#" onclick="return setWhat('lowres-coverage')" id="lowres-coverage"><span class=""></span> Likely Coverage (All Receivers - LowRes)</a></li>
|
||||
<li><a href="#" onclick="return setWhat('samples')" id="samples"><span class=""></span> Number of Samples</a></li>
|
||||
<li><a href="#" onclick="return setWhat('lowest')" id="lowest"><span class=""></span> Lowest Height</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="btn-group nav ">
|
||||
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
|
||||
Options <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu">
|
||||
<li><a href="#" onclick="return toggleOptions('circles')" id="circles"><span class=""></span> Show 10km circles</a></li>
|
||||
<li><a href="#" onclick="return toggleOptions('ambiguity')" id="ambiguity"><span class=""></span> Show ambiguity squares</a></li>
|
||||
<li><a href="#" onclick="return toggleOptions('airspace')" id="airspace"><span class=""></span> Show airspace</a></li>
|
||||
<li><a href="#" onclick="return toggleOptions('airports')" id="airports"><span class=""></span> Show airports</a></li>
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-submenu pull-left">
|
||||
<a href="#">Minimum Value Colour</a>
|
||||
<ul class="dropdown-menu pull-left">
|
||||
<li class="dropdown-submenu"><div id="minc"></div></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown-submenu pull-left">
|
||||
<a href="#">Maximum Value Colour</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li class="dropdown-submenu"><div id="maxc"></div></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</form>
|
||||
</ul>
|
||||
</div><!--/.nav-collapse -->
|
||||
</div>
|
||||
|
||||
<div id="description" style="text-align: center;"></div>
|
||||
</div>
|
||||
<div id="middle-row" style="position: relative; top: 0px;">
|
||||
|
||||
|
||||
|
||||
<div id="map_canvas" style="position: relative; width: 100%"></div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title">Tools Used</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<ul>
|
||||
<li>Perl - data collection</li>
|
||||
<li><a href="http://search.cpan.org/dist/Ham-APRS-FAP/">Ham-APRS-FAP</a> - APRS server connection and packet parsing</li>
|
||||
<li>BootStrap - lazy headers</li>
|
||||
<li>OGN - a bunch of gliding enthusiastas</li>
|
||||
<li>jQuery - lots of clever stuff</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="bottom_row" style="position: relative; bottom: 0px; overflow: hidden">
|
||||
<div style="float:left; padding-left: 5px; padding-right: 5px; overflow:hidden;">
|
||||
<a href="http://wiki.glidernet.org">Data from the Open Glider Network</a><br/>
|
||||
Copyright (c) 2014, <a href="http://www.temeletry.co.uk">Temeletry Ltd</a>,<a data-toggle="modal" href="#myModal">Credits</a>
|
||||
<div class="sponsor">
|
||||
|
||||
|
||||
</div>
|
||||
<span id="zoomIn_msg" style="display:none"> <b><font color="red">Zoom in to see coverage information</font></b></span>
|
||||
<span id="zoomOut_msg" style="display:none"> <b><font color="red">Zoom out to see coverage information</font></b></span>
|
||||
</div>
|
||||
<div style="float:right; padding-left: 5px; padding-right: 5px; height: 100px; overflow-y:auto;">
|
||||
<div id="details" style="display:none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Marker tooltip -->
|
||||
<div id="tooltip" class="tooltip"></div>
|
||||
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.js"></script>
|
||||
<script src="//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
||||
<script src="{{ url_for('static', filename='files/bootstrap/typeahead.bundle.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='files/tinycolor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='files/bootstrap/bootstrap.colorpickersliders.nocielch.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='files/ol/ol.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,5 +1,5 @@
|
|||
import json
|
||||
from datetime import datetime
|
||||
from datetime import datetime, date
|
||||
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
@ -8,10 +8,10 @@ from xmlunittest import XmlTestMixin
|
|||
|
||||
from tests.base import TestBaseDB, db
|
||||
|
||||
from ogn_python.model import AircraftBeacon, AircraftType, Receiver, Device, DeviceInfo
|
||||
from ogn_python.model import AircraftBeacon, AircraftType, Receiver, Device, DeviceInfo, ReceiverCoverage
|
||||
|
||||
from ogn_python.backend.liveglidernet import rec, lxml
|
||||
from ogn_python.backend.ognrange import stations2_filtered_pl
|
||||
from ogn_python.backend.ognrange import stations2_filtered_pl, max_tile_mgrs_pl
|
||||
|
||||
|
||||
class TestDB(TestBaseDB, XmlTestMixin):
|
||||
|
@ -47,6 +47,15 @@ class TestDB(TestBaseDB, XmlTestMixin):
|
|||
db.session.add(self.ab22)
|
||||
db.session.commit()
|
||||
|
||||
self.rc11 = ReceiverCoverage(location_mgrs_short='32TPU8312', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
|
||||
self.rc12 = ReceiverCoverage(location_mgrs_short='32TPU8434', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
|
||||
self.rc12 = ReceiverCoverage(location_mgrs_short='32TPU8434', date=date(2017, 12, 21), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r01)
|
||||
self.rc21 = ReceiverCoverage(location_mgrs_short='32TPU8512', date=date(2017, 12, 20), max_signal_quality=10, max_altitude=1000, min_altitude=600, aircraft_beacon_count=20, device_count=2, receiver=self.r02)
|
||||
db.session.add(self.rc11)
|
||||
db.session.add(self.rc12)
|
||||
db.session.add(self.rc21)
|
||||
db.session.commit()
|
||||
|
||||
@mock.patch('ogn_python.backend.liveglidernet.datetime')
|
||||
def test_rec(self, datetime_mock):
|
||||
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
|
||||
|
@ -91,7 +100,7 @@ class TestDB(TestBaseDB, XmlTestMixin):
|
|||
def test_stations2_filtered_pl(self, datetime_mock):
|
||||
datetime_mock.utcnow.return_value = datetime(2017, 12, 20, 10, 0)
|
||||
|
||||
result = stations2_filtered_pl()
|
||||
result = stations2_filtered_pl(start=date(2017, 12, 15), end=date(2017, 12, 25))
|
||||
|
||||
data = json.loads(result)
|
||||
|
||||
|
@ -119,6 +128,22 @@ class TestDB(TestBaseDB, XmlTestMixin):
|
|||
|
||||
self.assertEqual(s3["s"], 'Ohlstadt')
|
||||
|
||||
def test_max_tile_mgrs_pl(self):
|
||||
result = max_tile_mgrs_pl(station='Koenigsdf', start=date(2017, 12, 15), end=date(2017, 12, 25), squares='32TPU')
|
||||
|
||||
data = json.loads(result)
|
||||
|
||||
self.assertEqual(data['t'], '32TPU')
|
||||
self.assertEqual(data['p'][0], '8312/1')
|
||||
self.assertEqual(data['p'][1], '8434/2')
|
||||
|
||||
result = max_tile_mgrs_pl(station='Bene', start=date(2017, 12, 15), end=date(2017, 12, 25), squares='32TPU')
|
||||
|
||||
data = json.loads(result)
|
||||
|
||||
self.assertEqual(data['t'], '32TPU')
|
||||
self.assertEqual(data['p'][0], '8512/1')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -4,7 +4,7 @@ import unittest
|
|||
from tests.base import TestBaseDB, db
|
||||
|
||||
from ogn_python.model import AircraftBeacon, Receiver, ReceiverCoverage, Device
|
||||
from ogn_python.collect.ognrange import create_receiver_coverage
|
||||
from ogn_python.collect.ognrange import update_entries
|
||||
|
||||
|
||||
class TestOGNrange(TestBaseDB):
|
||||
|
@ -33,7 +33,7 @@ class TestOGNrange(TestBaseDB):
|
|||
db.session.commit()
|
||||
|
||||
def test_update_receiver_coverage(self):
|
||||
create_receiver_coverage(db.session, date=date(2017, 12, 10))
|
||||
update_entries(db.session, date=date(2017, 12, 10))
|
||||
|
||||
coverages = db.session.query(ReceiverCoverage).all()
|
||||
self.assertEqual(len(coverages), 1)
|
||||
|
|
Ładowanie…
Reference in New Issue