diff --git a/css/layout.css b/css/layout.css index 2b915f2..8ebfd8f 100644 --- a/css/layout.css +++ b/css/layout.css @@ -179,4 +179,20 @@ header h1{ padding: 10px; border-width: 1px; display: none; +} + +#banner { + display: none +} +@media only screen and (min-width: 768px){ + #banner { + line-height: 1.1; + font-size: small; + display: block; + margin-left: 300px; + text-align: center; + overflow-y: auto; + font-weight: bold; + height: 100%; + } } \ No newline at end of file diff --git a/css/main.css b/css/main.css index e273a77..ee57d7f 100644 --- a/css/main.css +++ b/css/main.css @@ -99,11 +99,13 @@ body { #timebox { top: 7px; right: 5px; + width: 205px; } #lookanglesbox { top: 40px; right: 5px; + width: 205px; } #timebox.past svg path { diff --git a/img/markers/payload-recovery-planned.png b/img/markers/payload-recovery-planned.png new file mode 100644 index 0000000..e314714 Binary files /dev/null and b/img/markers/payload-recovery-planned.png differ diff --git a/index.template.html b/index.template.html index 8a10079..964bc91 100644 --- a/index.template.html +++ b/index.template.html @@ -9,6 +9,7 @@ + @@ -28,7 +29,6 @@ - @@ -84,21 +84,70 @@ no
location
+
-

Report Recovery

-
-
- You can report a recovery here. Enter the serial number (e.g. 'S1234567'), tick recovered/not recovered, and add some notes. Click here for information on how to locate a sonde's serial number. If the sonde is not on the map, type its serial into the top-left search box and press enter to retrieve historical data. You can then mark it as recovered. Optionally, you can use your location instead of the sonde's last location. -
-
- Serial - -
-
- Recovery Success -
- - -
-
-
- Use Car Position -
- - -
-
-
- Notes -
-
- - - - -
-
- Report Result - none -
-
-
-
-
- -
+ Recovery reporting is now on the recoveries tab! (Balloon icon) @@ -402,6 +410,9 @@

Report Recovery

Zoom in for realtime data!
+ -
This version of SondeHub is out of date. Click here to reload. Page will reload automatically in -
+
This version of SondeHub is out of date. Click here to reload. Page will reload automatically in
+ diff --git a/js/app.js b/js/app.js index 60fa4ee..2731e23 100644 --- a/js/app.js +++ b/js/app.js @@ -128,6 +128,7 @@ function load_hash(no_refresh) { } break; case "f": + v = v.toUpperCase(); refocus = (follow_vehicle != v); follow_vehicle = v; def.focus = v; @@ -137,6 +138,7 @@ function load_hash(no_refresh) { if(modeList.indexOf(def.mode) == -1) def.mode = (is_mobile) ? modeDefaultMobile : modeDefault; break; case "q": + v = v.toUpperCase(); def.query = v; $("header .search input[type='text']").val(v); break; @@ -337,8 +339,7 @@ function positionUpdateError(error) { switch(error.code) { case error.PERMISSION_DENIED: - alert("no permission to use your location"); - $('#sw_chasecar').click(); // turn off chase car + $('#sw_chasecar').removeClass('on').addClass('off'); break; default: break; @@ -353,89 +354,83 @@ var positionUpdateHandle = function(position) { } } - //navigator.geolocation.getCurrentPosition(function(position) { - var lat = position.coords.latitude; - var lon = position.coords.longitude; - var alt = (position.coords.altitude) ? position.coords.altitude : 0; - var accuracy = (position.coords.accuracy) ? position.coords.accuracy : 0; - var speed = (position.coords.speed) ? position.coords.speed : 0; - - // constantly update 'last updated' field, and display friendly time since last update - if(!GPS_ts) { - GPS_ts = parseInt(position.timestamp/1000); - - setInterval(function() { - var delta_ts = parseInt(Date.now()/1000) - GPS_ts; - - // generate friendly timestamp - var hours = Math.floor(delta_ts / 3600); - var minutes = Math.floor(delta_ts / 60) % 60; - var ts_str = (delta_ts >= 60) ? - ((hours)?hours+'h ':'') + - ((minutes)?minutes+'m':'') + - ' ago' - : 'just now'; - $('#cc_timestamp').text(ts_str); - }, 30000); - - $('#cc_timestamp').text('just now'); - } + var lat = position.coords.latitude; + var lon = position.coords.longitude; + var alt = (position.coords.altitude) ? position.coords.altitude : 0; + var accuracy = (position.coords.accuracy) ? position.coords.accuracy : 0; + var speed = (position.coords.speed) ? position.coords.speed : 0; + + // constantly update 'last updated' field, and display friendly time since last update + if(!GPS_ts) { + GPS_ts = parseInt(position.timestamp/1000); + + setInterval(function() { + var delta_ts = parseInt(Date.now()/1000) - GPS_ts; + + // generate friendly timestamp + var hours = Math.floor(delta_ts / 3600); + var minutes = Math.floor(delta_ts / 60) % 60; + var ts_str = (delta_ts >= 60) ? + ((hours)?hours+'h ':'') + + ((minutes)?minutes+'m':'') + + ' ago' + : 'just now'; + $('#cc_timestamp').text(ts_str); + }, 30000); + + $('#cc_timestamp').text('just now'); + } - // save position and update only if different is available - if(CHASE_timer < (new Date()).getTime() && - ( - GPS_lat != lat || - GPS_lon != lon || - GPS_alt != alt || - GPS_speed != speed - ) + // save position and update only if different is available + if(CHASE_timer < (new Date()).getTime() && + ( + GPS_lat != lat || + GPS_lon != lon || + GPS_alt != alt || + GPS_speed != speed ) - { - GPS_lat = lat; - GPS_lon = lon; - GPS_alt = alt; - GPS_speed = speed; - GPS_ts = parseInt(position.timestamp/1000); - $('#cc_timestamp').text('just now'); - - // update look angles once we get position - if(follow_vehicle !== null && vehicles[follow_vehicle] !== undefined) { - update_lookangles(follow_vehicle); - } + ) + { + GPS_lat = lat; + GPS_lon = lon; + GPS_alt = alt; + GPS_speed = speed; + GPS_ts = parseInt(position.timestamp/1000); + $('#cc_timestamp').text('just now'); + + // update look angles once we get position + if(follow_vehicle !== null && vehicles[follow_vehicle] !== undefined) { + update_lookangles(follow_vehicle); + } - if(CHASE_enabled) { - ChaseCar.updatePosition(callsign, position); - CHASE_timer = (new Date()).getTime() + 15000; - } + if(CHASE_enabled) { + ChaseCar.updatePosition(callsign, position); + CHASE_timer = (new Date()).getTime() + 15000; } - else { return; } - - // add/update marker on the map (sondehub.js) - updateCurrentPosition(lat, lon); - - // round the coordinates - lat = parseInt(lat * 10000)/10000; // 4 decimal places (11m accuracy at equator) - lon = parseInt(lon * 10000)/10000; // 4 decimal places - speed = parseInt(speed * 10)/10; // 1 decimal place - accuracy = parseInt(accuracy); - alt = parseInt(alt); - - // dispaly them in the top right corner - $('#app_name b').html(lat + '
' + lon); - - // update chase car interface - $('#cc_lat').text(lat); - $('#cc_lon').text(lon); - $('#cc_alt').text(alt + " m"); - $('#cc_accuracy').text(accuracy + " m"); - $('#cc_speed').text(speed + " m/s"); - /* - }, - function() { - // when there is no location - $('#app_name b').html('mobile
tracker'); - }); - */ + } + + + // add/update marker on the map (sondehub.js) + updateCurrentPosition(lat, lon); + + // round the coordinates + lat = parseInt(lat * 10000)/10000; // 4 decimal places (11m accuracy at equator) + lon = parseInt(lon * 10000)/10000; // 4 decimal places + speed = parseInt(speed * 10)/10; // 1 decimal place + accuracy = parseInt(accuracy); + alt = parseInt(alt); + + // dispaly them in the top right corner + $('#app_name b').html(lat + '
' + lon); + + // update chase car interface + $('#cc_lat').text(lat); + $('#cc_lon').text(lon); + $('#cc_alt').text(alt + " m"); + $('#cc_accuracy').text(accuracy + " m"); + $('#cc_speed').text(speed + " m/s"); + + }; var twoZeroPad = function(n) { @@ -486,20 +481,22 @@ var format_time_friendly = function(start, end) { var format_coordinates = function(lat, lon, name) { var coords_text; var ua = navigator.userAgent.toLowerCase(); - + var span = document.createElement('span') + var a = document.createElement("a") + span.appendChild(a) // determine how to link the coordinates to a native app, if on a mobile device if(ua.indexOf('iphone') > -1) { - coords_text = '' + - roundNumber(lat, 5) + ', ' + roundNumber(lon, 5) +''; + a.href = 'maps://?q='+lat+','+lon } else if(ua.indexOf('android') > -1) { - coords_text = '' + - roundNumber(lat, 5) + ', ' + roundNumber(lon, 5) +''; + a.href = 'geo:'+lat+','+lon+'?q='+lat+','+lon+'('+name+')' } else { - coords_text = '' + - roundNumber(lat, 5) + ', ' + roundNumber(lon, 5) +''; + a.href = 'https://www.google.com/maps/search/?api=1&query='+lat+','+lon + a.target="_blank" + a.rel="noopener noreferrer" } + a.innerText = roundNumber(lat, 5) + ', ' + roundNumber(lon, 5) - return coords_text; + return span.innerHTML; }; // runs every second @@ -684,9 +681,7 @@ $(window).ready(function() { } else { wvar.box = null } - console.log("aaa") - console.log(wvar.box) - console.log("bbbb") + lhash_update(true); checkSize(); }); @@ -701,7 +696,6 @@ $(window).ready(function() { field.removeAttr('disabled'); e.removeClass('on').addClass('off'); - if(navigator.geolocation) navigator.geolocation.clearWatch(CHASE_enabled); CHASE_enabled = null; //CHASE_enabled = false; @@ -729,8 +723,7 @@ $(window).ready(function() { ChaseCar.updatePosition(callsign, { coords: { latitude: GPS_lat, longitude: GPS_lon, altitude: GPS_alt, speed: GPS_speed }}); } - if(navigator.geolocation) CHASE_enabled = navigator.geolocation.watchPosition(positionUpdateHandle, positionUpdateError); - //CHASE_enabled = true; + CHASE_enabled = true; // hide the blue man if(currentPosition && currentPosition.marker) map.removeLayer(currentPosition.marker); @@ -748,6 +741,23 @@ $(window).ready(function() { // turning the switch on } else { e.removeClass('off').addClass('on'); + // Remove any planned recovery check. + $("#sw_recovery_planned").removeClass('on').addClass('off'); + } + }); + // Logic to switch the recovery Planned button + $("#sw_recovery_planned").click(function() { + var e = $(this); + + // turning the switch off + if(e.hasClass('on')) { + e.removeClass('on').addClass('off'); + + // turning the switch on + } else { + e.removeClass('off').addClass('on'); + // Set recovery OK to false. + $("#sw_recovery_ok").removeClass('on').addClass('off'); } }); // Logic to switch the use car position button @@ -768,11 +778,21 @@ $(window).ready(function() { callsign = $(this).val().trim(); offline.set('callsign', callsign); // put in localStorage CHASE_listenerSent = false; + $('#recovery_callsign').val(callsign); }); + $("#recovery_callsign").on('change keyup', function() { + callsign = $(this).val().trim(); + offline.set('callsign', callsign); // put in localStorage + CHASE_listenerSent = false; + $('#cc_callsign').val(callsign); + }); + + // load value from localStorage callsign = offline.get('callsign'); $('#cc_callsign').val(callsign); + $('#recovery_callsign').val(callsign); // settings page @@ -821,6 +841,18 @@ $(window).ready(function() { else focusVehicle(null, true); break; case "opt_imperial": + if (map_scale){ + map_scale.remove() + } + // we need to remove and add the zoom controls so they end up in the same position + zoom_controls.remove() + if (on) { + map_scale = L.control.scale({position:'bottomright', imperial:true, metric:false}).addTo(map); + } else { + map_scale = L.control.scale({position:'bottomright', imperial:false}).addTo(map); + } + zoom_controls.addTo(map); + case "opt_haxis_hours": refreshUI(); break; @@ -945,10 +977,7 @@ $(window).ready(function() { }); navigator.geolocation.getCurrentPosition(positionUpdateHandle); - // check for location update every 30sec - //setInterval(positionUpdateHandle, 30000); - // immediatelly check for position - //positionUpdateHandle(); + if(navigator.geolocation) navigator.geolocation.watchPosition(positionUpdateHandle, positionUpdateError); } // weather feature @@ -1047,7 +1076,8 @@ $(window).ready(function() { $("header .search form").on('submit', function(e) { e.preventDefault(); - var text = $("header .search input[type='text']").val(); + var text = $("header .search input[type='text']").val().toUpperCase(); + $("header .search input[type='text']")[0].value = text; // update field in case it wasn't already uppercase if(text === wvar.query) return; @@ -1096,6 +1126,19 @@ function update_site(){ window.location.reload(true) } +function check_banner(){ + const bannerRequest = new Request("https://api.v2.sondehub.org/banner"); + fetch(bannerRequest) + .then(function(response){ return response.json()}) + .then(function(response){ + if (response['banner']) { + document.getElementsByTagName("header")[0].style.backgroundColor = "#ff6259" + document.getElementsByTagName("header")[0].style.borderColor = "#eb3d34" + document.getElementById("banner").innerText = response['banner'] + } + }) +} + function update_countdown(){ var date = new Date(0); time_remaining = (reload_end_time - new Date().getTime())/1000 @@ -1105,6 +1148,7 @@ function update_countdown(){ } check_version() +check_banner() update_check = setInterval(check_version, 15 * 60 * 1000) load_hash(); startAjax(); diff --git a/js/chasecar.lib.js b/js/chasecar.lib.js index 23b706c..1c470e2 100644 --- a/js/chasecar.lib.js +++ b/js/chasecar.lib.js @@ -45,8 +45,8 @@ ChaseCar.updatePosition = function(callsign, position) { ChaseCar.markRecovered = function(){ - // Distance limits reinstated 2021-12-04 - _run_checks = true; + // Remove range checks - 2025-01-24 + _range_check = false; _range_limit = 500000; // 200 km // Get the serial number to be marked recovered @@ -55,15 +55,15 @@ ChaseCar.markRecovered = function(){ _notes = $("#pr_notes").val().trim(); // Check it exists. - if(_serial.includes("chase") && _run_checks){ + if(_serial.includes("chase")){ $('#pr_last_report').text("Invalid sonde callsign."); return; } - _callsign = $("#cc_callsign").val().trim(); + _callsign = $("#recovery_callsign").val().trim(); if (_callsign == "" || _callsign == undefined || _callsign.length == 0) { - $('#pr_last_report').text("Enter a callsign in the chase-car section above!"); + $('#pr_last_report').text("Enter a callsign/name in the 'Recovered by' field!"); return; } @@ -109,7 +109,7 @@ ChaseCar.markRecovered = function(){ // Calculate the distance from the sonde _lookangles = calculate_lookangles(_chaser, _sonde); - if( (_lookangles.range > _range_limit ) && _run_checks){ + if( (_lookangles.range > _range_limit ) && _range_check){ $('#pr_last_report').text("Outside distance limit (500km)."); return; } @@ -120,28 +120,26 @@ ChaseCar.markRecovered = function(){ } } + // Logic to make recovery & planned mutually exclusive is in app.js + _recovered = $("#sw_recovery_ok").hasClass('on'); + _planned = $("#sw_recovery_planned").hasClass('on'); + var _doc = { "serial": _serial, "lat": _recov_lat, "lon": _recov_lon, "alt": 0.0, - "recovered": $("#sw_recovery_ok").hasClass('on'), + "recovered": _recovered, + "planned": _planned, "recovered_by": _callsign, "description": _notes }; - // Yes this is not the right way to do this... - // .. but it adds an extra bit of check. - var res = grecaptcha.getResponse(); - if (res == "" || res == undefined || res.length == 0) - { - $('#pr_last_report').text("Do Recaptcha first!"); - return; - } $('#pr_last_report').text("Submitting report..."); + $.ajax({ type: "PUT", url: ChaseCar.recovery_uri, diff --git a/js/sondehub.js b/js/sondehub.js index 15920e6..1296cc4 100644 --- a/js/sondehub.js +++ b/js/sondehub.js @@ -52,10 +52,10 @@ var focusID = 0; var receiverCanvas = null; -var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "iMet-1", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "MTS01", "WxR-301D", "chase"]; +var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "PS20", "iMet-1", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "MTS01", "WxR-301D", "chase"]; var sondeCodes = { "07":"iMet-1", "11":"LMS6-403", "13":"RS92", "14":"RS92", "17":"DFM-09", "18":"DFM-06", "19":"MRZ-N1", "22":"RS-11G", "23":"RS41", "24":"RS41", "34":"iMet-4", "35":"iMS-100", "38":"WxR-301D", "41":"RS41", "42":"RS41", "52":"RS92-NGP", - "54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "65":"MTS01", "77":"M10", "82":"LMS6-1680", "84":"iMet-54" + "54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "64":"PS20", "65":"MTS01", "77":"M10", "82":"LMS6-1680", "84":"iMet-54" }; var unsupportedSondeCodes = { "15":"PAZA-12M", "16":"PAZA-22", "20":"MK3", "21":"1524LA LORAN-C/GL5000", "26":"SRS-C34", "27":"AVK-MRZ", "28":"AVK–AK2-02", "29":"MARZ2-2", "30":"RS2-80", "33":"GTS1-2/GFE(L)", "45":"CF-06", "58":"AVK-BAR", @@ -168,7 +168,8 @@ var v1manufacturers = { "LMS6-400": "Lockheed Martin", "LMS6-1680": "Lockheed Martin", "M10": "Meteomodem", - "M20": "Meteomodem" + "M20": "Meteomodem", + "PS20": "Meteomodem" } var globalKeys = { @@ -569,7 +570,7 @@ function makeQuad(x, y, zoom) { // map type list -var osm = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { +var osm = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap contributors' }); @@ -618,6 +619,16 @@ var getlost = L.tileLayer('https://live.getlost.com.au/{z}/{x}/{y}.jpg', { }); getlost.id = "GetLost"; +var highSight = L.tileLayer('https://api.highsight.dev/v1/tiles/{z}/{x}/{y}?key=ARX3jYxzWotJVz7ZgdDiMQI18tZJ5WCk', { + attribution: '© HighSight', + maxZoom: 9, + minZoom: 3, + crossOrigin: true + }); + +highSight.id = "HighSight"; + + var baseMaps = { "Mapnik": osm, "DarkMatter": dark_matter, @@ -625,62 +636,11 @@ var baseMaps = { "Terrain": stamen_terrain, "Voyager": cartodb_voyager, "OpenTopoMap": opentopomap, + "HighSight": highSight } var selectedLayer = "Mapnik"; -// Tile load analytics - 2023-01-09 -// This code allows us to get some understanding of total tile loads across all users. -var tile_loads = { - "Mapnik": 0, - "DarkMatter": 0, - "WorldImagery": 0, - "Terrain": 0, - "Voyager": 0, - "OpenTopoMap": 0, -} - -// Add handlers to eadh tileload event to simply increment a counter. -// We don't need any more data than this. -osm.on('tileload', function() { tile_loads["Mapnik"]++ }); -dark_matter.on('tileload', function() { tile_loads["DarkMatter"]++ }); -worldimagery.on('tileload', function() { tile_loads["WorldImagery"]++ }); -stamen_terrain.on('tileload', function() { tile_loads["Terrain"]++ }); -cartodb_voyager.on('tileload', function() { tile_loads["Voyager"]++ }); -opentopomap.on('tileload', function() { tile_loads["OpenTopoMap"]++ }); - - -var last_sent_tile_loads = {} - -setInterval(function(){ - - temp_tile_loads = Object.assign({},tile_loads); - - // Check if the tile load count has changed. - // Using JSON stringify is a bit of a hack, but appropriate for this kind of job. - if(JSON.stringify(last_sent_tile_loads) == JSON.stringify(temp_tile_loads)){ - // Tile loads havent changed, do nothing, - } else { - // Tile loads have changed. Update the store, and send the data. - last_sent_tile_loads = Object.assign({},tile_loads); - - // Send! - - $.ajax({ - type: "PUT", - url: "https://api.v2.sondehub.org/tiles/count", - contentType: "application/json; charset=utf-8", - dataType: "json", - data: JSON.stringify({'client': clientID, 'tile_loads': last_sent_tile_loads}), - }); - } - -}, 60000) - - - - - // set map if in memory var maplayer = offline.get("map") @@ -721,7 +681,7 @@ function throttle_events(event) { function sub_to_nearby_sondes(){ let bounds = map.getBounds().pad(1); // expand by one viewport - let zoomed_out = map.getZoom() <= 6; + let zoomed_out = map.getZoom() <= 8; const sub_logging = false; if (zoomed_out){ // If we are fairly zooomed out - only give the slow feed @@ -908,10 +868,15 @@ function load() { L.control.status({ position: 'bottomright' }).addTo(map); // scale (would be better if integrated into attirbution bar) - L.control.scale({position:'bottomright', imperial:false}).addTo(map); + if (offline.get('opt_imperial')){ + map_scale = L.control.scale({position:'bottomright', imperial:true, metric:false}).addTo(map); + } else { + map_scale = L.control.scale({position:'bottomright', imperial:false}).addTo(map); + } + // zoom controls - new L.Control.Zoom({ position: 'bottomright' }).addTo(map); + zoom_controls = new L.Control.Zoom({ position: 'bottomright' }).addTo(map); // map selector layers = L.control.layers(baseMaps, null, {position: "topleft"}).addTo(map); @@ -1058,6 +1023,7 @@ function load() { } } updateZoom(); + recoveryPopup(); }); map.on('movestart', function() { @@ -1098,7 +1064,7 @@ function load() { var img = this._createImg(this.options['iconUrl']); var numdiv = document.createElement('div'); numdiv.setAttribute ( "class", "number" ); - numdiv.innerHTML = this.options['number'] || ''; + numdiv.innerText = this.options['number'] || ''; div.appendChild ( img ); div.appendChild ( numdiv ); this._setIconStyles(div, 'icon'); @@ -1369,6 +1335,11 @@ function habitat_data(jsondata, data_ages, current_timestamp, alternative) { } } + // Round frequency data + if(k === "frequency"){ + v = v.toFixed(3); + } + // Check if data is considered to be 'old' // Need to think about how to style old data. // Maybe make the text grey? @@ -1492,6 +1463,73 @@ function drawLOSPaths(vcallsign) { }); } +var recovery_popup = true; +function recoveryPopup(){ + // Conditions for showing this are: + // Zoom level >= 13 + // A sonde is in the map view area. (Use the subscribed sondes for this) + // If a user position is available, that position is within 500km of the sonde. + recovery_popup_limit = 500000; + + if( (map.getZoom() >= 13) && (clientTopic.length >= 2)){ + if(recovery_popup == true){ + // Logic to not show a popup if the centre of the map is > 500km from the viewer location, + _viewer_lat = parseFloat($('#cc_lat').text()) + _viewer_lon = parseFloat($('#cc_lon').text()) + + if ( (_viewer_lat != 0.0) && (_viewer_lon != 0.0) ){ + + _map_centre = { + 'lat':map.getCenter().lat, + 'lon':map.getCenter().lng, + 'alt':0.0 + }; + _viewer = { + 'lat': _viewer_lat, + 'lon': _viewer_lon, + 'alt': 0.0 + }; + + // Calculate the distance from the sonde + _lookangles = calculate_lookangles(_viewer, _map_centre); + + if(_lookangles.range > recovery_popup_limit){ + return; + } + } + + // If we're here, then we either don't have a valid viewer location, + // Or the viewer is within 500km. Show the popup. + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "block"; + } + return; + } + } + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "none"; + } + } + +function show_recoveries(){ + // Open the recovery reporting tab + if ($("#recoveriesbox").is(':visible') == false){ + $('.nav .recoveries').click(); + } + + // Hide the recovery notice + var recoveryNotice = document.getElementById("recovery_notice"); + if (recoveryNotice) { + recoveryNotice.style.display = "none"; + } + + // Don't show the recovery notice again. + recovery_popup = false; +} + + function focusVehicle(vcallsign, ignoreOpt) { if(!offline.get('opt_hilight_vehicle') && ignoreOpt === undefined) return; @@ -1623,9 +1661,9 @@ function formatDate(date,utc) { if(typeof utc != "undefined") { z = date.getTimezoneOffset() / -60; - return a+'-'+b+'-'+c+' '+e+':'+f+':'+g+" UTC"+((z<0)?"-":"+")+z; + return a+'-'+b+'-'+c+' '+e+':'+f+':'+g+" UTC"+((z<0)?"-":"+")+z; } else { - return a+'-'+b+'-'+c+' '+e+':'+f+':'+g; + return a+'-'+b+'-'+c+' '+e+':'+f+':'+g; } } @@ -2758,51 +2796,68 @@ function mapInfoBox_handle_path_old(vehicle, id) { div = document.createElement('div'); html = "
"; - html += "
"+data.serial+" ("+data.datetime+")
"; + html += "
"; html += "
"; - html += "
 "+format_coordinates(data.lat, data.lon, data.serial)+"
"; + html += "
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(data.alt)) : parseInt(data.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); + text_alt += " " + ((imp) ? 'ft':'m'); - html += "
Altitude: "+text_alt+"
"; - html += "
Time: "+formatDate(stringToDateUTC(data.datetime))+"
"; + html += "
Altitude:
"; + html += "
Time:
"; var value = vehicle.path_length; - html += "
Distance: "; + html += "
Distance: "; if(offline.get('opt_imperial')) { - html += Math.round(value*0.000621371192) + " mi"; + var distance = Math.round(value*0.000621371192) + " mi"; } else { - html += Math.round(value/10)/100 + " km"; + var distance = Math.round(value/10)/100 + " km"; } html += "
"; - html += "
Duration: " + format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + "
"; + html += "
Duration:
"; html += "
"; if (data.hasOwnProperty("humidity")) { - html += "
Relative Humidity: " + data.humidity + " %
"; + html += "
Relative Humidity: %
"; }; if (data.hasOwnProperty("temp")) { - html += "
Temperature External: " + data.temp + "°C
"; + html += "
Temperature External: °C
"; }; if (data.hasOwnProperty("comment")) { - html += "
Comment: " + data.comment + "
"; + html += "
Comment:
"; }; html += "
"; html += "
" if (data.hasOwnProperty("uploader_callsign")) { - html += "
" + data.uploader_callsign + "
"; + html += "
"; }; + + div.innerHTML = html; + div.getElementsByClassName("old_path_serial")[0].textContent = data.serial + div.getElementsByClassName("old_path_time")[0].textContent = "("+data.datetime+")" + div.getElementsByClassName("old_path_coords")[0].innerHTML = format_coordinates(data.lat, data.lon, data.serial) + div.getElementsByClassName("old_path_alt")[0].textContent = text_alt + div.getElementsByClassName("old_path_time_short")[0].textContent = formatDate(stringToDateUTC(data.datetime)) + div.getElementsByClassName("old_path_time_distance")[0].textContent = distance + div.getElementsByClassName("old_path_duration")[0].textContent = format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + div.getElementsByClassName("old_path_humidity")[0].textContent = data.humidity + div.getElementsByClassName("old_path_temp")[0].textContent = data.temp + div.getElementsByClassName("old_path_comment")[0].textContent = data.comment + + if (data.hasOwnProperty("uploader_callsign")) { + div.getElementsByClassName("old_path_uploader_callsign")[0].textContent = data.uploader_callsign + } + mapInfoBox.setContent(div); mapInfoBox.openOn(map); @@ -2833,87 +2888,90 @@ function mapInfoBox_handle_path_new(data, vehicle, date) { div = document.createElement('div'); html = "
"; - html += "
"+data.serial+" ("+date+")
"; + html += "
"; html += "
"; - html += "
 "+format_coordinates(data.lat, data.lon, data.serial)+"
"; + html += "
 
"; var imp = offline.get('opt_imperial'); var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(data.alt)) : parseInt(data.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); + text_alt += " " + ((imp) ? 'ft':'m'); - html += "
Altitude: "+text_alt+"
"; - html += "
Time: "+formatDate(stringToDateUTC(date))+"
"; + html += "
Altitude:
"; + html += "
Time:
"; var value = vehicle.path_length; - html += "
Distance: "; + html += "
Distance: "; if(offline.get('opt_imperial')) { - html += Math.round(value*0.000621371192) + " mi"; + var distance = Math.round(value*0.000621371192) + " mi"; } else { - html += Math.round(value/10)/100 + " km"; + var distance = Math.round(value/10)/100 + " km"; } - html += "
"; - html += "
Duration: " + format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + "
"; + html += "
"; + html += "
Duration:
"; - html += "
"; + html += "
"; - if (data.hasOwnProperty("batt")) { - html += "
Battery Voltage: " + data.batt + " V
"; - }; - if (data.hasOwnProperty("tx_frequency")) { - html += "
TX Frequency: " + data.tx_frequency + " MHz
"; - } else if (data.hasOwnProperty("frequency")) { - html += "
Frequency: " + data.frequency + " MHz
"; - }; - if (data.hasOwnProperty("humidity")) { - html += "
Relative Humidity: " + data.humidity + " %
"; - }; - if (data.hasOwnProperty("manufacturer")) { - html += "
Manufacturer: " + data.manufacturer + "
"; - }; - if (data.hasOwnProperty("pressure")) { - html += "
Pressure: " + data.pressure + " Pa
"; - }; - if (data.hasOwnProperty("sats")) { - html += "
Satellites: " + data.sats + "
"; - }; - if (data.hasOwnProperty("temp")) { - html += "
Temperature External: " + data.temp + "°C
"; - }; - if (data.hasOwnProperty("subtype")) { - html += "
Sonde Type: " + data.subtype + "
"; - } else if (data.hasOwnProperty("type")) { - html += "
Sonde Type: " + data.type + "
"; - }; + function prop(parent, field_name, description, unit="", fixed=undefined){ + if (data.hasOwnProperty(field_name)){ + var div = document.createElement("div") + var b = document.createElement("b") + b.innerText = description + ": " + var span = document.createElement("span") + var value = data[field_name]; + if (fixed){ + value = value.toFixed(fixed) + } + span.textContent = value + " " + unit + div.appendChild(b) + div.appendChild(span) + parent.appendChild(div) + return true + } + return false + } + var xdata_fields = document.createElement("div"); if (data.hasOwnProperty("xdata")) { html += "
"; html += "
" - html += "
XDATA: " + data.xdata + "
"; + html += "
"; if (data.hasOwnProperty("pressure")) { xdata_pressure = data.pressure; } else { xdata_pressure = 1100.0; } var tempXDATA = parseXDATA(data.xdata, xdata_pressure); + for (let field in tempXDATA) { if (tempXDATA.hasOwnProperty(field)) { + var xdiv = document.createElement("div"); + var xb = document.createElement("b"); + var xs = document.createElement("span"); + xdiv.appendChild(xb) + xdiv.appendChild(xs) + xdata_fields.appendChild(xdiv) if (field == "xdata_instrument") { - html += "
XDATA Instrument: " + tempXDATA.xdata_instrument.join(', ') + "
"; + xb.textContent = "XDATA Instrument: " + xs.textContent = tempXDATA.xdata_instrument.join(', ') } else { if (globalKeys.hasOwnProperty(field)) { if (globalSuffixes.hasOwnProperty(field)) { - html += "
" + globalKeys[field] + ": " + tempXDATA[field] + globalSuffixes[field] + "
"; + xb.textContent = globalKeys[field] + ": " + xs.textContent = tempXDATA[field] + globalSuffixes[field] } else { - html += "
" + globalKeys[field] + ": " + tempXDATA[field] + "
"; + xb.textContent = globalKeys[field] + ": " + xs.textContent = tempXDATA[field] } } else { if (globalSuffixes.hasOwnProperty(field)) { - html += "
" + guess_name(field) + ": " + tempXDATA[field] + globalSuffixes[field] + "
"; + xb.textContent = guess_name(field) + ": " + xs.textContent = tempXDATA[field] + globalSuffixes[field] } else { - html += "
" + guess_name(field) + ": " + tempXDATA[field] + "
"; + xb.textContent = guess_name(field) + ": " + xs.textContent = tempXDATA[field] } } } @@ -2944,11 +3002,48 @@ function mapInfoBox_handle_path_new(data, vehicle, date) { callsign_list.push(_new_call); // catch cases where there are no fields } - callsign_list = callsign_list.join("
"); - - html += callsign_list + "
"; + callsign_list = callsign_list.join("\n"); + callsign_span = document.createElement("span") + callsign_span.innerText = callsign_list div.innerHTML = html; + div.appendChild(callsign_span) + + div.getElementsByClassName("new_path_serial")[0].textContent = data.serial + div.getElementsByClassName("new_path_time")[0].textContent = "("+date+")" + + div.getElementsByClassName("new_path_coords")[0].innerHTML = format_coordinates(data.lat, data.lon, data.serial); // for compat we are generating safeish url in format_coordinates + + div.getElementsByClassName("new_path_alt")[0].textContent = text_alt + div.getElementsByClassName("new_path_time_short")[0].textContent = formatDate(stringToDateUTC(date)) + + + div.getElementsByClassName("new_path_time_distance")[0].textContent = distance + + div.getElementsByClassName("new_path_duration")[0].textContent = format_time_friendly(vehicle.start_time, convert_time(vehicle.curr_position.gps_time)) + + + var prop_parent = div.getElementsByClassName("new_path_props")[0] + prop(prop_parent,"batt", "Battery Voltage", "V"); + + if (!prop(prop_parent,"tx_frequency", "TX Frequency", "MHz", 3)){ + prop(prop_parent,"frequency", "Frequency", "MHz", 3); + } + + + prop(prop_parent,"humidity", "Relative Humidity","%", 1); + prop(prop_parent,"manufacturer", "Manufacturer"); + prop(prop_parent,"pressure", "Pressure", "Pa", 1); + prop(prop_parent,"sats", "Satellites"); + prop(prop_parent,"temp", "Temperature External", "°C", 1); + + if (!prop(prop_parent,"subtype", "Sonde Type")){ + prop(prop_parent,"type", "Sonde Type") + } + prop(prop_parent,"xdata", "XDATA"); + if (data.hasOwnProperty("xdata")) { + div.getElementsByClassName("new_path_xdata_prop")[0].appendChild(xdata_fields) + } mapInfoBox.setContent(div); mapInfoBox.openOn(map); @@ -4424,8 +4519,10 @@ function updateCurrentPosition(lat, lon) { } else { currentPosition.lat = lat; currentPosition.lon = lon; - currentPosition.marker.addTo(map); - currentPosition.marker.setLatLng(latlng); + if (!CHASE_enabled){ + currentPosition.marker.addTo(map); + currentPosition.marker.setLatLng(latlng); + } } } @@ -4675,6 +4772,13 @@ function updateRecoveryMarker(recovery) { _recovery_icon = host_url + markers_url + "payload-not-recovered.png"; } + // Override icon if 'is planned' field exists and is true + if(recovery.hasOwnProperty('planned')){ + if(recovery.planned == true){ + _recovery_icon = host_url + markers_url + "payload-recovery-planned.png"; + } + } + recoveryIcon = new L.icon({ iconUrl: _recovery_icon, iconSize: [17, 19], @@ -4694,23 +4798,36 @@ function updateRecoveryMarker(recovery) { div = document.createElement('div'); + _recovered_text = recovery.recovered ? " Recovered" : " Not Recovered"; + + // Override text is planned field exists and is true + if(recovery.hasOwnProperty('planned')){ + if(recovery.planned == true){ + _recovered_text = " Recovery Planned"; + } + } + html = "
"; - html += "
"+recovery.serial+(recovery.recovered ? " Recovered" : " Not Recovered")+"
"; + html += "
"; html += "
"; html += "
 "+format_coordinates(recovery.lat, recovery.lon, recovery.serial)+"
"; - var imp = offline.get('opt_imperial'); - var text_alt = Number((imp) ? Math.floor(3.2808399 * parseInt(recovery.alt)) : parseInt(recovery.alt)).toLocaleString("us"); - text_alt += " " + ((imp) ? 'ft':'m'); - - html += "
Time: "+formatDate(stringToDateUTC(recovery.datetime))+"
"; - html += "
Reported by: "+recovery.recovered_by+"
"; - html += "
Notes: "+$('
').text(recovery.description).html()+"
"; - html += "
Flight Path: "+recovery.serial+"
"; + html += "
Time: 
"; + html += "
Reported by: 
"; + html += "
Notes: 
"; + html += "
Flight Path: 
"; html += "
"; div.innerHTML = html; + div.getElementsByClassName("recovery_text")[0].textContent = recovery.serial+_recovered_text + div.getElementsByClassName("recovery_time")[0].textContent = formatDate(stringToDateUTC(recovery.datetime)) + div.getElementsByClassName("recovery_by")[0].textContent = recovery.recovered_by + div.getElementsByClassName("recovery_desc")[0].textContent = recovery.description + div.getElementsByClassName("recovery_path")[0].textContent = recovery.serial + div.getElementsByClassName("recovery_path")[0].onclick = function(){ + showRecoveredMap(recovery.serial) + } recovery.infobox.setContent(div); @@ -4750,6 +4867,7 @@ function updateRecoveryMarker(recovery) { recovery.recovered = r[i].recovered; recovery.description = r[i].description; recovery.datetime = r[i].datetime; + recovery.planned = r[i].planned; recovery.fresh = true; updateRecoveryMarker(recovery); @@ -4803,8 +4921,17 @@ function updateRecoveryPane(r){ recoveries[r_index] = {marker: null, infobox: null}; } + _recovered_text = r[i].recovered ? " Recovered by " : " Not Recovered by "; + + // Override text is planned field exists and is true + if(r[i].hasOwnProperty('planned')){ + if(r[i].planned == true){ + _recovered_text = " Recovery Planned by "; + } + } + html += "
"; - html += "
"+r[i].serial+(r[i].recovered ? " Recovered by " : " Not Recovered by ")+r[i].recovered_by+"
"; + html += "
"+r[i].serial+(_recovered_text)+r[i].recovered_by+"
"; html += "
 "+format_coordinates(lat, lon, r[i].serial)+"
"; var imp = offline.get('opt_imperial'); diff --git a/js/station.js b/js/station.js index 74f053c..6ad8857 100644 --- a/js/station.js +++ b/js/station.js @@ -174,7 +174,17 @@ function drawHistorical (data, station) { } } - var popup = L.popup(); + var popup = new L.popup({ autoClose: false, closeOnClick: false }).setContent(serial); + + if (!recovered) { + var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "white", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + } else { + var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "grey", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + } + + marker.bindPopup(popup); + + div = document.createElement('div'); html = "
"; html += "
"+serial+" ("+time+")
"; @@ -187,40 +197,60 @@ function drawHistorical (data, station) { html += "
Altitude: "+text_alt+"
"; html += "
Time: "+formatDate(stringToDateUTC(time))+"
"; - - if (landing.hasOwnProperty("type")) { - html += "
Sonde Type: " + landing.type + "
"; - }; + html += "
Sonde Type: Unknown
" html += "
"; - if (recovered) { - html += "
"+(recovery_info.recovered ? "Recovered by " : "Not Recovered by ")+recovery_info.recovered_by+"
"; - html += "
Recovery time: "+formatDate(stringToDateUTC(recovery_info.datetime))+"
"; - html += "
Recovery location: "+recovery_info.position[1]+", "+recovery_info.position[0] + "
"; - html += "
Recovery notes: "+recovery_info.description+"
"; - - html += "
"; - } + html += "
"; + html += "
"; + html += "
Reported at: 
"; + html += "
Reported by: 
"; + html += "
Notes: 
"; + html += "
"; + html += "
"; - html += "
Show Full Flight Path: " + serial + "
"; + html += "
Show Full Flight Path: 
"; html += "
"; html += "
" + html += "
" + html += "
"; - if (landing.hasOwnProperty("uploader_callsign")) { - html += "
Last received by: " + landing.uploader_callsign.toLowerCase() + "
"; + div.innerHTML = html; + + + div.getElementsByClassName("recovery_path")[0].textContent = serial + div.getElementsByClassName("recovery_path")[0].onclick = function(){ + showRecoveredMap(serial) + } + + if (landing.hasOwnProperty("type")) { + div.getElementsByClassName("landing_sonde_type")[0].textContent = landing.type; }; - popup.setContent(html); + if (landing.hasOwnProperty("uploader_callsign")) { + div.getElementsByClassName("landing_uploader_callsign")[0].textContent = "Last received by: " + landing.uploader_callsign.toLowerCase(); + }; - if (!recovered) { - var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "white", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + if (recovered) { + _recovered_text = recovery_info.recovered ? " Recovered" : "Not Recovered"; + + // Override text is planned field exists and is true + if(recovery_info.hasOwnProperty('planned')){ + if(recovery_info.planned == true){ + _recovered_text = " Recovery Planned"; + } + } + div.getElementsByClassName("recovery_text")[0].textContent = recovery_info.serial + _recovered_text; + div.getElementsByClassName("recovery_time")[0].textContent = formatDate(stringToDateUTC(recovery_info.datetime)); + div.getElementsByClassName("recovery_by")[0].textContent = recovery_info.recovered_by; + div.getElementsByClassName("recovery_desc")[0].textContent = recovery_info.description; } else { - var marker = L.circleMarker([landing.lat, landing.lon], {fillColor: "grey", color: iconColour, weight: 3, radius: 5, fillOpacity:1}); + div.getElementsByClassName("recovery_section")[0].style.display = "none"; } - marker.bindPopup(popup); + popup.setContent(div); + marker.addTo(map); marker.bringToBack(); @@ -602,26 +632,26 @@ function launchSitePredictions(times, station, properties, marker, id) { var date = new Date(); var time = times[i].split(":"); if (time[0] != 0) { - date.setDate(date.getDate() + (7 + time[0] - date.getDay()) % 7); + date.setUTCDate(date.getUTCDate() + (7 + time[0] - date.getUTCDay()) % 7); } date.setUTCHours(time[1]); date.setUTCMinutes(time[2]); - date.setSeconds(0); - date.setMilliseconds(0); + date.setUTCSeconds(0); + date.setUTCMilliseconds(0); // launch time 45 minutes before target time - date.setMinutes( date.getMinutes() - 45 ); + date.setUTCMinutes( date.getUTCMinutes() - 45 ); while (date < now) { if (time[0] == 0) { - date.setDate(date.getDate() + 1); + date.setUTCDate(date.getUTCDate() + 1); } else { - date.setDate(date.getDate() + 7); + date.setUTCDate(date.getUTCDate() + 7); } } if (day > 0) { if (time[0] == 0) { - date.setDate(date.getDate() + day); + date.setUTCDate(date.getUTCDate() + day); } else { - date.setDate(date.getDate() + (7*day)); + date.setUTCDate(date.getUTCDate() + (7*day)); } } if (count < maxCount) { @@ -738,15 +768,24 @@ function plotPrediction (data, dates, marker, properties) { icon: new L.NumberedDivIcon({number: dates.indexOf(data.request.launch_datetime)+1}) }).addTo(map); + var coords_text = format_coordinates(landingPoint.latitude, landingLongitude, "Prediction"); + var landingTime = new Date(landingPoint.datetime); if (properties[3] != "" && properties[4] != "") { var landingTooltip = "Time: " + landingTime.toLocaleString() + "
Model Dataset: " + data.request.dataset + + "
Prediction: " + coords_text + "
Model Assumptions:
- " + data.request.ascent_rate + "m/s ascent
- " + data.request.burst_altitude + "m burst altitude (" + properties[3] + " samples)
- " + data.request.descent_rate + "m/s descent (" + properties[4] + " samples)"; } else { var landingTooltip = "Time: " + landingTime.toLocaleString() + "
Model Dataset: " + data.request.dataset + + "
Prediction:" + coords_text + "
Model Assumptions:
- " + data.request.ascent_rate + "m/s ascent
- " + data.request.burst_altitude + "m burst altitude
- " + data.request.descent_rate + "m/s descent"; } - plot.landingMarker.bindTooltip(landingTooltip, {offset: [13,-28]}); + + plot.landingMarker.bindPopup(landingTooltip, { autoClose: false, closeOnClick: false }) + .on('click', function (e) { + this.openPopup(); + }); + } function showPrediction(url) { @@ -917,6 +956,7 @@ function generateLaunchSites() { } popupContent += "
Know when this site launches? Contribute here"; + popupContent += "
Site Code: " + key; // Generate view historical button popupContent += "
";