New VDEF functions for least squares slope, intercept & correlation for simple foreca...
[rrdtool.git] / src / rrd_graph.c
index a06417e..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
  ****************************************************************************/
@@ -2242,13 +2242,12 @@ 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
 
     im->yorigin = im->yimg - Yxlabel;
-    ytr(im,DNAN); 
 
     /* reserve space for the title *or* some padding above the graph */
     if (Ytitle) {
@@ -2260,7 +2259,7 @@ graph_size_location(image_desc_t *im, int elements
     }
     /* reserve space for padding below the graph */
     im->yimg += Yspacing;
-
+     
     /* Determine where to place the legends onto the image.
     ** Adjust im->yimg to match the space requirements.
     */
@@ -2289,6 +2288,7 @@ graph_size_location(image_desc_t *im, int elements
     }
 #endif
 
+    ytr(im,DNAN);
     return 0;
 }
 
@@ -2472,31 +2472,35 @@ 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);
              };
 
           }
@@ -3182,7 +3186,7 @@ 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",
@@ -3402,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
@@ -3437,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;
@@ -3595,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;
 }