diff --git a/static/css/chasemapper.css b/static/css/chasemapper.css index 769bf39..3cd9212 100644 --- a/static/css/chasemapper.css +++ b/static/css/chasemapper.css @@ -92,4 +92,20 @@ html, body, #map { width:45px; height:45px; font-size:20px; -} \ No newline at end of file +} + + +.custom_label { + background: rgba(0, 0, 0, 0) !important; + border: none !important; + font-size: 12px; + font-weight: bold; + color: black; + text-shadow: + -1px -1px 2px #FFF, + 1px -1px 2px #FFF, + -1px 1px 2px #FFF, + 1px 1px 2px #FFF; + box-shadow: none !important; +} + diff --git a/static/img/car-green-flip.png b/static/img/car-green-flip.png new file mode 100644 index 0000000..f157673 Binary files /dev/null and b/static/img/car-green-flip.png differ diff --git a/static/img/car-green.png b/static/img/car-green.png new file mode 100644 index 0000000..7645e78 Binary files /dev/null and b/static/img/car-green.png differ diff --git a/static/img/car-red-flip.png b/static/img/car-red-flip.png new file mode 100644 index 0000000..e9e9474 Binary files /dev/null and b/static/img/car-red-flip.png differ diff --git a/static/img/car-red.png b/static/img/car-red.png new file mode 100644 index 0000000..07d1641 Binary files /dev/null and b/static/img/car-red.png differ diff --git a/static/img/car-yellow-flip.png b/static/img/car-yellow-flip.png new file mode 100644 index 0000000..bdb84da Binary files /dev/null and b/static/img/car-yellow-flip.png differ diff --git a/static/img/car-yellow.png b/static/img/car-yellow.png new file mode 100644 index 0000000..8664f06 Binary files /dev/null and b/static/img/car-yellow.png differ diff --git a/static/js/habitat.js b/static/js/habitat.js new file mode 100644 index 0000000..04726ac --- /dev/null +++ b/static/js/habitat.js @@ -0,0 +1,178 @@ +// Habitat data scraping functions + +// URL to scrape recent vehicle position data from. +// TODO: Allow adjustment of the number of positions to request. +var spacenearus_url = "http://spacenear.us/tracker/datanew.php?mode=2hours&type=positions&format=json&max_positions=100&position_id="; +// Record of the last position ID, so we only request new data. +var spacenearus_last_position_id = 0; +// Keep track of whether an asynchronous AJAX request is in progress. +// Not really sure if this is necessary. +var snear_request_running = false; + +// Store for vehicle data. +var habitat_vehicles = {}; +// Only add chase cars which are (initially) within this range limit (km). +var habitat_vehicle_max_range = 800.0; + + +function process_habitat_vehicles(data){ + // Check we have a 'valid' response to process. + if (data === null || + !data.positions || + !data.positions.position || + !data.positions.position.length) { + snear_request_running = false; + return; + } + + data.positions.position.forEach(function(position){ + // Update the highest position ID, so we don't request old data. + if (position.position_id > spacenearus_last_position_id){ + spacenearus_last_position_id = position.position_id; + } + + var vcallsign = position.vehicle; + + // Check this isn't our callsign. + // If it is, don't process it. + if (vcallsign.startsWith(chase_config.habitat_call)){ + return; + } + + // Determine if the vehicle is a chase car. + // This is denoted by _chase at the end of the callsign. + if(vcallsign.search(/(chase)/i) != -1) { + + var v_lat = parseFloat(position.gps_lat); + var v_lon = parseFloat(position.gps_lon); + var v_alt = parseFloat(position.gps_alt); + + // If the vehicle is already known to us, then update it position. + // Update any existing entries (even if the range is above the threshold) + if (habitat_vehicles.hasOwnProperty(vcallsign)){ + + // Only update if the position ID of this position is newer than that last seen. + if (habitat_vehicles[vcallsign].position_id < position.position_id){ + //console.log("Updating: " + vcallsign); + // Update the position ID. + habitat_vehicles[vcallsign].position_id = position.position_id; + + // Since we don't always get a heading with the vehicle position, calculate it. + var old_v_pos = {lat:habitat_vehicles[vcallsign].latest_data[0], + lon: habitat_vehicles[vcallsign].latest_data[1], + alt:habitat_vehicles[vcallsign].latest_data[2]}; + var new_v_pos = {lat: v_lat, lon:v_lon, alt:v_alt}; + habitat_vehicles[vcallsign].heading = calculate_lookangles(old_v_pos, new_v_pos).bearing; + + // Update the position data. + habitat_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt]; + + // Update the marker position. + habitat_vehicles[vcallsign].marker.setLatLng(habitat_vehicles[vcallsign].latest_data).update(); + + // Rotate/replace the icon to match the bearing. + var _car_heading = habitat_vehicles[vcallsign].heading - 90.0; + if (_car_heading<=90.0){ + habitat_vehicles[vcallsign].marker.setIcon(habitat_car_icons[habitat_vehicles[vcallsign].colour]); + habitat_vehicles[vcallsign].marker.setRotationAngle(_car_heading); + }else{ + // We are travelling West - we need to use the flipped car icon. + _car_heading = _car_heading - 180.0; + habitat_vehicles[vcallsign].marker.setIcon(habitat_car_icons_flipped[habitat_vehicles[vcallsign].colour]); + habitat_vehicles[vcallsign].marker.setRotationAngle(_car_heading); + } + return; + } + + + // No need to go any further. + + return; + } + + // Otherwise, we need to decide if we're going to add it or not. + // Determine the vehicle distance from our current position. + var v_pos = {lat: v_lat, lon:v_lon, alt:v_alt}; + if (chase_car_position.marker === "NONE"){ + var my_pos = {lat:chase_config.default_lat, lon:chase_config.default_lon, alt:0}; + }else{ + var my_pos = {lat:chase_car_position.latest_data[0], lon:chase_car_position.latest_data[1], alt:chase_car_position.latest_data[2]}; + } + var v_range = calculate_lookangles(my_pos, v_pos).range/1000.0; + + // If the range is less than the threshold, add it to our list of chase vehicles. + if(v_range < habitat_vehicle_max_range){ + //console.log("Adding: " + vcallsign); + habitat_vehicles[vcallsign] = {}; + // Initialise a few default values + habitat_vehicles[vcallsign].heading = 90; + habitat_vehicles[vcallsign].latest_data = [v_lat, v_lon, v_alt]; + habitat_vehicles[vcallsign].position_id = position.position_id; + + // Get an index for the car icon. This is incremented for each vehicle, + // giving each a different colour. + habitat_vehicles[vcallsign].colour = car_colour_values[car_colour_idx]; + car_colour_idx = (car_colour_idx+1)%car_colour_values.length; + + // Create marker + habitat_vehicles[vcallsign].marker = L.marker(habitat_vehicles[vcallsign].latest_data, + {title:vcallsign, + icon: habitat_car_icons[habitat_vehicles[vcallsign].colour], + rotationOrigin: "center center"}) + .addTo(map); + // Keep our own record of if this marker has been added to a map, + // as we shouldn't be using the private _map property of the marker object. + habitat_vehicles[vcallsign].onmap = true; + + // Add tooltip, with custom CSS which removes all tooltip borders, and adds a text shadow. + habitat_vehicles[vcallsign].marker.bindTooltip(vcallsign, + {permanent: true, + direction: 'center', + offset:[0,25], + className:'custom_label'}).openTooltip(); + } + } + + }); + + snear_request_running = false; +} + +// Request the latest 100 vehicle positions from spacenear.us +function get_habitat_vehicles(){ + var snear_request_url = spacenearus_url + spacenearus_last_position_id; + + if(!snear_request_running){ + snear_request_running = true; + $.ajax({ + url: snear_request_url, + dataType: 'json', + timeout: 15000, + async: true, // Yes, this is deprecated... + success: function(data) { + process_habitat_vehicles(data); + } + }); + } +} + + +// Show/Hide all vehicles. +function show_habitat_vehicles(){ + var state = document.getElementById("showOtherCars").checked; + for (_car in habitat_vehicles){ + // Add to map, if its not already on there. + if(state){ + if(!habitat_vehicles[_car].onmap){ + habitat_vehicles[_car].marker.addTo(map); + habitat_vehicles[_car].onmap = true; + } + } else{ + if(habitat_vehicles[_car].onmap){ + habitat_vehicles[_car].marker.remove(); + habitat_vehicles[_car].onmap = false; + } + } + } + +} \ No newline at end of file diff --git a/static/js/utils.js b/static/js/utils.js index f14496a..9c9cf71 100644 --- a/static/js/utils.js +++ b/static/js/utils.js @@ -42,12 +42,14 @@ var burstIcon = L.icon({ iconAnchor: [10,10] }); +// Abort prediction icon (red) var abortIcon = L.icon({ iconUrl: "/static/img/target-red.png", iconSize: [20,20], iconAnchor: [10,10] }); +// Icons for our own chase car. var carIcon = L.icon({ iconUrl: "/static/img/car-blue.png", iconSize: [55,25], @@ -60,6 +62,25 @@ var carIconFlip = L.icon({ iconAnchor: [27,12] // Revisit this }); +// Habitat (or APRS?) sourced chase car icons. +var car_colour_values = ['red', 'green', 'yellow']; +var car_colour_idx = 0; +var habitat_car_icons = {}; +var habitat_car_icons_flipped = {}; +for (_col in car_colour_values){ + habitat_car_icons[car_colour_values[_col]] = L.icon({ + iconUrl: "/static/img/car-"+car_colour_values[_col]+".png", + iconSize: [55,25], + iconAnchor: [27,12] // Revisit this + }); + + habitat_car_icons_flipped[car_colour_values[_col]] = L.icon({ + iconUrl: "/static/img/car-"+car_colour_values[_col]+"-flip.png", + iconSize: [55,25], + iconAnchor: [27,12] // Revisit this + }); +} + // calculates look angles between two points // format of a and b should be {lon: 0, lat: 0, alt: 0} diff --git a/templates/index.html b/templates/index.html index 14fe8e6..b04278e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -30,6 +30,7 @@ + @@ -53,7 +54,8 @@ pred_update_rate: 15, pred_model: 'Disabled', show_abort: true, // Show a prediction of an 'abort' paths (i.e. if the balloon bursts *now*) - offline_tile_layers: [] + offline_tile_layers: [], + habitat_call: 'N0CALL' }; // Object which will contain balloon markers and traces. @@ -87,9 +89,15 @@ // Other markers which may be added. (TBD, probably other chase car positions via the LoRa payload?) var misc_markers = {}; + // Habitat chase cars + var habitat_chase_cars = {}; + var updateSettings; var setChaseCarTrack; + // Leaflet map instance. + var map; + $(document).ready(function() { // Use the 'chasemapper' namespace for all of our traffic namespace = '/chasemapper'; @@ -209,7 +217,7 @@ // Sonde position Map // Setup a basic Leaflet map - var map = L.map('map').setView([chase_config.default_lat, chase_config.default_lon], 8); + map = L.map('map').setView([chase_config.default_lat, chase_config.default_lon], 8); // Add OSM Map. var osm_map = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { @@ -776,6 +784,7 @@ }); + // Data age update. var age_update_rate = 500; window.setInterval(function () { // Update the data age displays. @@ -814,7 +823,16 @@ }else{ $("#pred_age").text("Predictions: " + pred_data_age.toFixed(1)+"s"); } - }, age_update_rate); + }, age_update_rate); + + + // Habitat Chase Car Position Grabber + var habitat_update_rate = 20000; + window.setInterval(function(){ + if(document.getElementById("showOtherCars").checked){ + get_habitat_vehicles(); + } + }, habitat_update_rate); }); @@ -885,6 +903,9 @@

Habitat

+
+ Show Nearby Chase-Cars: +
Enable Chase-Car Position Upload