diff --git a/build.sh b/build.sh index 5f5c06d..99c8903 100755 --- a/build.sh +++ b/build.sh @@ -4,7 +4,7 @@ echo -n "Compiling CSS... " cd css rm -f mobile.css -cat base.css skeleton.css layout.css habitat-font.css main.css > mobile.tmp +cat base.css skeleton.css layout.css habitat-font.css main.css leaflet.css leaflet.fullscreen.css skewt.css > mobile.tmp java -jar "../tools/yuicompressor-2.4.8.jar" --type=css mobile.tmp > mobile.css rm -f mobile.tmp cd .. diff --git a/css/main.css b/css/main.css index 61ad3ae..eceb5e0 100644 --- a/css/main.css +++ b/css/main.css @@ -412,7 +412,7 @@ header .search form input[type='submit'] { #main .row .data .vbutton { position: absolute; background-color: #fff; - width: 25px !important; + width: 30px !important; right: 5px; top: 150px; padding-left: 3px; @@ -428,7 +428,7 @@ header .search form input[type='submit'] { #main .row .data .sbutton { position: absolute; background-color: #fff; - width: 25px !important; + width: 30px !important; right: 5px; top: 170px; padding-left: 3px; diff --git a/css/skewt.css b/css/skewt.css new file mode 100644 index 0000000..ac623f3 --- /dev/null +++ b/css/skewt.css @@ -0,0 +1 @@ +.skew-t{position:relative;padding:0px}.skew-t .fnt{transition:font .3s;font:10px Arial}.skew-t .mainsvg{background-color:transparent}.skew-t .controls{box-sizing:border-box;width:100%;display:flex}.skew-t .controls .buttons,.skew-t .range-container .buttons,.skew-t .range-container-extra .buttons{flex-grow:1;margin:3px;padding:3px;border-radius:10px;text-align:center;cursor:pointer;background-color:#dcdcdc}.skew-t .controls .buttons.clicked,.skew-t .range-container .buttons.clicked,.skew-t .range-container-extra .buttons.clicked{background-color:#969696;color:white}.skew-t .controls .buttons.units,.skew-t .range-container .buttons.units,.skew-t .range-container-extra .buttons.units{flex-grow:0;width:80px}.skew-t .controls .buttons.noclick,.skew-t .range-container .buttons.noclick,.skew-t .range-container-extra .buttons.noclick{cursor:initial}.skew-t .controls .values{flex-grow:1;margin:3px;padding:3px;text-align:center;border-radius:10px;border:1px solid #dcdcdc;min-width:40px}.skew-t .axis path,.skew-t .axis line{fill:none;stroke:#000;stroke-width:1px;shape-rendering:crispEdges}.skew-t .axis{fill:#000}.skew-t .y.axis{font-size:10px}.skew-t .y.axis.hght{font-size:10px;fill:red}.skew-t .x.axis{font-size:10px}.skew-t .y.axis.ticks text{display:none}.skew-t .y.axis.hght-ticks text{display:none}.skew-t .y.axis.hght-ticks line{stroke:red}.skew-t .skewt-line{fill:none;stroke-width:1.5px;stroke-opacity:.5}.skew-t .skewt-line.highlight-line{stroke-opacity:1;stroke-width:2px}.skew-t .temp{fill:none;stroke-width:1.5px;stroke-opacity:.5;stroke:red}.skew-t .temp.highlight-line{stroke-opacity:1;stroke-width:2px}.skew-t .dwpt{fill:none;stroke-width:1.5px;stroke-opacity:.5;stroke:blue}.skew-t .dwpt.highlight-line{stroke-opacity:1;stroke-width:2px}.skew-t .parcel{fill:none;stroke-width:1.5px;stroke-opacity:.5;stroke:green;stroke-opacity:.3}.skew-t .parcel.highlight-line{stroke-opacity:1;stroke-width:2px}.skew-t .cond-level{fill:none;stroke-width:1.5px;stroke-opacity:.5;stroke-width:1px;stroke:rgba(128,128,128,0.8);stroke-opacity:.15}.skew-t .cond-level.highlight-line{stroke-opacity:1;stroke-width:2px}.skew-t .cond-level.highlight-line{stroke-width:1px}.skew-t .gridline{stroke-width:.5px;stroke-opacity:.3;fill:none}.skew-t .gridline.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .tempzero{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:#aaa;stroke-width:1.25px}.skew-t .tempzero.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .dryadiabat{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:green}.skew-t .dryadiabat.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .templine{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:red}.skew-t .templine.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .pressure{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:rgba(120, 120, 120)}.skew-t .pressure.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .moistadiabat{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:green;stroke-dasharray:5}.skew-t .moistadiabat.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .isohume{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:blue;stroke-dasharray:2}.skew-t .isohume.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .elr{stroke-width:.5px;stroke-opacity:.3;fill:none;stroke:purple;stroke-opacity:.03;stroke-width:3px}.skew-t .elr.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .elr.highlight-line{stroke-opacity:.7}.skew-t .sigline{stroke-width:.5px;stroke-opacity:.3;fill:none}.skew-t .sigline.highlight-line{stroke-opacity:1;stroke-width:1px}.skew-t .sigline.surface{stroke:green}.skew-t .sigline.tropopause-level{stroke:blue}.skew-t .windbarb{stroke:#000;stroke-width:.75px;fill:none;stroke-opacity:1}.skew-t .windbarb .barblines{opacity:.4}.skew-t .windbarb .barblines.highlight-line{opacity:1}.skew-t .flag{fill:#000}.skew-t .overlay{fill:none;pointer-events:all}.skew-t .focus.tmpc circle{fill:red;stroke:none}.skew-t .focus.dwpc circle{fill:blue;stroke:none}.skew-t .focus text{font-size:14px}.skew-t .skewt-wind-arrow{alignment-baseline:middle;text-anchor:middle;fill:black;font-size:16px;font-weight:bold}.skew-t .range-container{display:flex;flex-wrap:wrap;margin-top:10px}.skew-t .range-container-extra{display:flex;flex-wrap:wrap;margin-top:10px;margin-top:0px}.skew-t .range-container-extra .buttons{flex-grow:1;margin:3px;padding:3px;border-radius:10px;text-align:center;cursor:pointer;background-color:#dcdcdc}.skew-t .range-container-extra .buttons.clicked{background-color:#969696;color:white}.skew-t .range-container-extra .buttons.units{flex-grow:0;width:80px}.skew-t .range-container-extra .buttons.noclick{cursor:initial}.skew-t .skewt-ranges{all:revert;-webkit-appearance:none;color:white;width:60%}.skew-t .skewt-ranges:focus{outline:none}.skew-t .skewt-ranges::-webkit-slider-runnable-track{all:revert;-webkit-appearance:none;background:rgba(220, 220, 220);border-radius:16px;height:16px;padding:2px}.skew-t .skewt-ranges::-webkit-slider-thumb{all:revert;-webkit-appearance:none;border:1px solid rgba(100, 100, 100);height:12px;width:12px;border-radius:12px;background:#ffffff;cursor:pointer;box-shadow:1px 1px 1px #000000}.skew-t .skewt-range-des{width:20%}.skew-t .skewt-range-val{width:15%;white-space:nowrap}.skew-t .checkbox-container{width:100%;line-height:20px}.skew-t .skewt-checkbox-text{display:inline-block}.skew-t .flex-break{flex-basis:100%}.skew-t .cloud-container{overflow:hidden;position:absolute;width:20px;opacity:.8}.skew-t .cloud-container .cloud{position:absolute;width:10px} \ No newline at end of file diff --git a/index.html b/index.html index 5339800..e50279d 100644 --- a/index.html +++ b/index.html @@ -20,8 +20,7 @@ - - +
@@ -50,6 +49,11 @@Recovery reports can be added on the Chase-Car pane.
+ P[0]&&(P=ha(p,D,p,h)),d.CCL=P[0],z=f.invert(P[0]),d.isohumeToTemp=[].concat(ga(h,c).filter(function(e){return e[1]>=z}),[[P[1],z]],ga(D,c).filter(function(e){return e[1]>=z}).reverse()),d.moistFromCCL=x(P[0],P[1]).moist}if(d.surface=e.gh[0],!A)return d;if(d.origThermalTop=A[0],A&&S&&S[0]j});X.push([S[1],j]),d.isohume=X}var q=f.invert(A[0]),I=ga(u,c).filter(function(e){return e[1]>q});return I.push([A[1],q]),d.dry=I,d.pThermalTop=q,d.elevThermalTop=A[0],d}function Sa(e,t,n){return e+n*(t-e)}function Aa(e,t){return 0>e?0:e>t-1?t-1:e}function La(e){return g[aa(Aa(e,160))]}function Ea(e,t,n,a,r){return(.5*-e+.5*(3*t)-.5*(3*n)+.5*a)*la(r,3)+(e-.5*(5*t)+2*n-.5*a)*la(r,2)+(.5*-e+.5*n)*r+t}function Pa(e,n,a){return Ea(Ea(e[0],e[1],e[2],e[3],n),Ea(e[4],e[5],e[6],e[7],n),Ea(e[8],e[9],e[10],e[11],n),Ea(e[12],e[13],e[14],e[15],n),a)}function Na(e,t){return e >8|240&r>>4,15&r>>4|240&r,(15&r)<<4|15&r,1):8===i?Qt(255&r>>24,255&r>>16,255&r>>8,(255&r)/255):4===i?Qt(15&r>>12|240&r>>8,15&r>>8|240&r>>4,15&r>>4|240&r,((15&r)<<4|15&r)/255):null):(r=Wt.exec(a))?new gn(r[1],r[2],r[3],1):(r=rn.exec(a))?new gn(255*r[1]/100,255*r[2]/100,255*r[3]/100,1):(r=an.exec(a))?Qt(r[1],r[2],r[3],r[4]):(r=on.exec(a))?Qt(255*r[1]/100,255*r[2]/100,255*r[3]/100,r[4]):(r=sn.exec(a))?_n(r[1],r[2]/100,r[3]/100,1):(r=ln.exec(a))?_n(r[1],r[2]/100,r[3]/100,r[4]):cn.hasOwnProperty(a)?Kt(cn[a]):"transparent"===a?new gn(NaN,NaN,NaN,0):null}function Kt(e){return new gn(255&e>>16,255&e>>8,255&e,1)}function Qt(a,i,l,o){return 0>=o&&(a=i=l=NaN),new gn(a,i,l,o)}function dn(e){return e instanceof Yt||(e=Zt(e)),e?new gn((e=e.rgb()).r,e.g,e.b,e.opacity):new gn}function pn(a,t,n,e){return 1===arguments.length?dn(a):new gn(a,t,n,null==e?1:e)}function gn(a,t,n,e){this.r=+a,this.g=+t,this.b=+n,this.opacity=+e}function mn(){return"#"+yn(this.r)+yn(this.g)+yn(this.b)}function fn(){var e=this.opacity;return(1===(e=isNaN(e)?1:pa(0,da(1,e)))?"rgb(":"rgba(")+pa(0,da(255,ra(this.r)||0))+", "+pa(0,da(255,ra(this.g)||0))+", "+pa(0,da(255,ra(this.b)||0))+(1===e?")":", "+e+")")}function yn(e){return(16>(e=pa(0,da(255,ra(e)||0)))?"0":"")+e.toString(16)}function _n(a,i,l,o){return 0>=o?a=i=l=NaN:0>=l||1<=l?a=i=NaN:0>=i&&(a=NaN),new xn(a,i,l,o)}function vn(l){if(l instanceof xn)return new xn(l.h,l.s,l.l,l.opacity);if(l instanceof Yt||(l=Zt(l)),!l)return new xn;if(l instanceof xn)return l;var d=(l=l.rgb()).r/255,n=l.g/255,e=l.b/255,r=da(d,n,e),i=pa(d,n,e),o=NaN,p=i-r,c=(i+r)/2;return p?(o=d===i?(n-e)/p+6*(n =(e*=2)?e*e*e:(e-=2)*e*e+2)/2}};Et.prototype.interrupt=function(e){return this.each(function(){!function(l,t){var s,d,p,c=l.__transition,o=!0;if(c){for(p in t=null==t?null:t+"",c)(s=c[p]).name===t?(d=2 >1)+c+e+u+m.slice(g);break;default:e=m+c+e+u;}return a(e)}var C=(s=Ia(s)).fill,k=s.align,S=s.sign,h=s.symbol,E=s.zero,P=s.width,d=s.comma,A=s.precision,N=s.trim,z=s.type;"n"===z?(d=!0,z="g"):Wr[z]||(void 0===A&&(A=12),N=!0,z="g"),(E||"0"===C&&"="===k)&&(E=!0,C="0",k="=");var D="$"===h?r:"#"===h&&/[boxX]/.test(z)?"0"+z.toLowerCase():"",O="$"===h?i:/[%p]/.test(z)?u:"",w=Wr[z],x=/[defgprs%]/.test(z);return A=void 0===A?6:/[gprs]/.test(z)?pa(1,da(21,A)):pa(0,da(20,A)),c.toString=function(){return s+""},c}var h,g,T=void 0===d.grouping||void 0===d.thousands?$a:(h=mr.call(d.grouping,Number),g=d.thousands+"",function(e,t){for(var n=e.length,r=[],l=0,o=h[0],d=0;0 >1,a+1)}(e,0),n}function n(){E=parseInt(A.style("width"),10)-10,r=E-P.left-P.right,d=E-P.top-P.bottom,s=N((v||55)*z),p=Mr.scaleLinear().range([-r/2,r+r/2]).domain([0-2*X,0+2*X]),K=Mr.scaleLog().range([0,d]).domain([k,1050]),Q=Mr.axisBottom(p).tickSize(0,0).ticks(20),J=Mr.axisLeft(K).tickSize(0,0).tickValues($.filter(function(e){return 0==e%100||50==e||150==e})).tickFormat(Mr.format(".0d")),W=Mr.axisRight(K).tickSize(5,0).tickValues(B),ee=Mr.axisLeft(K).tickSize(2,0).tickValues(U)}function a(e,t){return"kt"===t?1.943844492*e:"kmh"===t?3.6*e:e}function l(e,t){return"m"===t?ra(e)+t:"f"===t?ra(3.28084*e)+"ft":e}function o(){ue.selectAll("*").remove(),n(),le.attr("width",r+P.right+P.left).attr("height",d+P.top+P.bottom),ce.attr("transform","translate("+P.left+","+P.top+")"),we(),ae.forEach(function(e){Ae(e.data,{add:!0,select:!1})}),u&&Se(u.data),ke(),fe.attr("width",r).attr("height",d),ie.style("left",P.left+2+"px").style("top",P.top+"px").style("height",d+"px");var e=K(100);ye.style("left","0px").style("top",e+"px").style("height",d-e+"px"),_e.style("left","10px").style("top",e+"px").style("height",d-e+"px")}for(var s,u,g=this,m=1g&&(h=d.slice(g,h),f[m]?f[m]+=h:f[++m]=h),(p=p[0])===(c=c[0])?f[m]?f[m]+=c:f[++m]=c:(f[++m]=null,u.push({i:m,x:En(p,c)})),g=Xn.lastIndex;return gT&&(X=T)}}return{clouds:S,width:T,height:C}},cloudsToCanvas:function(e){var t=e.clouds,n=e.width,a=e.height,r=e.canvas;null==r&&(r=document.createElement("canvas")),r.width=n,r.height=a;for(var i=r.getContext("2d"),l=i.getImageData(0,0,n,a),o=l.data,s=0,d=0,p=0;p
").concat(m?"TCON"==g?(m-H).toFixed(1)+"°C":(f?"> ":"")+l(m,re):"","");Ne[g].val.html(y)}},Se=function(e){ae.forEach(function(t){var n=t.data==e;for(var a in t.lines)t.lines[a].classed("highlight-line",n&&(!Ne[a]||Ne[a].hi));n&&(u=t,ne=[].concat(t.data).reverse(),Oe.parctemp.input.node().value=Oe.parctemp.value=t.parctemp=ra(10*t.parctemp)/10,Oe.parctemp.valueDiv.html(Re(t.parctemp,"parctemp")))}),S.hideTooltips()},Ae=function(e){var n=1
";
html += "' +
'Path' +
((vehicle.vehicle_type!="car") ? 'Share' : '') +
- ((vehicle.vehicle_type!="car") ? 'Plot' : '') +
+ ((vehicle.vehicle_type!="car") ? 'SkewT' : '') +
'
';
//mobile
@@ -2282,7 +2346,7 @@ function updateVehicleInfo(vcallsign, newPosition) {
'
' +
'Path' +
((vehicle.vehicle_type!="car") ? 'Share' : '') +
- ((vehicle.vehicle_type!="car") ? 'Plot' : '') +
+ ((vehicle.vehicle_type!="car") ? 'SkewT' : '') +
'
';
var b = '
' +
@@ -2336,6 +2400,208 @@ function updateVehicleInfo(vcallsign, newPosition) {
return true;
}
+function skewTdelete () {
+ var box = $("#skewtbox");
+
+ skewt.clear();
+ $('#resetSkewt').hide();
+ $('#deleteSkewt').hide();
+ $("#skewtSerial").hide();
+ box.hide();
+ $('.skewt').hide();
+ checkSize();
+}
+
+function skewTrefresh () {
+ skewt.clear();
+ $("#skewt-plot").empty();
+ $('#resetSkewt').hide();
+ $('#deleteSkewt').hide();
+
+ skewt = new SkewT('#skewt-plot');
+
+ try {
+ skewt.plot(skewtdata);
+ $('#resetSkewt').show();
+ $('#deleteSkewt').show();
+ }
+ catch(err) {}
+}
+
+function skewTdraw (callsign) {
+ // Open sidebar
+ var box = $("#skewtbox");
+
+ if(box.is(':hidden')) {
+ $('.flatpage, #homebox').hide();
+ $('.skewt').show();
+ box.show().scrollTop(0);
+ checkSize();
+ };
+
+ // Delete existing
+ try {
+ skewt.clear();
+ } catch (err) {}
+
+ $('#resetSkewt').hide();
+ $('#deleteSkewt').hide();
+ $("#skewt-plot").empty();
+
+ // Loading gif
+ $("#skewtLoading").show();
+ $("#skewtSerial").show();
+ $("#skewtSerial").text("Serial: " + callsign);
+
+ // Download Data
+ var data_url = "https://api.v2.sondehub.org/sonde/" + encodeURIComponent(callsign);
+ $.ajax({
+ type: "GET",
+ url: data_url,
+ dataType: "json",
+ success: function(data) {
+ processSkewT(data);
+ }
+ });
+
+ // Credit https://github.com/projecthorus/sondehub-card/blob/main/js/utils.js#L116
+ function processSkewT (data) {
+ burst_idx = -1;
+ max_alt = -99999.0;
+ for (let i = 0; i < data.length; i++){
+ alt = parseFloat(data[i].alt);
+ if (alt > max_alt){
+ max_alt = alt;
+ burst_idx = i;
+ }
+ }
+ if(data.length < 50){
+ alert("Insufficient data for Skew-T plot.");
+ return;
+ }
+
+ // Check that we have ascent data
+ if (burst_idx <= 0){
+ alert("Insufficient data for Skew-T plot (Only descent data available).");
+ return;
+ }
+
+ // Check that the first datapoint is at a reasonable altitude.
+ if (data[0].alt > 15000){
+ alert("Insufficient data for Skew-T plot (Only data > 15km available)");
+ return;
+ }
+
+ v1_data = false;
+ sonde_type = data[data.length-1].type;
+ if(sonde_type == 'payload_telemetry'){
+ // Sondehub v1 data.
+ v1_data = true;
+ }
+
+ var skewt_data = [];
+ decimation = 25;
+ if (v1_data == true){
+ decimation = 1;
+ }
+
+ idx = 1;
+
+ while (idx < burst_idx){
+ entry = data[idx];
+ old_entry = data[idx-1];
+
+ _old_date = new Date(old_entry.datetime);
+ _new_date = new Date(entry.datetime);
+ _time_delta = (_new_date - _old_date)/1000.0;
+ if (_time_delta <= 0){
+ idx = idx + 1;
+ continue;
+ }
+
+ _temp = null;
+ _dewp = -999.0;
+ _pressure = null;
+
+ // Extract temperature datapoint
+ if (entry.hasOwnProperty('temp')){
+ if(parseFloat(entry.temp) > -270.0){
+ _temp = parseFloat(entry.temp);
+ } else{
+ idx = idx + 1;
+ continue;
+ }
+ }else{
+ // No temp data. Skip to the next point
+ idx = idx + 1;
+ continue;
+ }
+
+ // Try and extract RH datapoint
+ if (entry.hasOwnProperty('humidity')){
+ if(parseFloat(entry.humidity) >= 0.0){
+ _rh = parseFloat(entry.humidity);
+ // Calculate the dewpoint
+ _dewp = (243.04 * (Math.log(_rh / 100) + ((17.625 * _temp) / (243.04 + _temp))) / (17.625 - Math.log(_rh / 100) - ((17.625 * _temp) / (243.04 + _temp))));
+ } else {
+ _dewp = -999.0;
+ }
+ }
+
+ // Calculate movement
+ _old_pos = {'lat': old_entry.lat, 'lon': old_entry.lon, 'alt': old_entry.alt};
+ _new_pos = {'lat': entry.lat, 'lon': entry.lon, 'alt': entry.alt};
+
+ _pos_info = calculate_lookangles(_old_pos, _new_pos);
+ _wdir = (_pos_info['azimuth']+180.0)%360.0;
+ _wspd = _pos_info['great_circle_distance']/_time_delta;
+
+ if (entry.hasOwnProperty('pressure')){
+ _pressure = entry.pressure;
+ } else {
+ // Otherwise, calculate it
+ _pressure = getPressure(_new_pos.alt);
+ }
+
+ if(_pressure < 100.0){
+ break;
+ }
+
+ _new_skewt_data = {"press": _pressure, "hght": _new_pos.alt, "temp": _temp, "dwpt": _dewp, "wdir": _wdir, "wspd": _wspd};
+
+ skewt_data.push(_new_skewt_data);
+
+ idx = idx + decimation;
+ }
+
+ skewtdata = skewt_data;
+
+ $("#skewtLoading").hide();
+
+ if (skewtdata.length > 0){
+
+ if(box.is(':hidden')) {
+ $('.flatpage, #homebox').hide();
+ $('.skewt').show();
+ box.show().scrollTop(0);
+ checkSize();
+ };
+
+ skewt = new SkewT('#skewt-plot');
+
+ try {
+ skewt.plot(skewtdata);
+ $('#resetSkewt').show();
+ $('#deleteSkewt').show();
+ }
+ catch(err) {}
+
+ } else {
+ alert("Insufficient Data available, or no Temperature/Humidity data available to generate Skew-T plot.");
+ };
+ }
+};
+
function set_polyline_visibility(vcallsign, val) {
var vehicle = vehicles[vcallsign];
vehicle.polyline_visible = val;