1
0
Fork 0

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:
Torsten Dreyer 2015-07-30 21:03:06 +02:00
parent dbc5d6689b
commit 2812cc6321
8 changed files with 168 additions and 139 deletions

View file

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

View file

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

View file

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

View file

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

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

View 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
};
});

View file

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

View file

@ -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>&deg</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);
} }