Files
eacp_webapp/htdocs/js/ol-display_styles.js
T
2026-06-16 11:24:50 +08:00

433 lines
15 KiB
JavaScript

/* The defition of display styles needed by openlayersmap.js */
var DisplayStyles = new Map();
_acIconStyleCache = {}; // cache for icon objects (TODO: move this into separate object which can be destroyed on view-change)
DisplayStyles.set("standard", {
name:"OSM Standard",
shortname:"standard",
defaultTrackLookbackSecs:undefined,
acIconStyle: function(){
feature = this;
if(selfDestruct(feature)) return;
var ac = planes.get(feature.hex);
var acStyleProperties = getAircraftStyleProperties(ac);
if(ac.cat==""){
ac.cat="A2"; // default: medium jet
}
var iconProps = get_image_object(ac.cat, ac.typ);
var lineDashJoined;
if(acStyleProperties.lineDash != undefined){
lineDashJoined = acStyleProperties.lineDash.join(" ");
} else {
lineDashJoined = "";
}
var borderRgbaSplitted = acStyleProperties.borderCol.replace(/^(rgb|rgba)\(/,'').replace(/\)$/,'').replace(/\s/g,'').split(',');
var borderCol = 'rgb('+borderRgbaSplitted[0]+','+borderRgbaSplitted[1]+','+borderRgbaSplitted[2]+')';
var borderAlpha = borderRgbaSplitted[3];
// find approximation to center of svg icon, the center is patched to the right position already by an include
var bbox = Raphael.pathBBox(iconProps.path);
var x = Math.floor(bbox.x + bbox.width/2.0);
var y = Math.floor(bbox.y + bbox.height/2.0);
var center = {'x': x,'y':y };
// this applies the scaling of all points in the svg path so the resulting bounding box should not be larger than 32x32 (restriction by gmaps api)
// matrix transform is faster than mapPath()
offs_x = 0;
if(iconProps.ofX){
offs_x = iconProps.ofX;
}
offs_y = 0;
if(iconProps.ofY){
offs_y = iconProps.ofY;
}
var transform = "T"+(-1*center.x + offs_x)+","+(-1*center.y + offs_y)+"S"+iconProps.scale;
var newPath = Raphael.mapPath(iconProps.path, Raphael.toMatrix(iconProps.path, transform));
// recalculate bounding box
var object_box = Raphael.pathBBox(newPath);
var strokew = 1;
if (ac.hex == highlightedPlane) { strokew = 2; }
if (ac.timeout == 2) { strokew = 0.333; } /* light on timeout 2 */
// use a viewbox that has its top left point at the top left point of the geometry's bounding box
// as specified by the google maps js api, a symbol has to fit a 32x32 coordinate system, hence the size
var viewBox = 'viewBox="'+[-16, -16, 32, 32].join(' ')+'"';
var svgDynProps = {
fill : getAircraftFillColor(ac),
stroke : borderCol,
strokeOpc : borderAlpha,
strokeWdt : strokew,
strokeDash: lineDashJoined,
//rotationAngle: rotationAngle,
}
var generateSvgString = function(addition){
var pathplain = '<path style="fill:'+svgDynProps.fill+';stroke:'+svgDynProps.stroke+';stroke-opacity:'+svgDynProps.strokeOpc+';stroke-width:'+svgDynProps.strokeWdt+';'+((svgDynProps.strokeDash)?'stroke-dasharray:'+svgDynProps.strokeDash+';':"")+'" fill-opacity="0.9" d="'+newPath+'" '+addition+'/>';
var svgplain = '<svg version="1.1" shape-rendering="geometricPrecision" width="32px" height="32px" '+viewBox+' xmlns="http://www.w3.org/2000/svg">'+pathplain+'</svg>';
return svgplain;
}
rotationAngle = ((ac.trk + iconProps.rotcorr)%360) / 180 * Math.PI;
var svgdat = 'data:image/svg+xml;charset=utf-8,'+encodeURIComponent(generateSvgString(""));
// retrieve icon from cache
var styleBundle = _acIconStyleCache[feature.hex];
var style = undefined;
var cachedSvgDynProps = styleBundle ? styleBundle['svgprops'] : undefined;
if(styleBundle && cachedSvgDynProps && (JSON.stringify(cachedSvgDynProps) === JSON.stringify(svgDynProps))){
style = styleBundle['style'];
// now check if we have other requirements than the style has properties
childImg = style.getImage();
/*if(childImg.getScale() != iconProps.scale){
childImg.setScale(5.0/iconProps.scale);
}*/
if(childImg.getRotation() != rotationAngle){
childImg.setRotation(rotationAngle);
}
} else {
var iconEdgeLen = 32; //parseFloat($("body").css("font-size"))*2.9; // calculate icon size in pixels
style = new ol.style.Style( {image: new ol.style.Icon({
snapToPixel: false,
//anchor: [center.x, center.y],
//anchor: [fractX, fractY],
//anchorOrigin: 'top-left',
//anchorXUnits: 'pixels',
//anchorYUnits: 'pixels',
src: svgdat,
rotation: rotationAngle,
imgSize: [iconEdgeLen, iconEdgeLen] // only necessary because of IE11
})
});
_acIconStyleCache[feature.hex] = {
style: style,
svgprops: svgDynProps
};
}
return [style];
}, // end acIconStyle
trackMissingStyle : function(coords){
return generateTrackMissingStyle(coords, 'red', 15, '1.3em');
},
// standard-skin
getInfoText: function(ac, vRateIndicator){
var label = "";
label += "<div data-mouseover=\""+ labelDataGetMouseover(ac) +"\">"; //+(ac.reg==null?"":ac.reg);
label += "<table>";
label += "<tr><td class=\"flight\" colspan=\"2\">" + labelDataID(ac) + "&#8239;</td><td>" + (ac.reg==null?"":ac.reg) + "</td></tr>";
label += "<tr>";
label += "<td class=\"altitude\">" + vRateIndicator + labelDataAltitude(ac) + "</td>";
label += "<td class=\"verticalRate\">" + '&nbsp;' + labelDataVerticalRate(ac, "Δ") + "</td><td class=\"speed\"><i class=\"icon ion-speedometer\"></i>" + labelDataSpeed(ac) + "</td>";
label += "</tr></table></div>";
return label;
},
drawAllTracks: false, // do not draw tracks of all airplanes in sight
datasourceColorMap: {
"A" : 'rgba(0, 0, 0, 0.5)', //ADS-B -- black
"M" : 'rgba(235,49,49,1)', //MLAT -- red
"F" : 'rgba(0,170,0,1)', //FlightAware -- green
"O" : 'rgba(209, 87, 209,1)',// OGN/Flarm -- pink
"L" : 'rgba(146, 22, 146,1)'// Jetvision Flarm -- darkmagenta
},
highlightedColor: 'rgba(247,110,12,1)',
vrateColorMap: {
"climb": 'rgba(25,165,166,1)',
"descend": 'rgba(185,92,0,1)',
"const": 'rgba(255,255,0,1)',
"timeout": 'rgba(128,128,128,1)'
}
});
DisplayStyles.set("atcscope", {
name:"ATC Scope Display",
shortname:"atcscope",
drawAllTracks: true, // draw tracks of all airplanes in sight
defaultTrackLookbackSecs: 100, // draw a history of n seconds for un-highlighted planes (counted from newest datapoint)
acIconStyle: function(){
feature = this;
if(selfDestruct(feature)) return;
var ac = planes.get(feature.hex);
//current ac coordinates, origin for track indicating vector
//var acCoord = ol.proj.fromLonLat([parseFloat(ac.lon), parseFloat(ac.lat)]);
var acCoordWGS84 = [parseFloat(ac.lon), parseFloat(ac.lat)];
var acTrackDirWGS84 = acCoordWGS84.slice(); // clone accoords
// var acTrackDirection = ol.proj.fromLonLat([parseFloat(ac.lon), parseFloat(ac.lat)]);
//calculate the other end of the track indicating vector
// get ac track in rad, origin is heading north
var angle_to_rotate = (ac.trk / 180) * Math.PI;
// move a bit to create a vector to acCoords
var offset = 0.5;
ol.coordinate.add(acTrackDirWGS84,[offset,offset]);
// get vector length in meters
var wgs84Sphere= new ol.Sphere(6378137);
var vLength = wgs84Sphere.haversineDistance(acCoordWGS84, acTrackDirWGS84);
var kt_in_ms = 0.514444; // 1kt = 0.51444.. m/s
var acMeters_per_min = (ac.spd * kt_in_ms) * 60; // distance the ac travels in 1min
var vScaleFactor = acMeters_per_min/vLength;
// reset to acCoords and apply actual translation
acTrackDirWGS84 = acCoordWGS84.slice();
ol.coordinate.add(acTrackDirWGS84,[offset*vScaleFactor,offset*vScaleFactor]);
var acCoord = ol.proj.fromLonLat(acCoordWGS84);
var acTrackDirection = ol.proj.fromLonLat(acTrackDirWGS84);
// angle correction so vector points up
var angle_to_north = Math.atan(
(acCoord[1]-acTrackDirection[1])/(acCoord[0]-acTrackDirection[0])
) - (0.5 * Math.PI);
var indicatingVector = new ol.geom.LineString([acCoord, acTrackDirection]);
// rotation is clockwise
indicatingVector.rotate(-1.0 * angle_to_north - angle_to_rotate, acCoord);
var commonFill = new ol.style.Fill({
color: 'rgba(255,255,255,0.0)'
});
var acVector = new ol.style.Style({
geometry: indicatingVector,
fill: commonFill,
//color of the track-vector
stroke : new ol.style.Stroke({
color : 'rgba(98,255,119,1)',
width : 2
})
});
var acSquare = new ol.style.Style({
image: new ol.style.RegularShape({
fill: new ol.style.Fill({
color: getAircraftFillColor(ac)
}),
stroke: new ol.style.Stroke({
color: getActiveDisplaystyle().datasourceColorMap[ac.src],
width: 1
}),
points: 4,
radius: 4,
angle: Math.PI / 4
})
});
return [acVector, acSquare];
},
trackMissingStyle : function(coords){
return generateTrackMissingStyle(coords, 'green', 10, '0.8em');
},
// atcscope skin
getInfoText: function(ac, vRateIndicator){
var label = "";
// label += "<div data-mouseover=\""+ labelDataGetMouseover(ac) +"\">";
// label += "<table style=\"width: 100%;\">";
// label += "<tr><td class=\"infoLabel-centercell\" colspan=\"2\">" + (ac.fli || ("~" + ac.hex)) + "</td></tr>";
// label += "<tr>";
// label += "<td class=\"infoLabel-leftcell\">" + labelDataAltitude(ac) +"&#8239;</td>";
// label += "<td class=\"infoLabel-rightcell\">"+vRateIndicator + labelDataVerticalRate(ac) + "</td></tr>";
// label+= "<tr><td colspan=\"2\" class=\"infoLabel-centercell\">" + labelDataSpeed(ac) + "</td></tr>";
// label += "</table></div>";
label += "<div data-mouseover=\""+ labelDataGetMouseover(ac) +"\">"; //+(ac.reg==null?"":ac.reg);
label += "<table>";
label += "<tr><td class=\"flight\" colspan=\"2\">" + labelDataID(ac) + "&#8239;</td><td>" + (ac.reg==null?"":ac.reg) + "</td></tr>";
label += "<tr>";
label += "<td class=\"altitude\">" + vRateIndicator + labelDataAltitude(ac) + "</td>";
label += "<td class=\"verticalRate\">" + '&nbsp;' + labelDataVerticalRate(ac, "Δ") + "</td><td class=\"speed\"><i class=\"icon ion-speedometer\"></i>" + labelDataSpeed(ac) + "</td>";
label += "</tr></table></div>";
return label;
},
// spare color: 232,183,12
datasourceColorMap: {
"A" : 'rgba(97, 76, 250,0.8)', //ADS-B bluish
"M" : 'rgba(255, 37, 26, 0.8)', //MLAT red
"F" : 'rgba(47, 255, 176, 0.8)', //FlightAware green
"O" : 'rgba(209, 87, 209,1)',// OGN/Flarm pinkish/light magenta
"L" : 'rgba(146, 22, 146,1)'// Jetvision Flarm dark intense magenta
},
highlightedColor: 'rgba(247,110,12,1)',
vrateColorMap: {
"climb": 'rgba(25,165,166,1)',
"descend": 'rgba(185,92,0,1)',
"const": 'rgba(255,255,255,0.0)',
"timeout": 'rgba(128,128,128,1)'
}
});
function getAircraftStyleProperties(plane){
var styleProperties = {
borderCol: undefined,
lineDash:undefined,
featureScale:undefined
};
styleProperties.borderCol = getActiveDisplaystyle().datasourceColorMap[plane.src];
//styleProperties.lineDash = [5,10];
if (plane.timeout == 1){ /* after timeout 1 */
styleProperties.borderCol = 'rgba(128,128,128,1)';
}
styleProperties.featureScale = function(){
var defaultScale = 1.0;
/* var myZoom = map.getView().getZoom();
if((myZoom <= 14) && ((6 < myZoom))) {
return defaultScale + 0.2 * ((myZoom-10)/4);
}
else if(myZoom < 14) {
return defaultScale - 0.2;
}
else {
return defaultScale + 0.2;
}*/
}();
return styleProperties;
}
function getAircraftFillColor(plane){
var fill = undefined;
/* Change color for climb/decent */
if (plane.vrt > 300)
{
/*rFill = 25; gFill = 165; bFill = 166;*/
fill = getActiveDisplaystyle().vrateColorMap["climb"];
}
else if (plane.vrt < -300)
{
/* original A320: rFill = 148; gFill = 91; bFill = 1; */
/*rFill = 228; gFill = 137; bFill = 0;*/
// last color used before hex conversion: rFill = 185; gFill = 92; bFill = 0;
fill = getActiveDisplaystyle().vrateColorMap["descend"];
} else {
fill = getActiveDisplaystyle().vrateColorMap["const"]; // transparent if no significant vrate
}
if (plane.timeout == 1) /* after timeout 1 */
{
fill = getActiveDisplaystyle().vrateColorMap["timeout"];
}
return fill;
}
// generates a style with four question marks around coords
function generateTrackMissingStyle(coords, textColor, radius, fontSize){
var off = radius; // alignment offset
var offsets = [
[0,off],
[0,off*-1],
[off,0],
[off*-1,0]
];
var styleArr = [];
offsets.forEach(function(offset){
styleArr.push(new ol.style.Style({
text: new ol.style.Text({
text:'?',
offsetX: offset[0],
offsetY: offset[1],
font: fontSize+' sans-serif',
fill: new ol.style.Fill({color: textColor})
}),
}));
});
return styleArr;
}
/*
* Check if feature still got a position update, if not destroy map artifacts
* */
function selfDestruct(feature){
if(!feature) return false; // cannot destruct nonexisting feature
var acHasFeaturesInCurrentExtent = keepPathsFromEviction.has(feature.hex);
if(!inLastUpdate.get(feature.hex) && !acHasFeaturesInCurrentExtent) {
removePlaneFeatures(planes.get(feature.hex));
return true;
} else {
return false;
}
}
/* from gmap */
function labelDataGetMouseover(ac){
var title = ac.hex + " " + (ac.typ==null?"":ac.typ);
if ( ac.org!=null && ac.org.length ) { title += '<br/>' + ac.org; }
if ( ac.dst!=null && ac.dst.length ) { title += '&rarr;' + ac.dst; }
if ( ac.squ!=null && ac.squ.length ) {title += '<br/>SQ:' + ac.squ; }
return title;
}
function labelDataID(ac) {
if( ac.fli!==null && ac.fli!=="") return ac.fli;
if( ac.reg!==null && ac.reg!=="" && ac.reg!=="(state)" ) return ac.reg;
return ac.hex;
}
function labelDataVerticalRate(ac, prefix) {
var s = "";
if(prefix != "" && prefix != undefined){
s+= prefix;
}
// Vertical Rate
if (ac.vrt > -9000) {
/*if (ac.vrt < -300) s = */
s+= Math.round(ac.vrt/100);
}
return s;
}
function labelDataAltitude(ac) {
var s = "";
// Aircraft on Ground
if (ac.gda == "G")
{
s = "GND";
}
else
{
// Altitude
s = Math.round(ac.alt/100);
if (s < 0)
s = "-0" + -1*s;
else {
if (s < 10) s = "00" + s; else
if (s < 100) s = "0" + s;
}
}
return s;
}
function labelDataSpeed(ac) {
return Math.round(ac.spd/10);
}