|
|
| Rivi 703: |
Rivi 703: |
| w = mwDate.getWeek(); | | w = mwDate.getWeek(); |
| v = memo.obsesA[o].value; | | v = memo.obsesA[o].value; |
| | |
| | if ( typeof memo.avgW[y] == 'undefined' ) { memo.avgW[y] = {}; } |
|
| |
|
| if ( typeof memo.avgW[y][w] == 'undefined' ) { | | if ( typeof memo.avgW[y][w] == 'undefined' ) { |
| Rivi 760: |
Rivi 762: |
| } | | } |
| | | |
| function XaddDataToSeasonChart(memo) {
| |
|
| |
| memo.axisMinDate = new Date( memo.obsMinCompDateMs );
| |
| memo.axisMinDate.setDate(1);
| |
| memo.axisMaxDate = new Date( memo.obsMaxCompDateMs );
| |
| memo.axisMaxDate.setMonth( memo.axisMaxDate.getMonth()+1 );
| |
| memo.axisMaxDate.setDate(0);
| |
|
| |
| memo.seasonsO = {};
| |
| memo.seasonsA = [];
| |
|
| |
| for (var o in memo.obsesA) {
| |
|
| |
| s = memo.obsesA[o].seriesId;
| |
| if (typeof memo.seasonsO[s] == 'undefined' ) { memo.seasonsO[s] = { 'obses': [] }; }
| |
| memo.seasonsO[s].obses.push( memo.obsesA[o] );
| |
| }
| |
|
| |
| for (var o in memo.seasonsO) { memo.seasonsA.push(o); } memo.seasonsA.sort( function(a,b) { return b-a; } );
| |
|
| |
| // create cols for each series
| |
| var sc = 0;
| |
| for (var s in memo.seasonsA) {
| |
|
| |
| // data
| |
| if ( memo.season == 'summer' ) {
| |
| memo.data.addColumn( 'number', memo.seasonsA[s].toString() );
| |
| } else {
| |
| memo.data.addColumn( 'number', memo.seasonsA[s].toString() + '-' + (parseInt(memo.seasonsA[s])+1).toString() );
| |
| }
| |
| // style
| |
| memo.data.addColumn( { type: 'string', role: 'style' } );
| |
| // tooltip
| |
| memo.data.addColumn( { type: 'string', role: 'tooltip', 'p': { 'html': true } } );
| |
| // annotation
| |
| memo.data.addColumn( { type: 'string', role: 'annotation' } );
| |
|
| |
| // add to series
| |
| if ( memo.seasonsA[s] == memo.curSeason ) {
| |
| if ( memo.type == 'cat' ) {
| |
| memo.options.series[sc++] = { type: 'scatter', curveType: 'function', color: colors['curObses'], XpointSize: 3, lineWidth: 0, visibleInLegend: true, hideThis: false, origSize: 10 };
| |
| } else {
| |
| memo.options.series[sc++] = { type: 'scatter', curveType: 'function', color: colors['curObses'], XpointSize: 3, lineWidth: 4, visibleInLegend: true, hideThis: false, origSize: 10 };
| |
| }
| |
| } else {
| |
| memo.options.series[sc++] = { type: 'scatter', curveType: 'function', color: colors['obses'], XpointSize: 5, lineWidth: 0, visibleInLegend: false, hideThis: true, origSize: 5 };
| |
| }
| |
| }
| |
|
| |
| // legend entry for timespan
| |
| memo.data.addColumn( 'number', memo.obsMinDate.getFullYear() + '-' + memo.obsMaxDate.getFullYear() );
| |
| memo.options.series[sc++] = { type: 'scatter', color: colors['obses'], pointSize: 5, visibleInLegend: true, hideThis: false };
| |
|
| |
| // series for average
| |
| memo.data.addColumn( 'number', i18n['average'][memo.lang] );
| |
|
| |
| if ( i18n[memo.obscode].trend.layout == 'area') {
| |
| memo.options.series[sc++] = { type: 'area', curveType: 'function', lineWidth: 2, pointSize: 0, color: colors['average'], areaOpacity: 0.25, lineDashStyle: [2,2], enableInteractivity: false, visibleInLegend: memo.trendType=='moving' };
| |
| } else {
| |
| memo.options.series[sc++] = { type: 'line', curveType: 'function', lineWidth: 3, pointSize: 5, color: colors['average'], dataOpacity: 0.75, lineDashStyle: [6,3], enableInteractivity: false, visibleInLegend: memo.trendType=='moving' };
| |
| }
| |
|
| |
| memo.data.addColumn( { id: 'dev', type: 'number', role: 'interval' } );
| |
| memo.data.addColumn( { id: 'dev', type: 'number', role: 'interval' } );
| |
|
| |
| // series for trend line
| |
| memo.data.addColumn( 'number', 'trend' );
| |
| //memo.options.trendlines[sc] = { labelInLegend: i18n['trendline'][memo.lang], tooltip: false, color: colors['trend'], lineWidth: 2, opacity: 0.5, type: 'polynomial', degree: 15, visibleInLegend: memo.trendType!='moving' };
| |
| memo.options.series[sc++] = { type: 'scatter', pointSize: 0, enableInteractivity: false, visibleInLegend: false, hideThis: false };
| |
|
| |
| // do average
| |
| if (memo.trendType == 'gaussian') {
| |
|
| |
| memo.gSmoothing = 4;
| |
| if ( i18n[memo.obscode]['type'] == 'cat') {
| |
| memo.distF = 0.1;
| |
| } else {
| |
| memo.distF = 0;
| |
| }
| |
|
| |
| // sort according to compare date
| |
| memo.obsesA.sort( function( a, b ) { return a.compareDate - b.compareDate; });
| |
|
| |
| // make week averages
| |
| memo.avgW = {};
| |
| for (var o in memo.obsesA) {
| |
|
| |
| mwDate = new Date( memo.obsesA[o].compareDate.getFullYear(), memo.obsesA[o].compareDate.getMonth(), memo.obsesA[o].compareDate.getDate() );
| |
| y = mwDate.getFullYear();
| |
| w = mwDate.getWeek();
| |
| v = memo.obsesA[o].value + memo.distF*Math.random() - memo.distF*Math.random();
| |
|
| |
| if ( typeof memo.avgW[y] == 'undefined' ) { memo.avgW[y] = {}; }
| |
|
| |
| if ( typeof memo.avgW[y][w] == 'undefined' ) {
| |
| mwDate.setDate( mwDate.getDate() - mwDate.getDay() + 3 )
| |
| memo.avgW[y][w] = { cDate: mwDate, values: [ v ], sum: v, count: 1, avg: v };
| |
| } else {
| |
| memo.avgW[y][w].values.push( v );
| |
| memo.avgW[y][w].sum += v;
| |
| memo.avgW[y][w].count++;
| |
| memo.avgW[y][w].avg = memo.avgW[y][w].sum / memo.avgW[y][w].count;
| |
| }
| |
| }
| |
|
| |
| //console.log(memo.avgW);
| |
|
| |
| // calculate standard deviation
| |
| devSum = 0;
| |
| devC = 0;
| |
| devMax = 0;
| |
| for ( var y in memo.avgW ) {
| |
| for ( var w in memo.avgW[y] ) {
| |
| devC++;
| |
| devx = 0;
| |
| for (var vi in memo.avgW[y][w].values ) {
| |
| devx += Math.pow( (memo.avgW[y][w].values[vi] - memo.avgW[y][w].avg), 2 );
| |
| }
| |
| memo.avgW[y][w].dev = Math.sqrt( devx/memo.avgW[y][w].count );
| |
| devSum += memo.avgW[y][w].dev;
| |
| devMax = Math.max( memo.avgW[y][w].dev, devMax );
| |
| }
| |
| }
| |
|
| |
| devAvg = devSum/devC;
| |
|
| |
| for ( var y in memo.avgW ) {
| |
| for ( var w in memo.avgW[y] ) {
| |
| if ( memo.avgW[y][w].count < 5 ) {
| |
| //memo.avgW[y][w].dev = Math.max( memo.avgW[y][w].dev, devAvg );
| |
| }
| |
| }
| |
| }
| |
|
| |
| memo.avgWlist = [];
| |
|
| |
| for ( var y in memo.avgW ) {
| |
| for ( var w in memo.avgW[y] ) {
| |
| memo.avgWlist.push( { y: parseInt(y), w: parseInt(w), avg: memo.avgW[y][w].avg, dev: memo.avgW[y][w].dev } );
| |
| }
| |
| }
| |
|
| |
| memo.avgWlist.sort( function(a, b) { return (a.y*100+a.w) - (b.y*100+b.w); });
| |
|
| |
| //console.log(memo.avgWlist);
| |
|
| |
| firstY = memo.avgWlist[0].y;
| |
| firstW = memo.avgWlist[0].w;
| |
| lastY = memo.avgWlist[memo.avgWlist.length-1].y;
| |
| lastW = memo.avgWlist[memo.avgWlist.length-1].w;
| |
|
| |
| memo.avgMlist = [];
| |
| memo.avgLlist = [];
| |
| memo.avgHlist = [];
| |
|
| |
| for ( var y=firstY; y<=lastY; y++ ) {
| |
|
| |
| if ( y == firstY ) { w1 = firstW; }
| |
| else { w1 = 1; }
| |
|
| |
| if ( y == lastY ) { w2 = lastW; }
| |
| else { w2 = 52; }
| |
|
| |
| for ( var w=w1; w<=w2; w++) {
| |
|
| |
| //console.log( y + '/' + w );
| |
|
| |
| if ( typeof(memo.avgW[y][w]) == 'undefined' ) {
| |
|
| |
| memo.avgMlist.push( null );
| |
| memo.avgLlist.push( null );
| |
| memo.avgHlist.push( null );
| |
|
| |
|
| |
| } else {
| |
| memo.avgMlist.push( memo.avgW[y][w].avg );
| |
|
| |
| if ( memo.avgW[y][w].count < 2 ) {
| |
| d = Math.max( devMax, memo.avgW[y][w].dev );
| |
| } else if ( memo.avgW[y][w].count < 4 ) {
| |
| d = Math.max( devAvg, memo.avgW[y][w].dev );
| |
| } else {
| |
| d = memo.avgW[y][w].dev;
| |
| }
| |
|
| |
| if ( i18n[memo.obscode].type == 'cat' && ( memo.avgW[y][w].avg - 2*memo.avgW[y][w].dev ) < -memo.distF ) {
| |
| memo.avgLlist.push( -memo.distF*Math.random()*2 );
| |
| } else {
| |
| memo.avgLlist.push( memo.avgW[y][w].avg - 2*d ); //memo.avgW[y][w].dev );
| |
| }
| |
|
| |
| memo.avgHlist.push( memo.avgW[y][w].avg + 2*d ); //memo.avgW[y][w].dev );
| |
| }
| |
| }
| |
| }
| |
|
| |
| //console.log(memo.avgMlist);
| |
|
| |
| for ( var i=0; i<memo.avgMlist.length; i++ ) {
| |
|
| |
| if ( memo.avgMlist[i] === null ) {
| |
|
| |
| lastM = memo.avgMlist[i-1];
| |
| lastL = memo.avgLlist[i-1];
| |
| lastH = memo.avgHlist[i-1];
| |
|
| |
| for ( var ix=(i+1); ix < memo.avgMlist.length; ix++ ) {
| |
|
| |
| if ( memo.avgMlist[ix] !== null ) {
| |
|
| |
| memo.avgMlist[i] = lastM + (memo.avgMlist[ix]-lastM) / (ix-i+1);
| |
| memo.avgLlist[i] = lastL + (memo.avgLlist[ix]-lastL) / (ix-i+1);
| |
| memo.avgHlist[i] = lastH + (memo.avgHlist[ix]-lastH) / (ix-i+1);
| |
|
| |
| break;
| |
| }
| |
| }
| |
| }
| |
| }
| |
|
| |
| //console.log(memo.avgMlist);
| |
|
| |
| // if full year
| |
| if ( memo.avgMlist.length >= 52 ) {
| |
|
| |
| memo.avgMlist = memo.avgMlist.slice( memo.avgMlist.length - memo.gSmoothing/2 - 2 ).concat( memo.avgMlist ).concat( memo.avgMlist.slice( 0, memo.gSmoothing/2 + 2 ) );
| |
| memo.avgLlist = memo.avgLlist.slice( memo.avgLlist.length - memo.gSmoothing/2 - 2 ).concat( memo.avgLlist ).concat( memo.avgLlist.slice( 0, memo.gSmoothing/2 + 2 ) );
| |
| memo.avgHlist = memo.avgHlist.slice( memo.avgHlist.length - memo.gSmoothing/2 - 2 ).concat( memo.avgHlist ).concat( memo.avgHlist.slice( 0, memo.gSmoothing/2 + 2 ) );
| |
|
| |
| } else {
| |
|
| |
| for (var x=0; x < ( memo.gSmoothing/2 + 3 ); x++) {
| |
|
| |
| if ( memo.obscode == 'secchi' ) {
| |
| prev = Math.max ( memo.avgMlist[0], 0 );
| |
| next = Math.max ( memo.avgMlist[memo.avgMlist.length-1], 0 );
| |
| } else {
| |
| prev = Math.max ( 2 * memo.avgMlist[0] - memo.avgMlist[1], 0 );
| |
| next = Math.max ( 2 * memo.avgMlist[memo.avgMlist.length-1] - memo.avgMlist[memo.avgMlist.length-2], 0 );
| |
| }
| |
|
| |
| memo.avgMlist.unshift( prev );
| |
| memo.avgMlist.push( next );
| |
|
| |
| memo.avgLlist.unshift( prev - 2 * devAvg );
| |
| memo.avgLlist.push( next - 2 * devAvg );
| |
|
| |
| memo.avgHlist.unshift( prev + 2 * devAvg );
| |
| memo.avgHlist.push( next + 2 * devAvg );
| |
|
| |
|
| |
| }
| |
|
| |
| }
| |
|
| |
| memo.avgMsmooth = smooth( memo.avgMlist, memo.gSmoothing );
| |
| memo.avgLsmooth = smooth( memo.avgLlist, memo.gSmoothing );
| |
| memo.avgHsmooth = smooth( memo.avgHlist, memo.gSmoothing );
| |
|
| |
|
| |
| //console.log(memo.avgMlist);
| |
| //console.log(memo.avgLsmooth);
| |
|
| |
| cc = memo.data.getNumberOfColumns();
| |
|
| |
| msW = 7*24*60*60*1000;
| |
|
| |
| for ( var i=0; i < ( memo.avgMsmooth.length ); i++ ) {
| |
|
| |
| // put data into dataArr
| |
| dataRow = [];
| |
| for (var e=0; e<cc; e++) { dataRow.push( null ); }
| |
|
| |
| dataRow[0] = new Date( memo.avgW[firstY][firstW].cDate.getTime() + (i - 1)*msW );
| |
|
| |
| //dataRow[cc-4] = memo.avgMlist[i];
| |
|
| |
| dataRow[cc-4] = memo.avgMsmooth[i];
| |
| dataRow[cc-3] = memo.avgLsmooth[i];
| |
| dataRow[cc-2] = memo.avgHsmooth[i];
| |
|
| |
| //dataRow[cc-3] = Math.min( memo.avgLsmooth[i], memo.avgMlist[i+memo.gSmoothing/2+2] );
| |
| //dataRow[cc-2] = Math.max( memo.avgHsmooth[i], memo.avgMlist[i+1] );
| |
|
| |
| memo.dataA.push( dataRow );
| |
| }
| |
|
| |
| memo.options.intervals = { 'style':'area' };
| |
|
| |
|
| |
| //console.log(memo.dataA);
| |
|
| |
| } else if (memo.trendType == 'moving') {
| |
|
| |
| //
| |
| }
| |
|
| |
| // add data to table
| |
| cc = memo.data.getNumberOfColumns();
| |
| for (var s in memo.seasonsA) {
| |
|
| |
| for (var o in memo.seasonsO[memo.seasonsA[s]].obses) {
| |
|
| |
| obs = memo.seasonsO[memo.seasonsA[s]].obses[o];
| |
|
| |
| dataRow = [];
| |
|
| |
| // fill row with nulls
| |
| for (var i=0; i<cc; i++) { dataRow.push( null ); }
| |
|
| |
| // x axis value
| |
| dataRow[0] = obs.compareDate;
| |
|
| |
| // data for series cols
| |
| if ( memo.seasonsA[s] == memo.curSeason ) {
| |
| dataRow[s*4+1] = obs.value;
| |
| } else {
| |
| dataRow[s*4+1] = obs.value + memo.distF*Math.random() - memo.distF*Math.random();
| |
| }
| |
| dataRow[s*4+2] = obs.style;
| |
| dataRow[s*4+3] = obs.popup;
| |
| dataRow[s*4+4] = null; //annotation
| |
|
| |
| memo.dataA.push( dataRow );
| |
|
| |
| }
| |
|
| |
| }
| |
|
| |
| // make ticks for x axis
| |
|
| |
| var minM = memo.axisMinDate.getMonth();
| |
| var minY = memo.axisMinDate.getFullYear();
| |
| var maxM = memo.axisMaxDate.getMonth();
| |
|
| |
| if (maxM < minM) {
| |
| mCount = 13 - minM + maxM; // 13-10+4 = 1+4
| |
| } else {
| |
| mCount = maxM - minM + 1;
| |
| }
| |
|
| |
| memo.options.hAxis.viewWindow.min = memo.axisMinDate;
| |
| memo.options.hAxis.viewWindow.max = memo.axisMaxDate;
| |
| memo.options.hAxis.ticks = [];
| |
|
| |
| for (i=0; i<mCount; i++) {
| |
|
| |
| m = minM + i;
| |
| maxY = minY;
| |
|
| |
| if (m > 11) {
| |
| m = minM + i - 12;
| |
| maxY++;
| |
| }
| |
|
| |
| middleOfMonth = new Date( maxY, m, 16 );
| |
|
| |
| if (mCount < 5) {
| |
| memo.options.hAxis.ticks.push({v: middleOfMonth, f: i18n.months[m]['long'][memo.lang] });
| |
| } else if ( mCount < 10 ) {
| |
| memo.options.hAxis.ticks.push({v: middleOfMonth, f: i18n.months[m]['short'][memo.lang] });
| |
| } else {
| |
| memo.options.hAxis.ticks.push({v: middleOfMonth, f: i18n.months[m]['roman'] });
| |
| }
| |
| }
| |
|
| |
| drawChart(memo);
| |
|
| |
| }
| |
|
| |
|
| |
| function drawChart(memo) { | | function drawChart(memo) { |
|
| |
|