Ero sivun ”Widget:Test4” versioiden välillä
Rivi 1: | Rivi 1: | ||
− | + | 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 | + | // add to series |
− | memo. | + | 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 { | } 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. | + | 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' }; | |
− | memo. | ||
} else { | } else { | ||
− | memo. | + | 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') { | if ( i18n[memo.obscode]['type'] == 'cat') { | ||
− | + | memo.distF = 0.1; | |
− | |||
− | |||
} else { | } 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 = []; | dataRow = []; | ||
Rivi 775: | Rivi 310: | ||
// x axis value | // x axis value | ||
− | dataRow[0] = obs. | + | dataRow[0] = obs.compareDate; |
− | + | ||
// data for series cols | // data for series cols | ||
− | if ( memo. | + | if ( memo.seasonsA[s] == memo.curSeason ) { |
− | dataRow[s*4+1] = obs. | + | dataRow[s*4+1] = obs.value; |
} else { | } else { | ||
− | dataRow[s*4+1] = obs. | + | dataRow[s*4+1] = obs.value + memo.distF*Math.random() - memo.distF*Math.random(); |
} | } | ||
dataRow[s*4+2] = obs.style; | dataRow[s*4+2] = obs.style; | ||
− | dataRow[s*4+3] = obs. | + | dataRow[s*4+3] = obs.popup; |
− | dataRow[s*4+4] = | + | dataRow[s*4+4] = null; //annotation |
memo.dataA.push( dataRow ); | memo.dataA.push( dataRow ); | ||
Rivi 791: | Rivi 326: | ||
} | } | ||
− | } | + | } |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
// make ticks for x axis | // make ticks for x axis | ||
Rivi 839: | Rivi 364: | ||
} | } | ||
} | } | ||
− | |||
− | |||
drawChart(memo); | drawChart(memo); | ||
} | } | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− |
Versio 29. joulukuuta 2021 kello 11.03
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);
}