diff --git a/webgui/map/FollowControl.js b/webgui/map/FollowControl.js new file mode 100644 index 000000000..774b2be3c --- /dev/null +++ b/webgui/map/FollowControl.js @@ -0,0 +1,58 @@ + L.FollowControl = L.Control.extend({ + options: { + getPosition: function() { return L.latLng(53.5,10); }, + element: 'div', + cssClass: '', + innerHTML: '', + initialFollow: true, + followUpdateInterval: 100, + noFollowUpdateInterval: 1000, + }, + + initialize: function(options) { + L.Control.prototype.initialize.call(this,options); + L.Util.setOptions(this,options); + }, + + onAdd: function(map) { + this._map = map; + this._div = L.DomUtil.create(this.options.element, this.options.cssClass ); + this._div.innerHTML = this.options.innerHTML; + this._doFollow = this.options.initialFollow; + var that = this; + this._div.onclick = function() { + that.setFollow(true); + return true; + }; + this.update(); + return this._div; + }, + + onRemove: function(map) { + this._map = null; + }, + + setFollow: function( v ) { + this._doFollow = v; + }, + + update: function() { + if( this._map && this._doFollow ) { + this._map.setView( this.options.getPosition() ); + var that = this; + setTimeout( function() { that.update(); }, this.options.noFollowUpdateInterval ); + } else { + var that = this; + setTimeout( function() { that.update(); }, this.options.followUpdateInterval ); + } + }, + + _map: null, + _doFollow: true, + }); + + L.followControl = function( options ) { + return new L.FollowControl( options ); + } + + diff --git a/webgui/map/Marker.js b/webgui/map/Marker.js new file mode 100644 index 000000000..d97aa3f0e --- /dev/null +++ b/webgui/map/Marker.js @@ -0,0 +1,96 @@ +L.RotatedMarker = L.Marker.extend({ + + options : { + angle : 0 + }, + + _setPos : function(pos) { + L.Marker.prototype._setPos.call(this, pos); + + if (L.DomUtil.TRANSFORM) { + // use the CSS transform rule if available + this._icon.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)'; + } else if (L.Browser.ie) { + // fallback for IE6, IE7, IE8 + var rad = this.options.angle * (Math.PI / 180), costheta = Math.cos(rad), sintheta = Math.sin(rad); + this._icon.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' + costheta + + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')'; + } + }, + + initialize: function(latlng,options) { + L.Marker.prototype.initialize(latlng,options); + }, + +}); + +L.rotatedMarker = function(pos) { + return new L.RotatedMarker(pos); +} + +L.AircraftMarker = L.RotatedMarker.extend({ + options : { + angle : 0, + getProperties:function() { + return {}; + }, + icon : L.divIcon({ + iconSize : [ 60, 60 ], + iconAnchor : [ 30, 30 ], + className: 'aircraft-marker-icon', + html: '<svg xmlns="http://www.w3.org/2000/svg" height="100%" width="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="#808080" stroke="black" stroke-width="5"/></svg>', + }), + 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( { + closeButton: false, + className: 'aircraft-marker-popup', + closeOnClick: false, + maxWidth: 200, + minWidth: 100, + offset: [30,30], + }, this ); + this.popup.setContent(""); + this.bindPopup( this.popup ); + this.addTo(this._map); + this.openPopup(); + + this.timeout(); + }, + + onRemove: function( map ) { + if( this.timeoutid != null ) + clearTimeout(this.timeoutid); + L.RotatedMarker.prototype.onRemove.call(this,map); + }, + + timeoutid: null, + timeout: function() { + var props = this.options.getProperties.call(this); + var popup = + '<div class="aircraft-marker-callsign">' + props.callsign + '</div>' + + '<div class="aircraft-marker-model">' + props.model + '</div>' + + '<div class="aircraft-marker-altitude">' + props.altitude + '</div>' + + '<div class="aircraft-marker-gs">' + props.speed + '</div><div style="clear: both"/>'; + this.popup.setContent(popup); + + this.options.angle = props.heading; + this.setLatLng( props.position ); + var that = this; + this.timeoutid = setTimeout( function() { that.timeout(); }, this.options.updateInterval ); + }, +}); + +L.aircraftMarker = function(latlng,options) { + return new L.AircraftMarker(latlng,options); +} + diff --git a/webgui/map/index-2.0.html b/webgui/map/index-2.0.html new file mode 100644 index 000000000..30cdd4c85 --- /dev/null +++ b/webgui/map/index-2.0.html @@ -0,0 +1,243 @@ +<!doctype html> +<html> +<head> +<meta charset="UTF-8" /> +<meta name="description" content="FlightGear - Map" /> +<meta name="viewport" + content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> +<meta name="apple-mobile-web-app-capable" content="yes"> +<link rel="apple-touch-icon" href="/img/FlightGear_logo.png"> +<meta name="mobile-web-app-capable" content="yes"> +<meta name="apple-mobile-web-app-status-bar-style" content="black"> +<meta name="format-detection" content="telephone=no"> +<link rel="shortcut icon" sizes="200x200" href="/img/FlightGear_logo.png"> +<link rel="icon" sizes="200x200" href="/img/FlightGear_logo.png"> + +<script src="../3rdparty/jquery/jquery-1.11.1.min.js" type="text/javascript"></script> +<link rel="stylesheet" href="../3rdparty/leaflet-0.7.3/leaflet.css" /> +<script src="../3rdparty/leaflet-0.7.3/leaflet.js" type="text/javascript"></script> +<script src="../lib/fgfs.js" type="text/javascript"></script> +<script src="FollowControl.js" type="text/javascript"></script> +<script src="Marker.js" type="text/javascript"></script> + +<title>FlightGear - Map</title> +</head> +<body> + <style> +html,body { + height: 100%; + margin: 0; + padding: 0; +} + +#map { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; +} + +.aircraft-marker-icon { + background-color: rgba(255,255,255,0); +} + +.aircraft-marker-icon path { + fill: #00ff00; +} + +.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-callsign,.aircraft-marker-altitude,.aircraft-marker-model,.aircraft-marker-gs { + float: left; + color: #00ff00; + text-shadow: 1px 1px #404040; + +} + +.aircraft-marker-model,.aircraft-marker-gs { + padding-left: 5px; +} + +.aircraft-marker-altitude,.aircraft-marker-callsign { + clear: both; +} + +.followAircraft { + background: white; + background: rgba(255, 255, 255, 0.8); + box-shadow: 0 0 15px rgba(0, 0, 0, 0.2); + border-radius: 5px; + width: 36px; + height: 36px; +} + + + </style> + + <div id='map'></div> + <script type="text/javascript"> + /* <![CDATA[ */ + + var propertyMirror = new FGFS.PropertyMirror([ + [ "latitude", "/position/latitude-deg" ], + [ "longitude", "/position/longitude-deg" ], + [ "altitude", "/position/altitude-deg" ], + [ "heading", "/orientation/heading-deg" ], + [ "groundspeed", "/velocities/groundspeed-kt" ], + [ "model", "/sim/model/path" ], + [ "callsign", "/sim/multiplay/callsign" ], + ]); + + + var osmLayer = new L.TileLayer( + 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', + { + maxZoom : 18, + attribution : 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors' + }); + + var map = new L.Map('map', { + center : [ 53.7, 10.0 ], + zoom : 10, + layers: [ osmLayer ], + zoomControl: true, + attributionControl: true, + }); + + L.control.layers({ + "OpenStreetMaps" : osmLayer, + "MapQuest Satelite" : new L.TileLayer( + 'http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.png', + { + maxZoom : 18, + subdomains : [ '1', '2', '3', '4' ], + attribution : 'Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a>.' + }), + + "MapQuest Roads" : new L.TileLayer( + 'http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.png', + { + maxZoom : 18, + subdomains : [ '1', '2', '3', '4' ], + attribution : + 'Tiles Courtesy of <a href="http://www.mapquest.com/" target="_blank">MapQuest</a>. Map data (c) <a href="http://www.openstreetmap.org/" target="_blank">OpenStreetMap</a> contributors, CC-BY-SA.' + }), + },{ +// "NAVDB" : navdbLayer, +// "AI" : aiLayer, + "VFRMap.com Sectionals (US)" : new L.TileLayer( + 'http://vfrmap.com/20140918/tiles/vfrc/{z}/{y}/{x}.jpg', + { + maxZoom : 12, + minZoom : 3, + attribution : '(c) <a href="VFRMap.com">VFRMap.com</a>', + tms: true, + opacity: 0.5, + bounds: L.latLngBounds(L.latLng(16.0,-179.0), L.latLng(72.0,-60.0) ), + }), + + "VFRMap.com - Low IFR (US)" : new L.TileLayer( + 'http://vfrmap.com/20140918/tiles/ifrlc/{z}/{y}/{x}.jpg', + { + maxZoom : 12, + minZoom : 5, + attribution : '© <a href="VFRMap.com">VFRMap.com</a>', + tms: true, + opacity: 0.5, + bounds: L.latLngBounds(L.latLng(16.0,-179.0), L.latLng(72.0,-60.0) ), + }), + + + "dfs.de VFR" : new L.TileLayer( + 'https://secais.dfs.de/static-maps/ICAO500-2014-DACH-Reprojected_01/tiles/{z}/{x}/{y}.png', + { + minZoom : 5, + maxZoom : 15, + attribution : 'Map data © <a href="http://www.dfs.de">DFS</a>', + bounds: L.latLngBounds(L.latLng(46.0,5.0), L.latLng(55.1,16.5) ), + }), + + "Lower Airspace (Germany)" : new L.TileLayer( + 'https://secais.dfs.de/static-maps/lower_20131114/tiles/{z}/{x}/{y}.png', + { + minZoom : 5, + maxZoom : 15, + attribution : 'Map data © <a href="http://www.dfs.de">DFS</a>', + bounds: L.latLngBounds(L.latLng(46.0,5.0), L.latLng(55.1,16.5) ), + }), + + "Precipitation" : new L.TileLayer( + 'http://{s}.tile.openweathermap.org/map/precipitation/{z}/{x}/{y}.png', + { + maxZoom : 14, + minZoom : 0, + subdomains : '12', + format : 'image/png', + transparent : true, + opacity : 0.5 + }), + + "Isobares" : new L.TileLayer( + 'http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png', + { + maxZoom : 7, + minZoom : 0, + subdomains : '12', + format : 'image/png', + transparent : true, + opacity : 0.5 + }), + }).addTo(map); + + var followAircraft = L.followControl({ + cssClass: 'followAircraft', + innerHTML: '<img src="images/followAircraft.svg" title="Center Map on Aircraft Position" />', + getPosition: function() { + return L.latLng(propertyMirror.getNode("latitude").getNumValue(), propertyMirror.getNode("longitude").getNumValue() ); + }, + }); + followAircraft.addTo(map); + map.on('dragstart', function(e) { + followAircraft.setFollow(false); + }); + + var aircraftMarker = L.aircraftMarker( L.latLng(53.6,10.1), { + getProperties:function() { + var model = propertyMirror.getNode("model").getStringValue(""); + model = model.slice( model.lastIndexOf('/')+1 ); + model = model.slice( 0, model.lastIndexOf('.') ); + + var callsign = propertyMirror.getNode("callsign").getStringValue("You!"); + if( callsign == 'callsign' ) callsign = "You!"; + + return { + "model": model, + "callsign": callsign, + "altitude": Math.round(propertyMirror.getNode("altitude").getNumValue()/100), + "speed": Math.round(propertyMirror.getNode("groundspeed").getNumValue()), + "heading": Math.round(propertyMirror.getNode("heading").getNumValue()), + "position": L.latLng( + propertyMirror.getNode("latitude").getNumValue(), + propertyMirror.getNode("longitude").getNumValue() ), + }; + }, + + updateInterval: 100, + }); + aircraftMarker.addTo(map); + + + /* ]]> */ + </script> +</body> +</html>