From 8639ab3837bc5459ae348c4a1ffd3b26e0c4a3b7 Mon Sep 17 00:00:00 2001 From: Oliver Jowett Date: Sat, 2 Jul 2016 14:44:14 +0100 Subject: [PATCH] More work on OL3. --- public_html/formatter.js | 4 +- public_html/markers.js | 10 +- public_html/planeObject.js | 248 ++++++++++++++++++++----------------- public_html/script.js | 44 ++----- 4 files changed, 152 insertions(+), 154 deletions(-) diff --git a/public_html/formatter.js b/public_html/formatter.js index 11e151d..d73fd26 100644 --- a/public_html/formatter.js +++ b/public_html/formatter.js @@ -166,7 +166,7 @@ function format_distance_long(dist) { return dist_text; } -// p as a LatLng +// p is a [lon, lat] coordinate function format_latlng(p) { - return p.lat().toFixed(3) + DEGREES + "," + NBSP + p.lng().toFixed(3) + DEGREES; + return p[1].toFixed(3) + DEGREES + "," + NBSP + p[0].toFixed(3) + DEGREES; } diff --git a/public_html/markers.js b/public_html/markers.js index bd4904f..2dae794 100755 --- a/public_html/markers.js +++ b/public_html/markers.js @@ -61,31 +61,31 @@ var _heavy_svg = var MarkerIcons = { generic : { scale : 0.4, - anchor : new google.maps.Point(32, 32), + anchor : [32, 32], path : _generic_plane_svg }, light : { scale : 0.4, - anchor : new google.maps.Point(32, 25), + anchor : [32, 25], path : _beechcraft_svg }, medium : { scale : 0.4, - anchor : new google.maps.Point(32, 32), + anchor : [32, 32], path : _generic_plane_svg }, heavy : { scale : 0.6, - anchor : new google.maps.Point(32, 32), + anchor : [32, 32], path : _heavy_svg }, rotorcraft : { scale : 0.5, - anchor : new google.maps.Point(22, 32), + anchor : [22, 32], path : _rotorcraft_svg } }; diff --git a/public_html/planeObject.js b/public_html/planeObject.js index bc717ee..e1ab6b5 100644 --- a/public_html/planeObject.js +++ b/public_html/planeObject.js @@ -13,6 +13,7 @@ function PlaneObject(icao) { this.altitude = null; this.speed = null; this.track = null; + this.prev_position = null; this.position = null; this.position_from_mlat = false this.sitedist = null; @@ -59,67 +60,75 @@ function PlaneObject(icao) { // Appends data to the running track so we can get a visual tail on the plane // Only useful for a long running browser session. PlaneObject.prototype.updateTrack = function(estimate_time) { - var here = this.position; - if (!here) - return; + if (!this.position) + return false; + if (this.position == this.prev_position) + return false; + + var projHere = ol.proj.fromLonLat(this.position); + var projPrev; + if (this.prev_position === null) { + projPrev = projHere; + } else { + projPrev = ol.proj.fromLonLat(this.prev_position); + } + + this.prev_position = this.position; if (this.track_linesegs.length == 0) { // Brand new track //console.log(this.icao + " new track"); - var newseg = { track : new google.maps.MVCArray([here,here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") + var newseg = { fixed: new ol.geom.LineString([projHere]), + feature: null, + head_update: this.last_position_time, + tail_update: this.last_position_time, + estimated: false, + ground: (this.altitude === "ground") }; this.track_linesegs.push(newseg); - this.history_size += 2; + this.history_size ++; return; } - + var lastseg = this.track_linesegs[this.track_linesegs.length - 1]; - var lastpos = lastseg.track.getAt(lastseg.track.getLength() - 1); var elapsed = (this.last_position_time - lastseg.head_update); - var new_data = (here !== lastpos); var est_track = (elapsed > estimate_time); var ground_track = (this.altitude === "ground"); - if (!new_data) - return false; - if (est_track) { + if (!lastseg.estimated) { // >5s gap in data, create a new estimated segment //console.log(this.icao + " switching to estimated"); - this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), - line : null, - head_update : this.last_position_time, - estimated : true }); + lastseg.fixed.appendCoordinate(projPrev); + this.track_linesegs.push({ fixed: new ol.geom.LineString([projPrev, projHere]), + feature: null, + head_update: this.last_position_time, + estimated: true }); this.history_size += 2; - return true; + } else { + // Keep appending to the existing dashed line; keep every point + lastseg.fixed.appendCoordinate(projPrev); + lastseg.head_update = this.last_position_time; + this.history_size++; } - - // Append to ongoing estimated line - //console.log(this.icao + " extending estimated (" + lastseg.track.getLength() + ")"); - lastseg.track.push(here); - lastseg.head_update = this.last_position_time; - this.history_size++; + return true; } if (lastseg.estimated) { - // We are back to good data. - //console.log(this.icao + " switching to good track"); - this.track_linesegs.push({ track : new google.maps.MVCArray([lastpos, here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") }); - this.history_size += 2; - return true; + // We are back to good data (we got two points close in time), switch back to + // solid lines. + lastseg = { fixed: new ol.geom.LineString([projPrev]), + feature: null, + head_update: this.last_position_time, + tail_update: this.last_position_time, + estimated: false, + ground: (this.altitude === "ground") }; + this.track_linesegs.push(lastseg); + this.history_size ++; + // continue } if ( (lastseg.ground && this.altitude !== "ground") || @@ -127,15 +136,17 @@ PlaneObject.prototype.updateTrack = function(estimate_time) { //console.log(this.icao + " ground state changed"); // Create a new segment as the ground state changed. // assume the state changed halfway between the two points - var midpoint = google.maps.geometry.spherical.interpolate(lastpos,here,0.5); - lastseg.track.push(midpoint); - this.track_linesegs.push({ track : new google.maps.MVCArray([midpoint,here,here]), - line : null, - head_update : this.last_position_time, - tail_update : this.last_position_time, - estimated : false, - ground : (this.altitude === "ground") }); - this.history_size += 4; + // FIXME needs reimplementing post-google + + lastseg.fixed.appendCoordinate(projPrev); + this.track_linesegs.push({ fixed: new ol.geom.LineString([projPrev, projHere]), + latest: here, + feature: null, + head_update: this.last_position_time, + tail_update: this.last_position_time, + estimated: false, + ground: (this.altitude === "ground") }); + this.history_size += 3; return true; } @@ -145,24 +156,22 @@ PlaneObject.prototype.updateTrack = function(estimate_time) { if (this.last_position_time - lastseg.tail_update >= 5) { // enough time has elapsed; retain the last point and add a new one //console.log(this.icao + " retain last point"); - lastseg.track.push(here); + lastseg.fixed.appendCoordinate(projHere); lastseg.tail_update = lastseg.head_update; this.history_size ++; - } else { - // replace the last point with the current position - lastseg.track.setAt(lastseg.track.getLength()-1, here); } + lastseg.head_update = this.last_position_time; return true; }; // This is to remove the line from the screen if we deselect the plane PlaneObject.prototype.clearLines = function() { - for (var i = 0; i < this.track_linesegs.length; ++i) { + for (var i = this.track_linesegs.length - 1; i >= 0 ; --i) { var seg = this.track_linesegs[i]; - if (seg.line !== null) { - seg.line.setMap(null); - seg.line = null; + if (seg.feature !== null) { + PlaneTrailFeatures.remove(seg.feature); + seg.feature = null; } } }; @@ -262,21 +271,19 @@ PlaneObject.prototype.updateIcon = function() { var weight = this.selected ? 2 : 1; var rotation = (this.track === null ? 0 : this.track); - if (col === this.icon.fillColor && opacity == this.icon.fillOpacity && weight === this.icon.strokeWeight && outline == this.icon.strokeColor && rotation === this.icon.rotation && type == this.icon.type) - return false; // no changes - - this.icon.fillColor = col; - this.icon.fillOpacity = opacity; - this.icon.strokeWeight = weight; - this.icon.strokeColor = outline; - this.icon.rotation = rotation; - this.icon.type = type; - this.icon.path = MarkerIcons[type].path; - this.icon.anchor = MarkerIcons[type].anchor; - this.icon.scale = MarkerIcons[type].scale; - if (this.marker) - this.marker.setIcon(this.icon); + /* TODO use the aircraft icon svgs */ + this.markerStyle = new ol.style.Style({ + image: new ol.style.Circle({ + fill: new ol.style.Fill({ color: col }), + stroke: new ol.style.Stroke({ color: outline, width: weight }), + radius: 5, + }) + }); + if (this.marker !== null) { + this.marker.setStyle(this.markerStyle); + } + return true; }; @@ -296,11 +303,12 @@ PlaneObject.prototype.updateData = function(receiver_timestamp, data) { if (typeof data.track !== "undefined") this.track = data.track; if (typeof data.lat !== "undefined") { - this.position = new google.maps.LatLng(data.lat, data.lon); + this.position = [data.lon, data.lat]; this.last_position_time = receiver_timestamp - data.seen_pos; if (SitePosition !== null) { - this.sitedist = google.maps.geometry.spherical.computeDistanceBetween (SitePosition, this.position); + var WGS84 = new ol.Sphere(6378137); + this.sitedist = WGS84.haversineDistance(SitePosition, this.position); } this.position_from_mlat = false; @@ -352,8 +360,8 @@ PlaneObject.prototype.updateTick = function(receiver_timestamp, last_timestamp) PlaneObject.prototype.clearMarker = function() { if (this.marker) { - this.marker.setMap(null); - google.maps.event.clearListeners(this.marker, 'click'); + PlaneIconFeatures.remove(this.marker); + /* FIXME google.maps.event.clearListeners(this.marker, 'click'); */ this.marker = null; } }; @@ -365,28 +373,30 @@ PlaneObject.prototype.updateMarker = function(moved) { return; } - if (this.marker) { - if (moved) - this.marker.setPosition(this.position); - this.updateIcon(); + if (this.marker) { + if (moved) { + this.marker.setGeometry(new ol.geom.Point(ol.proj.fromLonLat(this.position))); + } } else { + this.marker = new ol.Feature(new ol.geom.Point(ol.proj.fromLonLat(this.position))); this.updateIcon(); - this.marker = new google.maps.Marker({ - position: this.position, - map: GoogleMap, - icon: this.icon, - visible: true - }); + PlaneIconFeatures.push(this.marker); + /* FIXME // Trap clicks for this marker. google.maps.event.addListener(this.marker, 'click', selectPlaneByHex.bind(undefined,this.icao,false)); google.maps.event.addListener(this.marker, 'dblclick', selectPlaneByHex.bind(undefined,this.icao,true)); + */ } - + + this.updateIcon(); + + /* // Setting the marker title var title = (this.flight === null || this.flight.length == 0) ? this.icao : (this.flight+' ('+this.icao+')'); if (title !== this.marker.title) this.marker.setTitle(title); + */ }; // Update our planes tail line, @@ -394,39 +404,55 @@ PlaneObject.prototype.updateLines = function() { if (!this.selected) return; + var estimateStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#a08080', + width: 1.5, + lineDash: [3, 3] + }) + }); + + var airStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#000000', + width: 2 + }) + }); + + var groundStyle = new ol.style.Style({ + stroke: new ol.style.Stroke({ + color: '#408040', + width: 2 + }) + }); + + // create the new latest-position line + var lastseg = this.track_linesegs[this.track_linesegs.length - 1]; + var lastfixed = lastseg.fixed.getCoordinateAt(1.0); + var geom = new ol.geom.LineString([lastfixed, ol.proj.fromLonLat(this.position)]); + var feature = new ol.Feature(geom); + feature.setStyle(this.altitude === 'ground' ? groundStyle : airStyle); + + if (PlaneTrailFeatures.length == 0) { + PlaneTrailFeatures.push(feature); + } else { + PlaneTrailFeatures.setAt(0, feature); + } + + // create any missing fixed line features for (var i = 0; i < this.track_linesegs.length; ++i) { var seg = this.track_linesegs[i]; - if (seg.line === null) { - // console.log("create line for seg " + i + " with " + seg.track.getLength() + " points" + (seg.estimated ? " (estimated)" : "")); - // for (var j = 0; j < seg.track.getLength(); j++) { - // console.log(" point " + j + " at " + seg.track.getAt(j).lat() + "," + seg.track.getAt(j).lng()); - // } - + if (seg.feature === null) { + seg.feature = new ol.Feature(seg.fixed); if (seg.estimated) { - var lineSymbol = { - path: 'M 0,-1 0,1', - strokeOpacity : 1, - strokeColor : '#804040', - strokeWeight : 2, - scale: 2 - }; - - seg.line = new google.maps.Polyline({ - path: seg.track, - strokeOpacity: 0, - icons: [{ - icon: lineSymbol, - offset: '0', - repeat: '10px' }], - map : GoogleMap }); + seg.feature.setStyle(estimateStyle); + } else if (seg.ground) { + seg.feature.setStyle(groundStyle); } else { - seg.line = new google.maps.Polyline({ - path: seg.track, - strokeOpacity: 1.0, - strokeColor: (seg.ground ? '#408040' : '#000000'), - strokeWeight: 3, - map: GoogleMap }); + seg.feature.setStyle(airStyle); } + + PlaneTrailFeatures.push(seg.feature); } } }; diff --git a/public_html/script.js b/public_html/script.js index 761dbf7..a0950a2 100644 --- a/public_html/script.js +++ b/public_html/script.js @@ -18,7 +18,7 @@ var SpecialSquawks = { }; // Get current map settings -var CenterLat, CenterLon, ZoomLvl, MapType; +var CenterLat, CenterLon, ZoomLvl; var Dump1090Version = "unknown version"; var RefreshInterval = 1000; @@ -319,7 +319,6 @@ function initialize_map() { CenterLat = Number(localStorage['CenterLat']) || DefaultCenterLat; CenterLon = Number(localStorage['CenterLon']) || DefaultCenterLon; ZoomLvl = Number(localStorage['ZoomLvl']) || DefaultZoomLvl; - //MapType = localStorage['MapType'] || google.maps.MapTypeId.ROADMAP; // Set SitePosition, initialize sorting if (SiteShow && (typeof SiteLat !== 'undefined') && (typeof SiteLon !== 'undefined')) { @@ -635,7 +634,7 @@ function refreshSelected() { $('#selected_follow').removeClass('hidden'); if (FollowSelected) { $('#selected_follow').css('font-weight', 'bold'); - GoogleMap.panTo(selected.position); + OLMap.getView().setCenter(ol.proj.fromLonLat(selected.position)); } else { $('#selected_follow').css('font-weight', 'normal'); } @@ -810,8 +809,8 @@ function selectPlaneByHex(hex,autofollow) { if (SelectedPlane !== null && autofollow) { FollowSelected = true; - if (GoogleMap.getZoom() < 8) - GoogleMap.setZoom(8); + if (OLMap.getView().getZoom() < 8) + OLMap.getView().setZoom(8); } else { FollowSelected = false; } @@ -821,8 +820,8 @@ function selectPlaneByHex(hex,autofollow) { function toggleFollowSelected() { FollowSelected = !FollowSelected; - if (FollowSelected && GoogleMap.getZoom() < 8) - GoogleMap.setZoom(8); + if (FollowSelected && OLMap.getView().getZoom() < 8) + OLMap.getView().setZoom(8); refreshSelected(); } @@ -831,37 +830,10 @@ function resetMap() { localStorage['CenterLat'] = CenterLat = DefaultCenterLat; localStorage['CenterLon'] = CenterLon = DefaultCenterLon; localStorage['ZoomLvl'] = ZoomLvl = DefaultZoomLvl; - localStorage['MapType'] = MapType = google.maps.MapTypeId.ROADMAP; // Set and refresh - GoogleMap.setZoom(ZoomLvl); - GoogleMap.setCenter(new google.maps.LatLng(CenterLat, CenterLon)); + OLMap.getView().setZoom(ZoomLvl); + OLMap.getView().setCenter(ol.proj.fromLonLat([CenterLon, CenterLat])); selectPlaneByHex(null,false); } - -function drawCircle(marker, distance) { - if (typeof distance === 'undefined') { - return false; - } - - distance = parseFloat(distance); - if (isNaN(distance) || !isFinite(distance) || distance < 0) { - return false; - } - - distance *= 1000.0; - if (!Metric) { - distance *= 1.852; - } - - // Add circle overlay and bind to marker - var circle = new google.maps.Circle({ - map: GoogleMap, - radius: distance, // In meters - fillOpacity: 0.0, - strokeWeight: 1, - strokeOpacity: 0.3 - }); - circle.bindTo('center', marker, 'position'); -}