New VDEF functions for least squares slope, intercept & correlation for simple foreca...
[rrdtool.git] / src / rrd_graph.c
index 0572241..3ce3959 100644 (file)
@@ -1,5 +1,5 @@
 /****************************************************************************
- * RRDtool 1.2.10  Copyright by Tobi Oetiker, 1997-2005
+ * RRDtool 1.2.11  Copyright by Tobi Oetiker, 1997-2005
  ****************************************************************************
  * rrd__graph.c  produce graphs from data in rrdfiles
  ****************************************************************************/
@@ -469,7 +469,7 @@ apply_gridfit(image_desc_t *im)
       double new_log10_range = factor * log10_range;
       double new_ymax_log10 = log10(im->minval) + new_log10_range;
       im->maxval = pow(10, new_ymax_log10);
-      ytr(im, DNAN); /* reset precalc */
+      ytr(im,DNAN); /* reset precalc */
       log10_range = log10(im->maxval) - log10(im->minval);
     }
     /* make sure first y=10^x gridline is located on 
@@ -481,7 +481,7 @@ apply_gridfit(image_desc_t *im)
       double yfrac = ypixfrac / im->ysize;
       im->minval = pow(10, log10(im->minval) - yfrac * log10_range);
       im->maxval = pow(10, log10(im->maxval) - yfrac * log10_range);
-      ytr(im, DNAN); /* reset precalc */
+      ytr(im,DNAN); /* reset precalc */
     }
   } else {
     /* Make sure we have an integer pixel distance between
@@ -494,7 +494,7 @@ apply_gridfit(image_desc_t *im)
     double gridstep = im->ygrid_scale.gridstep;
     double minor_y, minor_y_px, minor_y_px_frac;
     im->maxval = im->minval + new_range;
-    ytr(im, DNAN); /* reset precalc */
+    ytr(im,DNAN); /* reset precalc */
     /* make sure first minor gridline is on integer pixel y coord */
     minor_y = gridstep * floor(im->minval / gridstep);
     while (minor_y < im->minval)
@@ -506,7 +506,7 @@ apply_gridfit(image_desc_t *im)
       double range = im->maxval - im->minval;
       im->minval = im->minval - yfrac * range;
       im->maxval = im->maxval - yfrac * range;
-      ytr(im, DNAN); /* reset precalc */
+      ytr(im,DNAN); /* reset precalc */
     }
     calc_horizontal_grid(im); /* recalc with changed im->maxval */
   }
@@ -2151,6 +2151,7 @@ graph_size_location(image_desc_t *im, int elements
        im->ximg = im->xsize;
         im->yimg = im->ysize;
         im->yorigin = im->ysize;
+        ytr(im,DNAN); 
        return 0;
     }
 
@@ -2241,7 +2242,7 @@ graph_size_location(image_desc_t *im, int elements
     /* reserve space for main and/or pie */
 
     im->yimg = Ymain + Yxlabel;
-
+    
 #ifdef WITH_PIECHART
     if (im->yimg < Ypie) im->yimg = Ypie;
 #endif
@@ -2258,8 +2259,7 @@ graph_size_location(image_desc_t *im, int elements
     }
     /* reserve space for padding below the graph */
     im->yimg += Yspacing;
-    ytr(im,DNAN);
-
+     
     /* Determine where to place the legends onto the image.
     ** Adjust im->yimg to match the space requirements.
     */
@@ -2288,6 +2288,7 @@ graph_size_location(image_desc_t *im, int elements
     }
 #endif
 
+    ytr(im,DNAN);
     return 0;
 }
 
@@ -2471,45 +2472,86 @@ graph_paint(image_desc_t *im, char ***calcpr)
       if (im->gdes[i].col != 0x0){   
         /* GF_LINE and friend */
         if(stack_gf == GF_LINE ){
+          double last_y=0;
           node = NULL;
-          for(ii=1;ii<im->xsize;ii++){     
+          for(ii=1;ii<im->xsize;ii++){
            if (isnan(im->gdes[i].p_data[ii]) || (im->slopemode==1 && isnan(im->gdes[i].p_data[ii-1]))){
                node = NULL;
                continue;
            }
             if ( node == NULL ) {
+               last_y = ytr(im,im->gdes[i].p_data[ii]);
                if ( im->slopemode == 0 ){
                   node = gfx_new_line(im->canvas,
-                                    ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
-                                    ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
+                                    ii-1+im->xorigin,last_y,
+                                    ii+im->xorigin,last_y,
                                     im->gdes[i].linewidth,
                                     im->gdes[i].col);
                } else {
                   node = gfx_new_line(im->canvas,
                                     ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii-1]),
-                                    ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]),
+                                    ii+im->xorigin,last_y,
                                     im->gdes[i].linewidth,
                                     im->gdes[i].col);
                }
              } else {
-              if ( im->slopemode==0 ){
-                   gfx_add_point(node,ii-1+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
+               double new_y = ytr(im,im->gdes[i].p_data[ii]);
+              if ( im->slopemode==0 && new_y != last_y){
+                   gfx_add_point(node,ii-1+im->xorigin,new_y);
+                   last_y = new_y;
               };
-               gfx_add_point(node,ii+im->xorigin,ytr(im,im->gdes[i].p_data[ii]));
+               gfx_add_point(node,ii+im->xorigin,new_y);
              };
 
           }
         } else {
-         double ybase0 = DNAN,ytop0=DNAN;
-          for(ii=0;ii<im->xsize;ii++){
+         int idxI=-1;
+         double *foreY=malloc(sizeof(double)*im->xsize*2);
+         double *foreX=malloc(sizeof(double)*im->xsize*2);
+         double *backY=malloc(sizeof(double)*im->xsize*2);
+         double *backX=malloc(sizeof(double)*im->xsize*2);
+         int drawem = 0;
+          for(ii=0;ii<=im->xsize;ii++){
+           double ybase,ytop;
+           if ( idxI > 0 && ( drawem != 0 || ii==im->xsize)){
+               int cntI=1;
+               int lastI=0;
+               while (cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;}
+               node = gfx_new_area(im->canvas,
+                                backX[0],backY[0],
+                                foreX[0],foreY[0],
+                                foreX[cntI],foreY[cntI], im->gdes[i].col);
+               while (cntI < idxI) {
+                 lastI = cntI;
+                 cntI++;
+                 while ( cntI < idxI && foreY[lastI] == foreY[cntI] && foreY[lastI] == foreY[cntI+1]){cntI++;} 
+                 gfx_add_point(node,foreX[cntI],foreY[cntI]);
+               }
+               gfx_add_point(node,backX[idxI],backY[idxI]);
+               while (idxI > 1){
+                 lastI = idxI;
+                 idxI--;
+                 while ( idxI > 1 && backY[lastI] == backY[idxI] && backY[lastI] == backY[idxI-1]){idxI--;} 
+                 gfx_add_point(node,backX[idxI],backY[idxI]);
+               }
+               idxI=-1;
+               drawem = 0;
+            }
+            if (drawem != 0){
+              drawem = 0;
+              idxI=-1;
+            }
+            if (ii == im->xsize) break;
+            
            /* keep things simple for now, just draw these bars
               do not try to build a big and complex area */
-           double ybase,ytop;
+
+                                                             
            if ( im->slopemode == 0 && ii==0){
                continue;
            }
            if ( isnan(im->gdes[i].p_data[ii]) ) {
-               ybase0 = DNAN;
+               drawem = 1;
                continue;
            }
             ytop = ytr(im,im->gdes[i].p_data[ii]);
@@ -2519,7 +2561,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
                   ybase = ytr(im,areazero);
             }
             if ( ybase == ytop ){
-               ybase0 = DNAN;
+               drawem = 1;
                continue;       
            }
            /* every area has to be wound clock-wise,
@@ -2529,24 +2571,22 @@ graph_paint(image_desc_t *im, char ***calcpr)
                ytop = ybase;
                ybase = extra;
            }
-           if ( im->slopemode == 0){
-                ybase0 = ybase;
-                ytop0 = ytop;
-           }
-           if ( !isnan(ybase0) ){
-                   node = gfx_new_area(im->canvas,
-                                (double)ii-1.2+(double)im->xorigin,ybase0-0.2,
-                                (double)ii-1.2+(double)im->xorigin,ytop0+0.2,
-                                (double)ii+0.2+(double)im->xorigin,ytop+0.2,
-                                im->gdes[i].col
-                               );
-                   gfx_add_point(node,
-                               (double)ii+0.02+im->xorigin,ybase-0.2
-                              );
+            if ( im->slopemode == 0 ){
+                    backY[++idxI] = ybase-0.2;
+                    backX[idxI] = ii+im->xorigin-1;
+                    foreY[idxI] = ytop+0.2;
+                    foreX[idxI] = ii+im->xorigin-1;
             }
-           ybase0=ybase;
-           ytop0=ytop;
-          }             
+            backY[++idxI] = ybase-0.2;
+            backX[idxI] = ii+im->xorigin;
+            foreY[idxI] = ytop+0.2;
+            foreX[idxI] = ii+im->xorigin;
+          }
+          /* close up any remaining area */             
+          free(foreY);
+          free(foreX);
+          free(backY);
+          free(backX);
         } /* else GF_LINE */
       } /* if color != 0x0 */
       /* make sure we do not run into trouble when stacking on NaN */
@@ -2555,7 +2595,7 @@ graph_paint(image_desc_t *im, char ***calcpr)
           if (lastgdes && (im->gdes[i].stack)) {
             im->gdes[i].p_data[ii] = lastgdes->p_data[ii];
           } else {
-            im->gdes[i].p_data[ii] =  ytr(im,areazero);
+            im->gdes[i].p_data[ii] = areazero;
           }
         }
       } 
@@ -3146,11 +3186,11 @@ rrd_graph_options(int argc, char *argv[],image_desc_t *im)
         case 'n':{
            char prop[15];
            double size = 1;
-           char font[1024];
+           char font[1024] = "";
 
            if(sscanf(optarg,
                                "%10[A-Z]:%lf:%1000s",
-                               prop,&size,font) == 3){
+                               prop,&size,font) >= 2){
                int sindex,propidx;
                if((sindex=text_prop_conv(prop)) != -1){
                   for (propidx=sindex;propidx<TEXT_PROP_LAST;propidx++){                     
@@ -3366,6 +3406,9 @@ char *str;
     else if    (!strcmp("TOTAL",  func)) gdes->vf.op = VDEF_TOTAL;
     else if    (!strcmp("FIRST",  func)) gdes->vf.op = VDEF_FIRST;
     else if    (!strcmp("LAST",   func)) gdes->vf.op = VDEF_LAST;
+    else if     (!strcmp("LSLSLOPE", func)) gdes->vf.op = VDEF_LSLSLOPE;
+    else if     (!strcmp("LSLINT",   func)) gdes->vf.op = VDEF_LSLINT;
+    else if     (!strcmp("LSLCORREL",func)) gdes->vf.op = VDEF_LSLCORREL;
     else {
        rrd_set_error("Unknown function '%s' in VDEF '%s'\n"
            ,func
@@ -3401,6 +3444,9 @@ char *str;
        case VDEF_TOTAL:
        case VDEF_FIRST:
        case VDEF_LAST:
+       case VDEF_LSLSLOPE:
+       case VDEF_LSLINT:
+       case VDEF_LSLCORREL:
            if (isnan(param)) {
                gdes->vf.param = DNAN;
                gdes->vf.val   = DNAN;
@@ -3559,6 +3605,48 @@ printf("DEBUG: %3li:%10.2f %c\n",step,array[step],step==field?'*':' ');
                dst->vf.when = src->start + (step+1)*src->step;
            }
            break;
+       case VDEF_LSLSLOPE:
+       case VDEF_LSLINT:
+       case VDEF_LSLCORREL:{
+           /* Bestfit line by linear least squares method */ 
+
+           int cnt=0;
+           double SUMx, SUMy, SUMxy, SUMxx, SUMyy, slope, y_intercept, correl ;
+           SUMx = 0; SUMy = 0; SUMxy = 0; SUMxx = 0; SUMyy = 0;
+
+           for (step=0;step<steps;step++) {
+               if (finite(data[step*src->ds_cnt])) {
+                   cnt++;
+                   SUMx  += step;
+                   SUMxx += step * step;
+                   SUMxy += step * data[step*src->ds_cnt];
+                   SUMy  += data[step*src->ds_cnt];
+                   SUMyy  += data[step*src->ds_cnt]*data[step*src->ds_cnt];
+               };
+           }
+
+           slope = ( SUMx*SUMy - cnt*SUMxy ) / ( SUMx*SUMx - cnt*SUMxx );
+           y_intercept = ( SUMy - slope*SUMx ) / cnt;
+           correl = (SUMxy - (SUMx*SUMy)/cnt) / sqrt((SUMxx - (SUMx*SUMx)/cnt)*(SUMyy - (SUMy*SUMy)/cnt));
+
+           if (cnt) {
+                   if (dst->vf.op == VDEF_LSLSLOPE) {
+                       dst->vf.val  = slope;
+                       dst->vf.when = cnt*src->step;
+                   } else if (dst->vf.op == VDEF_LSLINT)  {
+                       dst->vf.val = y_intercept;
+                       dst->vf.when = cnt*src->step;
+                   } else if (dst->vf.op == VDEF_LSLCORREL)  {
+                       dst->vf.val = correl;
+                       dst->vf.when = cnt*src->step;
+                   };
+               
+           } else {
+               dst->vf.val  = DNAN;
+               dst->vf.when = 0;
+           }
+           }
+           break;
     }
     return 0;
 }