Konstantin Gründger 2019-04-25 08:39:24 +02:00
commit df0f32dc30
18 zmienionych plików z 8778 dodań i 16 usunięć

Wyświetl plik

@ -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

Wyświetl plik

@ -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.

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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;
}

Wyświetl plik

@ -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

Wyświetl plik

@ -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

Wyświetl plik

@ -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";
}

Wyświetl plik

@ -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;
}
})();

Wyświetl plik

@ -0,0 +1 @@
var url = "http://yourserver.com/OGNRANGE/";

Wyświetl plik

@ -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">&times;</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>

Wyświetl plik

@ -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()

Wyświetl plik

@ -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)