Skip to content

Commit 2a7a769

Browse files
authored
Merge pull request projecthorus#127 from projecthorus/testing
Launch Site Predictions
2 parents 700d5bd + a4109b3 commit 2a7a769

File tree

4 files changed

+1021
-730
lines changed

4 files changed

+1021
-730
lines changed

css/main.css

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,19 @@ header .search form input[type='submit'] {
767767
filter: drop-shadow(1px 0px 0 white) drop-shadow(0px 1px 0 white) drop-shadow(-1px -0px 0 white) drop-shadow(-0px -1px 0 white);
768768
}
769769

770+
.leaflet-marker-icon .number{
771+
position: relative;
772+
top: -41px;
773+
font-size: 12px;
774+
width: 25px;
775+
text-align: center;
776+
}
777+
778+
.leaflet-div-icon {
779+
background: transparent !important;
780+
border: none !important;
781+
}
782+
770783
@media only screen and (min-width: 900px) {
771784
}
772785

img/markers/marker_hole.png

4.48 KB
Loading

js/tracker.js

Lines changed: 302 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,20 @@ var receivers = [];
2828
var recovery_names = [];
2929
var recoveries = [];
3030

31+
var launchPredictions = {};
32+
3133
var launches = null;
3234
var receiverCanvas = null;
3335

3436
var sondePrefix = ["RS92", "RS92-SGP", "RS92-NGP", "RS41", "RS41-SG", "RS41-SGP", "RS41-SGM", "DFM", "DFM06", "DFM09", "DFM17", "M10", "M20", "iMet-4", "iMet-54", "LMS6", "LMS6-400", "LMS6-1680", "iMS-100", "MRZ", "chase"];
37+
var sondeCodes = {
38+
"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", "41":"RS41", "42":"RS41", "52":"RS92-NGP",
39+
"54":"DFM-17", "62":"MRZ-3MK", "63":"M20", "77":"M10", "82":"LMS6-1680", "84":"iMet-54"
40+
};
41+
var unsupportedSondeCodes = {
42+
"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",
43+
"59":"M2K2-R", "68":"AVK-RZM-2", "69":"MARL-A/Vektor-M-RZM-2", "73":"MARL-A", "78":"RS90", "80":"RS92", "88":"MARL-A/Vektor-M-MRZ", "89":"MARL-A/Vektor-M-BAR", "97":"iMet-2", "99":"iMet-2"
44+
};
3545

3646
var got_positions = false;
3747
var zoomed_in = false;
@@ -563,6 +573,34 @@ function load() {
563573
liveData();
564574
};
565575

576+
L.NumberedDivIcon = L.Icon.extend({
577+
options: {
578+
iconUrl: host_url + markers_url + "marker_hole.png",
579+
number: '',
580+
shadowUrl: null,
581+
iconSize: new L.Point(25, 41),
582+
iconAnchor: new L.Point(13, 41),
583+
popupAnchor: new L.Point(0, -33),
584+
className: 'leaflet-div-icon'
585+
},
586+
587+
createIcon: function () {
588+
var div = document.createElement('div');
589+
var img = this._createImg(this.options['iconUrl']);
590+
var numdiv = document.createElement('div');
591+
numdiv.setAttribute ( "class", "number" );
592+
numdiv.innerHTML = this.options['number'] || '';
593+
div.appendChild ( img );
594+
div.appendChild ( numdiv );
595+
this._setIconStyles(div, 'icon');
596+
return div;
597+
},
598+
599+
createShadow: function () {
600+
return null;
601+
}
602+
});
603+
566604
map.whenReady(callBack);
567605

568606
// animate-in the timebox,
@@ -609,39 +647,279 @@ function setTimeValue() {
609647
}, 100);
610648
}
611649

650+
function launchSitePredictions(times, station, properties, marker) {
651+
var popup = launches.getLayer(marker).getPopup();
652+
var popupContent = popup.getContent();
653+
var popupContentSplit = popupContent.split("<button onclick='launchSitePredictions(")[0];
654+
popupContentSplit += "<img style='width:60px;height:20px' src='img/hab-spinner.gif' />";
655+
popup.setContent(popupContentSplit);
656+
if (popupContent.includes("Delete</button>")) {
657+
deletePredictions(marker);
658+
popupContent = popupContent.split("<button onclick='deletePredictions(")[0];
659+
}
660+
times = times.split(",");
661+
position = station.split(",");
662+
properties = properties.split(":");
663+
var now = new Date();
664+
var maxCount = 7
665+
var count = 0;
666+
var day = 0;
667+
var dates = [];
668+
while (day < 8) {
669+
for (var i = 0; i < times.length; i++) {
670+
var date = new Date();
671+
var time = times[i].split(":");
672+
if (time[0] != 0) {
673+
date.setDate(date.getDate() + (7 + time[0] - date.getDay()) % 7);
674+
}
675+
date.setUTCHours(time[1]);
676+
date.setUTCMinutes(time[2]);
677+
date.setSeconds(0);
678+
date.setMilliseconds(0);
679+
date.setMinutes( date.getMinutes() - 45 );
680+
while (date < now) {
681+
if (time[0] == 0) {
682+
date.setDate(date.getDate() + 1);
683+
} else {
684+
date.setDate(date.getDate() + 7);
685+
}
686+
}
687+
if (day > 0) {
688+
if (time[0] == 0) {
689+
date.setDate(date.getDate() + day);
690+
} else {
691+
date.setDate(date.getDate() + (7*day));
692+
}
693+
}
694+
if (count < maxCount) {
695+
if (((date - now) / 36e5) < 170) {
696+
dates.push(date.toISOString().split('.')[0]+"Z");
697+
count += 1;
698+
}
699+
}
700+
}
701+
day += 1;
702+
}
703+
dates.sort();
704+
var completed = 0;
705+
for (var i = 0; i < dates.length; i++) {
706+
var lon = ((360 + (position[1] % 360)) % 360)
707+
var url = "https://predict.cusf.co.uk/api/v1/?launch_latitude=" + position[0] + "&launch_longitude=" + lon + "&launch_datetime=" + dates[i] + "&ascent_rate=" + properties[0] + "&burst_altitude=" + properties[2] + "&descent_rate=" + properties[1];
708+
//var url = "https://api.v2.sondehub.org/tawhiri?launch_latitude=" + position[0] + "&launch_longitude=" + lon + "&launch_datetime=" + dates[i] + "&ascent_rate=" + properties[0] + "&burst_altitude=" + properties[2] + "&descent_rate=" + properties[1];
709+
showPrediction(url).done(handleData).fail(handleError);
710+
}
711+
function handleData(data) {
712+
completed += 1;
713+
plotPrediction(data, dates, marker, properties);
714+
if (completed == dates.length) {
715+
popupContent += "<button onclick='deletePredictions(" + marker + ")' style='margin-bottom:0;'>Delete</button>";
716+
popup.setContent(popupContent);
717+
}
718+
}
719+
function handleError(error) {
720+
completed += 1;
721+
if (completed == dates.length) {
722+
popupContent += "<button onclick='deletePredictions(" + marker + ")' style='margin-bottom:0;'>Delete</button>";
723+
popup.setContent(popupContent);
724+
}
725+
}
726+
}
727+
728+
function plotPrediction (data, dates, marker, properties) {
729+
if (!launchPredictions.hasOwnProperty(marker)) {
730+
launchPredictions[marker] = {};
731+
}
732+
launchPredictions[marker][dates.indexOf(data.request.launch_datetime)+1] = {};
733+
plot = launchPredictions[marker][dates.indexOf(data.request.launch_datetime)+1];
734+
735+
ascent = data.prediction[0].trajectory;
736+
descent = data.prediction[1].trajectory;
737+
var predictionPath = [];
738+
for (var i = 0; i < ascent.length; i++) {
739+
if (ascent[i].longitude > 180.0) {
740+
var longitude = ascent[i].longitude - 360.0;
741+
} else {
742+
var longitude = ascent[i].longitude;
743+
}
744+
predictionPath.push([ascent[i].latitude, longitude]);
745+
};
746+
for (var x = 0; x < descent.length; x++) {
747+
if (descent[x].longitude > 180.0) {
748+
var longitude = descent[x].longitude - 360.0;
749+
} else {
750+
var longitude = descent[x].longitude;
751+
}
752+
predictionPath.push([descent[x].latitude, longitude]);
753+
};
754+
var burstPoint = ascent[ascent.length-1];
755+
var landingPoint = descent[descent.length-1];
756+
757+
plot.predictionPath = new L.polyline(predictionPath, {color: 'red'}).addTo(map);
758+
759+
burstIconImage = host_url + markers_url + "balloon-pop.png";
760+
761+
burstIcon = new L.icon({
762+
iconUrl: burstIconImage,
763+
iconSize: [20,20],
764+
iconAnchor: [10, 10],
765+
});
766+
767+
if (burstPoint.longitude > 180.0) {
768+
var burstLongitude = burstPoint.longitude - 360.0;
769+
} else {
770+
var burstLongitude = burstPoint.longitude;
771+
}
772+
773+
plot.burstMarker = new L.marker([burstPoint.latitude, burstLongitude], {
774+
icon: burstIcon
775+
}).addTo(map);
776+
777+
var burstTime = new Date(burstPoint.datetime);
778+
var burstTooltip = "<b>Time: </b>" + burstTime.toLocaleString() + "<br><b>Altitude: </b>" + Math.round(burstPoint.altitude) + "m";
779+
plot.burstMarker.bindTooltip(burstTooltip, {offset: [5,0]});
780+
781+
if (landingPoint.longitude > 180.0) {
782+
var landingLongitude = landingPoint.longitude - 360.0;
783+
} else {
784+
var landingLongitude = landingPoint.longitude;
785+
}
786+
787+
plot.landingMarker = new L.marker([landingPoint.latitude, landingLongitude], {
788+
icon: new L.NumberedDivIcon({number: dates.indexOf(data.request.launch_datetime)+1})
789+
}).addTo(map);
790+
791+
var landingTime = new Date(landingPoint.datetime);
792+
if (properties[3] != "" && properties[4] != "") {
793+
var landingTooltip = "<b>Time:</b> " + landingTime.toLocaleString() + "<br><b>Model Dataset:</b> " + data.request.dataset +
794+
"<br><b>Model Assumptions:</b><br>- " + data.request.ascent_rate + "m/s ascent<br>- " + data.request.burst_altitude + "m burst altitude (" + properties[3] + " samples)<br>- " + data.request.descent_rate + "m/s descent (" + properties[4] + " samples)";
795+
} else {
796+
var landingTooltip = "<b>Time:</b> " + landingTime.toLocaleString() + "<br><b>Model Dataset:</b> " + data.request.dataset +
797+
"<br><b>Model Assumptions:</b><br>- " + data.request.ascent_rate + "m/s ascent<br>- " + data.request.burst_altitude + "m burst altitude<br>- " + data.request.descent_rate + "m/s descent";
798+
}
799+
plot.landingMarker.bindTooltip(landingTooltip, {offset: [13,-28]});
800+
}
801+
802+
function showPrediction(url) {
803+
return $.ajax({
804+
type: "GET",
805+
url: url,
806+
dataType: "json",
807+
});
808+
}
809+
810+
function deletePredictions(marker) {
811+
if (launchPredictions.hasOwnProperty(marker)) {
812+
for (var prediction in launchPredictions[marker]) {
813+
if (launchPredictions[marker].hasOwnProperty(prediction)) {
814+
for (var object in launchPredictions[marker][prediction]) {
815+
if (launchPredictions[marker][prediction].hasOwnProperty(object)) {
816+
map.removeLayer(launchPredictions[marker][prediction][object]);
817+
}
818+
}
819+
}
820+
}
821+
}
822+
var popup = launches.getLayer(marker).getPopup();
823+
var popupContent = popup.getContent();
824+
if (popupContent.includes("Delete</button>")) {
825+
popupContent = popupContent.split("<button onclick='deletePredictions(")[0];
826+
popup.setContent(popupContent);
827+
}
828+
}
829+
612830
function showLaunchSites() {
613831
if (!launches) {
614832
launches = new L.LayerGroup();
615833
$.getJSON("launchSites.json", function(json) {
616834
for (var key in json) {
617835
if (json.hasOwnProperty(key)) {
618836
var latlon = [json[key].lat, json[key].lon];
619-
var sondes = json[key].rs_types.toString();
620-
sondes = sondes.replace(new RegExp("\\b07\\b"), "iMet-1 (possible to track)");
621-
sondes = sondes.replace(new RegExp("\\b11\\b"), "LMS6-403 (possible to track)");
622-
sondes = sondes.replace(new RegExp("\\b13\\b"), "RS92 (possible to track)");
623-
sondes = sondes.replace(new RegExp("\\b14\\b"), "RS92 (possible to track)");
624-
sondes = sondes.replace(new RegExp("\\b17\\b"), "DFM-09 (possible to track)");
625-
sondes = sondes.replace(new RegExp("\\b19\\b"), "MRZ-N1 (possible to track)");
626-
sondes = sondes.replace(new RegExp("\\b21\\b"), "RS-11G (possible to track)");
627-
sondes = sondes.replace(new RegExp("\\b22\\b"), "RS-11G (possible to track)");
628-
sondes = sondes.replace(new RegExp("\\b23\\b"), "RS41 (possible to track)");
629-
sondes = sondes.replace(new RegExp("\\b24\\b"), "RS41 (possible to track)");
630-
sondes = sondes.replace(new RegExp("\\b34\\b"), "iMet-4 (possible to track)");
631-
sondes = sondes.replace(new RegExp("\\b35\\b"), "iMS-100 (possible to track)");
632-
sondes = sondes.replace(new RegExp("\\b41\\b"), "RS41 (possible to track)");
633-
sondes = sondes.replace(new RegExp("\\b42\\b"), "RS41 (possible to track)");
634-
sondes = sondes.replace(new RegExp("\\b52\\b"), "RS92-NGP (possible to track)");
635-
sondes = sondes.replace(new RegExp("\\b54\\b"), "DFM-17 (possible to track)");
636-
sondes = sondes.replace(new RegExp("\\b62\\b"), "MRZ-3MK (possible to track)");
637-
sondes = sondes.replace(new RegExp("\\b63\\b"), "M20 (possible to track)");
638-
sondes = sondes.replace(new RegExp("\\b77\\b"), "M10 (possible to track)");
639-
sondes = sondes.replace(new RegExp("\\b82\\b"), "LMS6-1680 (possible to track)");
640-
sondes = sondes.replace(new RegExp("\\b84\\b"), "iMet-54 (possible to track)");
837+
var sondes = json[key].rs_types;
838+
var sondesList = "";
839+
for (var y = 0; y < sondes.length; y++) {
840+
if (Array.isArray(sondes[y]) == false) {
841+
sondes[y] = [sondes[y]];
842+
}
843+
if (sondeCodes.hasOwnProperty(sondes[y][0])) {
844+
sondesList += sondeCodes[sondes[y][0]]
845+
if (sondes[y].length > 1) {
846+
sondesList += " (" + sondes[y][1] + " MHz)";
847+
}
848+
} else if (unsupportedSondeCodes.hasOwnProperty(sondes[y][0])) {
849+
sondesList += unsupportedSondeCodes[sondes[y][0]];
850+
sondesList += " (cannot track)";
851+
} else {
852+
sondesList += sondes[y][0] + " (unknown WMO code)";
853+
}
854+
if (y < sondes.length-1) {
855+
sondesList += ", ";
856+
}
857+
}
641858
var marker = new L.circleMarker(latlon, {color: '#696969', fillColor: "white", radius: 8});
642-
var popup = new L.popup({ autoClose: false, closeOnClick: false }).setContent("<font style='font-size: 13px'>" + json[key].station_name + "</font><br><br><b>Sondes launched:</b> " + sondes);
859+
var popup = new L.popup({ autoClose: false, closeOnClick: false });
643860
marker.bindPopup(popup);
644861
launches.addLayer(marker);
862+
if (json[key].hasOwnProperty('times')) {
863+
var popupContent = null;
864+
popupContent = "<font style='font-size: 13px'>" + json[key].station_name + "</font><br><br><b>Sondes launched:</b> " + sondesList;
865+
var ascent_rate = 5;
866+
var descent_rate = 6;
867+
var burst_altitude = 26000;
868+
var burst_samples = "";
869+
var descent_samples = "";
870+
if (json[key].rs_types.includes("11") || json[key].rs_types.includes("82")) { //LMS6
871+
ascent_rate = 5;
872+
descent_rate = 2.5;
873+
burst_altitude = 33500;
874+
}
875+
if (json[key].hasOwnProperty('ascent_rate')) {
876+
ascent_rate = json[key]["ascent_rate"];
877+
}
878+
if (json[key].hasOwnProperty('descent_rate')) {
879+
descent_rate = json[key]["descent_rate"];
880+
}
881+
if (json[key].hasOwnProperty('burst_altitude')) {
882+
burst_altitude = json[key]["burst_altitude"];
883+
}
884+
if (json[key].hasOwnProperty('burst_samples')) {
885+
burst_samples = json[key]["burst_samples"];
886+
}
887+
if (json[key].hasOwnProperty('descent_samples')) {
888+
descent_samples = json[key]["descent_samples"];
889+
}
890+
popupContent += "<br><b>Launch schedule:</b>";
891+
for (var x = 0; x < json[key]['times'].length; x++) {
892+
popupContent += "<br>- ";
893+
var day = json[key]['times'][x].split(":")[0];
894+
if (day == 0) {
895+
popupContent += "Everyday at ";
896+
} else if (day == 1) {
897+
popupContent += "Monday at ";
898+
} else if (day == 2) {
899+
popupContent += "Tuesday at ";
900+
} else if (day == 3) {
901+
popupContent += "Wednesday at ";
902+
} else if (day == 4) {
903+
popupContent += "Thursday at ";
904+
} else if (day == 5) {
905+
popupContent += "Friday at ";
906+
} else if (day == 6) {
907+
popupContent += "Saturday at ";
908+
} else if (day == 7) {
909+
popupContent += "Sunday at ";
910+
}
911+
popupContent += json[key]['times'][x].split(":")[1] + ":" + json[key]['times'][x].split(":")[2] + " UTC";
912+
}
913+
if (json[key].hasOwnProperty('notes')) {
914+
popupContent += "<br><b>Notes:</b> " + json[key]["notes"];
915+
}
916+
popupContent += "<br><b>Know when this site launches?</b> Contribute <a href='https://github.com/projecthorus/sondehub-tracker/issues/114' target='_blank'>here</a>";
917+
popupContent += "<br><button onclick='launchSitePredictions(\"" + json[key]['times'].toString() + "\", \"" + latlon.toString() + "\", \"" + ascent_rate + ":" + descent_rate + ":" + burst_altitude + ":" + burst_samples + ":" + descent_samples + "\", \"" + launches.getLayerId(marker) + "\")' style='margin-bottom:0;'>Generate Predictions</button>";
918+
} else {
919+
popupContent = "<font style='font-size: 13px'>" + json[key].station_name + "</font><br><br><b>Sondes launched:</b> " + sondesList;
920+
popupContent += "<br><b>Know when this site launches?</b> Contribute <a href='https://github.com/projecthorus/sondehub-tracker/issues/114' target='_blank'>here</a>";
921+
}
922+
popup.setContent(popupContent);
645923
}
646924
}
647925
});
@@ -750,7 +1028,7 @@ function habitat_data(jsondata, alternative) {
7501028
"temperature_internal": "&deg;C",
7511029
"temperature_external": "&deg;C",
7521030
"temperature_radio": "&deg;C",
753-
"pressure": " Pa",
1031+
"pressure": " hPa",
7541032
"voltage_solar_1": " V",
7551033
"voltage_solar_2": " V",
7561034
"battery_percent": "%",

0 commit comments

Comments
 (0)