Phi: refactor some map marker
- better aircraft marker symbol - aircraft symbol loaded from file, no longer hardcoded - marker as knockout component
This commit is contained in:
parent
dbc5d6689b
commit
2812cc6321
8 changed files with 168 additions and 139 deletions
|
@ -1,4 +1,11 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 500 500" preserveAspectRatio="xMinYMin meet">
|
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 500 500" preserveAspectRatio="xMinYMin meet">
|
||||||
<path d="M250.2,59.002c11.001,0,20.176,9.165,20.176,20.777v122.24l171.12,95.954v42.779l-171.12-49.501v89.227l40.337,29.946v35.446l-60.52-20.18-60.502,20.166v-35.45l40.341-29.946v-89.227l-171.14,49.51v-42.779l171.14-95.954v-122.24c0-11.612,9.15-20.777,20.16-20.777z" fill="deeppink" stroke="black" stroke-width="5"/>
|
<defs>
|
||||||
|
<filter id="aircraftShadow1749" x="0" y="0" width="200%" height="200%">
|
||||||
|
<feOffset result="offOut" in="SourceAlpha" dx="20" dy="20" />
|
||||||
|
<feGaussianBlur result="blurOut" in="offOut" stdDeviation="10" />
|
||||||
|
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
|
<path d="M250.2,59.002c11.001,0,20.176,9.165,20.176,20.777v122.24l171.12,95.954v42.779l-171.12-49.501v89.227l40.337,29.946v35.446l-60.52-20.18-60.502,20.166v-35.45l40.341-29.946v-89.227l-171.14,49.51v-42.779l171.14-95.954v-122.24c0-11.612,9.15-20.777,20.16-20.777z" fill="deeppink" stroke="black" stroke-width="5" filter="url(#aircraftShadow1749)"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
Before Width: | Height: | Size: 488 B After Width: | Height: | Size: 834 B |
|
@ -454,6 +454,10 @@ require([
|
||||||
require : 'widgets/radiostack'
|
require : 'widgets/radiostack'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
ko.components.register('AircraftMarker', {
|
||||||
|
require : 'widgets/AircraftMarker'
|
||||||
|
});
|
||||||
|
|
||||||
ko.components.register('METAR', {
|
ko.components.register('METAR', {
|
||||||
require : 'widgets/metar'
|
require : 'widgets/metar'
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,13 +2,20 @@
|
||||||
if (typeof define === "function" && define.amd) {
|
if (typeof define === "function" && define.amd) {
|
||||||
// AMD. Register as an anonymous module.
|
// AMD. Register as an anonymous module.
|
||||||
define([
|
define([
|
||||||
'leaflet', 'props', './MapIcons'
|
'knockout', 'leaflet', 'props'
|
||||||
], factory);
|
], factory);
|
||||||
} else {
|
} else {
|
||||||
// Browser globals
|
// Browser globals
|
||||||
factory();
|
factory();
|
||||||
}
|
}
|
||||||
}(function(leaflet, SGPropertyNode, MAP_ICON) {
|
}(function(ko, leaflet, SGPropertyNode ) {
|
||||||
|
|
||||||
|
function ViewModel(h,l1,l2) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.heading = h;
|
||||||
|
self.labelLines = [ l1,l2 ];
|
||||||
|
}
|
||||||
|
|
||||||
leaflet.AILayer = leaflet.GeoJSON.extend({
|
leaflet.AILayer = leaflet.GeoJSON.extend({
|
||||||
options : {
|
options : {
|
||||||
|
@ -20,27 +27,22 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
if (feature.properties.type == "aircraft" || feature.properties.type == "multiplayer") {
|
if (feature.properties.type == "aircraft" || feature.properties.type == "multiplayer") {
|
||||||
options.angle = feature.properties.heading;
|
var l1 = feature.properties.callsign,
|
||||||
options.icon = MAP_ICON["aircraft"];
|
l2 = feature.properties.heading + 'T ' + feature.properties.speed + 'KTAS ' +
|
||||||
}
|
'F' + (feature.geometry.coordinates[2]/100).toFixed(0);
|
||||||
return new leaflet.RotatedMarker(latlng, options);
|
var m = L.aircraftMarker(latlng);
|
||||||
},
|
m.on('add', function(e) {
|
||||||
|
ko.applyBindings( new ViewModel(feature.properties.heading,l1,l2), e.target._icon);
|
||||||
onEachFeature : function(feature, layer) {
|
});
|
||||||
if (feature.properties) {
|
return m;
|
||||||
var popupString = '<div class="popup">';
|
|
||||||
for ( var k in feature.properties) {
|
|
||||||
var v = feature.properties[k];
|
|
||||||
popupString += k + ': ' + v + '<br />';
|
|
||||||
}
|
|
||||||
popupString += '</div>';
|
|
||||||
layer.bindPopup(popupString, {
|
|
||||||
maxHeight : 200
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
return new leaflet.Marker(latlng, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// onEachFeature : function(feature, layer) {
|
||||||
|
// },
|
||||||
},
|
},
|
||||||
|
|
||||||
onAdd : function(map) {
|
onAdd : function(map) {
|
||||||
leaflet.GeoJSON.prototype.onAdd.call(this, map);
|
leaflet.GeoJSON.prototype.onAdd.call(this, map);
|
||||||
this.update(++this.updateId);
|
this.update(++this.updateId);
|
||||||
|
@ -67,22 +69,21 @@
|
||||||
self.clearLayers();
|
self.clearLayers();
|
||||||
self.addData(self.aiPropsToGeoJson(data, [
|
self.addData(self.aiPropsToGeoJson(data, [
|
||||||
"aircraft", "multiplayer", "carrier"
|
"aircraft", "multiplayer", "carrier"
|
||||||
]));
|
], self._map.getBounds()));
|
||||||
}).fail(function(a, b) {
|
}).fail(function(a, b) {
|
||||||
self.updateId++;
|
self.updateId++;
|
||||||
console.log(a, b);
|
|
||||||
alert('failed to load AI data');
|
alert('failed to load AI data');
|
||||||
}).always(function() {
|
}).always(function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (self.updateId == id) {
|
if (self.updateId == id) {
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
self.update(id)
|
self.update(id)
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
aiPropsToGeoJson : function(props, types) {
|
aiPropsToGeoJson : function(props, types, bounds ) {
|
||||||
var geoJSON = {
|
var geoJSON = {
|
||||||
type : "FeatureCollection",
|
type : "FeatureCollection",
|
||||||
features : [],
|
features : [],
|
||||||
|
@ -100,7 +101,10 @@
|
||||||
var velocities = child.getNode("velocities");
|
var velocities = child.getNode("velocities");
|
||||||
var lon = position.getNode("longitude-deg").getValue();
|
var lon = position.getNode("longitude-deg").getValue();
|
||||||
var lat = position.getNode("latitude-deg").getValue();
|
var lat = position.getNode("latitude-deg").getValue();
|
||||||
var alt = position.getNode("altitude-ft") * 0.3048;
|
if( false == bounds.contains(L.latLng(lat,lon)) ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var alt = position.getNode("altitude-ft").getValue();
|
||||||
var heading = orientation.getNode("true-heading-deg").getValue();
|
var heading = orientation.getNode("true-heading-deg").getValue();
|
||||||
var id = child.getNode("id").getValue();
|
var id = child.getNode("id").getValue();
|
||||||
var callsign = "";
|
var callsign = "";
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new leaflet.RotatedMarker(latlng, options);
|
return new leaflet./*Rotated*/Marker(latlng, options);
|
||||||
},
|
},
|
||||||
|
|
||||||
onEachFeature : function(feature, layer) {
|
onEachFeature : function(feature, layer) {
|
||||||
|
|
24
Phi/widgets/AircraftMarker.html
Normal file
24
Phi/widgets/AircraftMarker.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<div data-bind="
|
||||||
|
html: iconSvg,
|
||||||
|
style: {
|
||||||
|
color: 'red',
|
||||||
|
transform: transformCss(),
|
||||||
|
'-webkit-transform': transformCss(),
|
||||||
|
'-ms-transform': transformCss(),
|
||||||
|
}">
|
||||||
|
</div>
|
||||||
|
<!-- ko foreach: label -->
|
||||||
|
<div data-bind="text: $data" style="
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
font-weight: bold;
|
||||||
|
border-radius: 3px;
|
||||||
|
color: white;
|
||||||
|
text-shadow: 0px 1px 0px black;
|
||||||
|
line-height: 105%;
|
||||||
|
border: 1px solid rgba(0,0,0,0.20);
|
||||||
|
background: rgba(40,40,40,0.2);
|
||||||
|
padding: 2px 3px;
|
||||||
|
">
|
||||||
|
</div>
|
||||||
|
<!-- /ko -->
|
41
Phi/widgets/AircraftMarker.js
Normal file
41
Phi/widgets/AircraftMarker.js
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
define([
|
||||||
|
'jquery', 'knockout', 'text!./AircraftMarker.html', 'text!../images/aircraft.svg'
|
||||||
|
], function(jquery, ko, htmlString, aircraftSvgFileContent ) {
|
||||||
|
|
||||||
|
// extract root element which should be <svg> from image xml (strip pi) and convert to string
|
||||||
|
// what an easy way to make Safari happy :-P
|
||||||
|
var iconSvgString = jquery('<div>') // wrap into detached <div> to get it's innerHTML
|
||||||
|
.append(
|
||||||
|
jquery( aircraftSvgFileContent ) // parse the file content
|
||||||
|
.filter(":first")[0]) // get root element
|
||||||
|
.html();
|
||||||
|
|
||||||
|
function ViewModel(params) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
self.iconSvg = iconSvgString;
|
||||||
|
self.rotate = 0;
|
||||||
|
self.label = [];
|
||||||
|
|
||||||
|
if( params && params.rotate ) {
|
||||||
|
self.rotate = params.rotate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( params && params.label ) {
|
||||||
|
self.label = params.label;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.transformCss = function() {
|
||||||
|
return 'rotate(' + ko.unwrap(self.rotate) + 'deg)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ViewModel.prototype.dispose = function() {
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Return component definition
|
||||||
|
return {
|
||||||
|
viewModel : ViewModel,
|
||||||
|
template : htmlString
|
||||||
|
};
|
||||||
|
});
|
|
@ -7,33 +7,25 @@
|
||||||
fill: rgba(0, 255, 0, 0.4);
|
fill: rgba(0, 255, 0, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.aircraft-marker-popup .leaflet-popup-tip {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aircraft-marker-popup .leaflet-popup-content-wrapper {
|
|
||||||
background-color: rgba(255, 255, 255, 0.50);
|
|
||||||
}
|
|
||||||
|
|
||||||
.aircraft-marker-popup .leaflet-popup-content {
|
|
||||||
margin: 5px 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aircraft-marker-altitude {
|
|
||||||
width: 33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aircraft-marker-heading {
|
|
||||||
text-align: center;
|
|
||||||
width: 33%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.aircraft-marker {
|
.aircraft-marker {
|
||||||
float: left;
|
float: left;
|
||||||
color: #00ff00;
|
color: #00ff00;
|
||||||
text-shadow: 1px 1px #404040;
|
text-shadow: 1px 1px #404040;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.aircraft-marker-label {
|
||||||
|
white-space: nowrap;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 10px;
|
||||||
|
text-shadow: 0px 1px 0px black;
|
||||||
|
line-height: 110%;
|
||||||
|
background: rgba(0,0,0,0.2);
|
||||||
|
border: 1px solid rgba(0,0,0,0.3);
|
||||||
|
padding: 2px 3px;
|
||||||
|
display: inline-block;
|
||||||
|
color: rgb(255,255,255);
|
||||||
|
}
|
||||||
|
|
||||||
.aircraft-marker span {
|
.aircraft-marker span {
|
||||||
padding-left: 0.5em;
|
padding-left: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,56 @@
|
||||||
define(
|
define(
|
||||||
[
|
[
|
||||||
'knockout', 'jquery', 'leaflet', 'text!./map.html', 'text!../images/aircraft.svg'
|
'knockout', 'jquery', 'leaflet', 'text!./map.html'
|
||||||
],
|
],
|
||||||
function(ko, jquery, leaflet, htmlString, aircraftSvg ) {
|
function(ko, jquery, leaflet, htmlString ) {
|
||||||
|
if( !L.AircraftMarker ) {
|
||||||
|
L.AircraftMarker = L.Marker
|
||||||
|
.extend({
|
||||||
|
options : {
|
||||||
|
clickable : false,
|
||||||
|
keyboard : false,
|
||||||
|
icon : L
|
||||||
|
.divIcon({
|
||||||
|
iconSize : [
|
||||||
|
60, 60
|
||||||
|
],
|
||||||
|
iconAnchor : [
|
||||||
|
30, 30
|
||||||
|
],
|
||||||
|
className : 'aircraft-marker-icon',
|
||||||
|
html :
|
||||||
|
'<div data-bind="component: { ' +
|
||||||
|
'name: \'AircraftMarker\', ' +
|
||||||
|
'params: { rotate: heading, label: labelLines } ' +
|
||||||
|
'}"></div>',
|
||||||
|
}),
|
||||||
|
zIndexOffset : 10000,
|
||||||
|
updateInterval : 100,
|
||||||
|
},
|
||||||
|
|
||||||
|
initialize : function(latlng, options) {
|
||||||
|
L.Marker.prototype.initialize(latlng, options);
|
||||||
|
L.Util.setOptions(this, options);
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
L.aircraftMarker = function(latlng, options) {
|
||||||
|
return new L.AircraftMarker(latlng, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function ViewModel(params, componentInfo) {
|
function ViewModel(params, componentInfo) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
{ // extract <svg> element from image xml (strip pi)
|
|
||||||
var xmlDoc = jquery.parseXML( aircraftSvg );
|
|
||||||
aircraftSvg = jquery( xmlDoc ).find("svg")[0].outerHTML;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.element = componentInfo.element;
|
self.element = componentInfo.element;
|
||||||
self.followAircraft = ko.observable(true);
|
self.followAircraft = ko.observable(true);
|
||||||
|
|
||||||
self.toggleFollowAircraft = function(a) {
|
self.toggleFollowAircraft = function(a) {
|
||||||
self.followAircraft(!self.followAircraft());
|
self.followAircraft(!self.followAircraft());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.altitude = ko.observable(0).extend({
|
self.altitude = ko.observable(0).extend({
|
||||||
fgprop : 'altitude'
|
fgprop : 'altitude'
|
||||||
});
|
});
|
||||||
|
@ -27,10 +59,6 @@ define(
|
||||||
fgprop : 'groundspeed'
|
fgprop : 'groundspeed'
|
||||||
});
|
});
|
||||||
|
|
||||||
self.heading = ko.observable(0).extend({
|
|
||||||
fgprop : 'heading'
|
|
||||||
});
|
|
||||||
|
|
||||||
if (params && params.css) {
|
if (params && params.css) {
|
||||||
for ( var p in params.css) {
|
for ( var p in params.css) {
|
||||||
jquery(self.element).css(p, params.css[p]);
|
jquery(self.element).css(p, params.css[p]);
|
||||||
|
@ -101,78 +129,6 @@ define(
|
||||||
L.control.scale(params.scale).addTo(self.map);
|
L.control.scale(params.scale).addTo(self.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
L.RotatedMarker = L.Marker.extend({
|
|
||||||
options : {
|
|
||||||
angle : 0
|
|
||||||
},
|
|
||||||
|
|
||||||
_setPos : function(pos) {
|
|
||||||
L.Marker.prototype._setPos.call(this, pos);
|
|
||||||
this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
L.AircraftMarker = L.RotatedMarker
|
|
||||||
.extend({
|
|
||||||
options : {
|
|
||||||
angle : 0,
|
|
||||||
clickable : false,
|
|
||||||
keyboard : false,
|
|
||||||
icon : L
|
|
||||||
.divIcon({
|
|
||||||
iconSize : [
|
|
||||||
60, 60
|
|
||||||
],
|
|
||||||
iconAnchor : [
|
|
||||||
30, 30
|
|
||||||
],
|
|
||||||
className : 'aircraft-marker-icon',
|
|
||||||
html : aircraftSvg,
|
|
||||||
}),
|
|
||||||
zIndexOffset : 10000,
|
|
||||||
updateInterval : 100,
|
|
||||||
},
|
|
||||||
|
|
||||||
initialize : function(latlng, options) {
|
|
||||||
L.RotatedMarker.prototype.initialize(latlng, options);
|
|
||||||
L.Util.setOptions(this, options);
|
|
||||||
},
|
|
||||||
|
|
||||||
onAdd : function(map) {
|
|
||||||
L.RotatedMarker.prototype.onAdd.call(this, map);
|
|
||||||
this.popup = L.popup({
|
|
||||||
autoPan : false,
|
|
||||||
keepInView : false,
|
|
||||||
closeButton : false,
|
|
||||||
className : 'aircraft-marker-popup',
|
|
||||||
closeOnClick : false,
|
|
||||||
maxWidth : 200,
|
|
||||||
minWidth : 120,
|
|
||||||
offset : [
|
|
||||||
30, 30
|
|
||||||
],
|
|
||||||
}, this);
|
|
||||||
this.popup
|
|
||||||
.setContent('<div class="aircraft-marker aircraft-marker-altitude"><span data-bind="text: altitude().toFixed(0)"></span>ft</div>'
|
|
||||||
+ '<div class="aircraft-marker aircraft-marker-heading"><span data-bind="text: heading().toFixed(0)"></span>°</div>'
|
|
||||||
+ '<div class="aircraft-marker aircraft-marker-tas"><span data-bind="text: tas().toFixed(0)"></span>kt</div><div style="clear: both"/>');
|
|
||||||
this.bindPopup(this.popup);
|
|
||||||
this.addTo(this._map);
|
|
||||||
this.openPopup();
|
|
||||||
},
|
|
||||||
|
|
||||||
onRemove : function(map) {
|
|
||||||
if (this.timeoutid != null)
|
|
||||||
clearTimeout(this.timeoutid);
|
|
||||||
L.RotatedMarker.prototype.onRemove.call(this, map);
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
L.aircraftMarker = function(latlng, options) {
|
|
||||||
return new L.AircraftMarker(latlng, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
var aircraftMarker = L.aircraftMarker(self.map.getCenter());
|
var aircraftMarker = L.aircraftMarker(self.map.getCenter());
|
||||||
|
|
||||||
aircraftMarker.addTo(self.map);
|
aircraftMarker.addTo(self.map);
|
||||||
|
@ -203,13 +159,15 @@ define(
|
||||||
aircraftMarker.setLatLng(newValue);
|
aircraftMarker.setLatLng(newValue);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.heading.subscribe(function(newValue) {
|
self.labelLines = [
|
||||||
var h = Math.round( newValue );
|
'You',
|
||||||
if( aircraftMarker.options.angle != h ) {
|
ko.pureComputed(function() {
|
||||||
aircraftMarker.options.angle = h;
|
var h = Math.round(self.heading());
|
||||||
aircraftMarker.setLatLng(self.position());
|
var t = Math.round(self.tas());
|
||||||
}
|
var a = Math.round(self.altitude());
|
||||||
});
|
return '' + h + "T " + t + "KTAS " + a + "ft";
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
|
||||||
self.mapCenter = ko.pureComputed(function() {
|
self.mapCenter = ko.pureComputed(function() {
|
||||||
return leaflet.latLng(self.latitude(), self.longitude());
|
return leaflet.latLng(self.latitude(), self.longitude());
|
||||||
|
@ -233,7 +191,6 @@ define(
|
||||||
|
|
||||||
var center = leaflet.latLng(self.latitude(), self.longitude());
|
var center = leaflet.latLng(self.latitude(), self.longitude());
|
||||||
self.map.setView( center );
|
self.map.setView( center );
|
||||||
aircraftMarker.options.angle = self.heading();
|
|
||||||
aircraftMarker.setLatLng(center);
|
aircraftMarker.setLatLng(center);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue