[rrd-developers] patch for legends position

Tobias Oetiker tobi at oetiker.ch
Fri Mar 6 06:47:27 CET 2009


Hi Melchior,

cool ... looking at your sample graphs it seems that the watermark
at the bottom of the graph is runing into the labeling ...

http://mrab.de/wp-content/uploads/go_all_w___.png

Also your patch lacks a patch for doc/rrdgrah.pod

I will be glad to integrate an updated version.

cheers
tobi


Feb 9 Melchior Rabe wrote:

> Hello together,
>
> at the moment the legend section is placed below the graph. This is in
> the most cases fine as the time axis gets a much space as possible. But
> I want to graph data that is a set of stacked values (up to 25) and also
> want to print some informational numbers into the legend. In that case I
> find it more convinient placing the legend on the side of the graph
> (Possibly that might be also nice for the pie chart function...). I
> created a patch that is introducing two new options:
> --legend-position north|south|west|east (default: south)
> --legend-direction topdown|bottomup (default: topdown)
>
> The first option decides where the legend is placed and the second can
> be used to reverse the vertical order of the legend items. In a stack
> the first item (AREA, LINEx) is the bottommost and the following is
> stacked on top of it. The legend is placed in the same sequence. That
> means that the first item and the next one is placed (after a line
> break) below the preceeding ones. So the graph is stacked bottomup while
> the legend is "stacked" topdown. The --legend-direction is reversing the
> vertical order of the legend items.
>
>
> Some not so nice (but IMHO acceptable) side effects:
>
> - Placing the legend on the side will result in a very wide graph with
> all items in one line (as long as there is no manual line break
> inserted). So the user has to take care for the line breaks. I think
> that is acceptable, as any other solution I thought of (giving the width
> of the legend or defining some ratio between legend and graph) was
> leading to more parameters.
>
> - Placing the legend items bottomup might lead to an incomplete first
> line, because only the vertical alignment is inverted, but not the
> sequence of the items e.g. like that:
>
> topdown legend:
> first   second third
> forth   fifth  sixth
> seventh
>
> will lead to bottomup:
> seventh
> forth   fifth  sixth
> first   second third
>
> The patch is tested on WinXP32, but as there is only some painting
> affected I do not expect any problems on other systems. I wrote a short
> description with some example images (http://mrab.de/?p=64) and placed
> the patch below also in a zip at
> http://mrab.de/wp-content/uploads/rrdtool_patch_legend-position.zip
>
> Any comments, hints and tips are appreciated!
>
> Cheers,
>
> Melchior
>
> Patch for rrd_graph.h
> ----------------------- BEGIN ----------------------------------------
> Index: rrd_graph.h
> ===================================================================
> --- rrd_graph.h	(revision 1746)
> +++ rrd_graph.h	(working copy)
> @@ -81,6 +81,8 @@
>     TEXT_PROP_LAST
> };
>
> +enum legend_pos{ NORTH = 0, WEST, SOUTH, EAST };
> +enum legend_direction { TOP_DOWN = 0, BOTTOM_UP };
>
> enum gfx_if_en { IF_PNG = 0, IF_SVG, IF_EPS, IF_PDF };
> enum gfx_en { GFX_LINE = 0, GFX_AREA, GFX_TEXT };
> @@ -231,6 +233,8 @@
>                            reasonable probablility that the
>                            existing one is out of date */
>     int       slopemode;    /* connect the dots of the curve directly, not using a stair */
> +    legend_pos legendposition; /* the position of the legend: north, west, south or east */
> +    legend_direction legenddirection; /* The direction of the legend topdown or bottomup */
>     int       logarithmic;  /* scale the yaxis logarithmic */
>     double    force_scale_min;  /* Force a scale--min */
>     double    force_scale_max;  /* Force a scale--max */
> @@ -238,7 +242,12 @@
>     /* status information */
>     int       with_markup;
>     long      xorigin, yorigin; /* where is (0,0) of the graph */
> +    long      xOriginTitle, yOriginTitle; /* where is the origin of the title */
> +    long      xOriginLegendY, yOriginLegendY; /* where is the origin of the y legend */
> +    long      xOriginLegendY2, yOriginLegendY2; /* where is the origin of the second y legend */
> +    long      xOriginLegend, yOriginLegend; /* where is the origin of the legend */
>     long      ximg, yimg;   /* total size of the image */
> +    long      legendwidth, legendheight; /* the calculated height and width of the legend */
>     size_t    rendered_image_size;
>     double    zoom;
>     double    magfact;  /* numerical magnitude */
> ----------------------- END   ----------------------------------------
>
> Patch for rrd_graph.c
> ----------------------- BEGIN ----------------------------------------
> Index: rrd_graph.c
> ===================================================================
> --- rrd_graph.c	(revision 1746)
> +++ rrd_graph.c	(working copy)
> @@ -1613,26 +1613,37 @@
> }
>
>
> +
> /* place legends with color spots */
> int leg_place(
>     image_desc_t *im,
> -    int *gY)
> +    bool calc_width)
> {
>     /* graph labels */
>     int       interleg = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
>     int       border = im->text_prop[TEXT_PROP_LEGEND].size * 2.0;
>     int       fill = 0, fill_last;
> +    double    legendwidth; // = im->ximg - 2 * border;
>     int       leg_c = 0;
>     double    leg_x = border;
> -    int       leg_y = im->yimg;
> -    int       leg_y_prev = im->yimg;
> +    int       leg_y = 0; //im->yimg;
> +    int       leg_y_prev = 0; // im->yimg;
>     int       leg_cc;
>     double    glue = 0;
>     int       i, ii, mark = 0;
>     char      default_txtalign = TXA_JUSTIFIED; /*default line orientation */
>     int      *legspace;
>     char     *tab;
> +    char      saved_legend[FMT_LEG_LEN + 5];
>
> +    if(calc_width){
> +        legendwidth = 0;
> +    }
> +    else{
> +        legendwidth = im->legendwidth - 2 * border;
> +    }
> +
> +
>     if (!(im->extra_flags & NOLEGEND) & !(im->extra_flags & ONLY_GRAPH)) {
>         if ((legspace = (int*)(malloc(im->gdes_c * sizeof(int)))) == NULL) {
>             rrd_set_error("malloc for legspace");
> @@ -1641,6 +1652,10 @@
>
>         for (i = 0; i < im->gdes_c; i++) {
>             char      prt_fctn; /*special printfunctions */
> +            if(calc_width){
> +                strcpy(saved_legend, im->gdes[i].legend);
> +            }
> +
>             fill_last = fill;
>             /* hide legends for rules which are not displayed */
>             if (im->gdes[i].gf == GF_TEXTALIGN) {
> @@ -1663,6 +1678,7 @@
>                 memmove(tab, tab + 1, strlen(tab));
>                 tab[0] = (char) 9;
>             }
> +
>             leg_cc = strlen(im->gdes[i].legend);
>             /* is there a controle code at the end of the legend string ? */
>             if (leg_cc >= 2 && im->gdes[i].legend[leg_cc - 2] == '\\') {
> @@ -1721,7 +1737,10 @@
>             }
>
>             if (prt_fctn == '\0') {
> -                if (i == im->gdes_c - 1 || fill > im->ximg - 2 * border) {
> +                if(calc_width && (fill > legendwidth)){
> +                    legendwidth = fill;
> +                }
> +                if (i == im->gdes_c - 1 || fill > legendwidth) {
>                     /* just one legend item is left right or center */
>                     switch (default_txtalign) {
>                     case TXA_RIGHT:
> @@ -1739,7 +1758,7 @@
>                     }
>                 }
>                 /* is it time to place the legends ? */
> -                if (fill > im->ximg - 2 * border) {
> +                if (fill > legendwidth) {
>                     if (leg_c > 1) {
>                         /* go back one */
>                         i--;
> @@ -1752,23 +1771,22 @@
>                 }
>             }
>
> -
>             if (prt_fctn != '\0') {
>                 leg_x = border;
>                 if (leg_c >= 2 && prt_fctn == 'j') {
> -                    glue = (double)(im->ximg - fill - 2 * border) / (double)(leg_c - 1);
> +                    glue = (double)(legendwidth - fill) / (double)(leg_c - 1);
>                 } else {
>                     glue = 0;
>                 }
>                 if (prt_fctn == 'c')
> -                    leg_x = (double)(im->ximg - fill) / 2.0;
> +                    leg_x = (double)(legendwidth - fill) / 2.0;
>                 if (prt_fctn == 'r')
> -                    leg_x = im->ximg - fill - border;
> +                    leg_x = legendwidth - fill - border;
>                 for (ii = mark; ii <= i; ii++) {
>                     if (im->gdes[ii].legend[0] == '\0')
>                         continue;   /* skip empty legends */
>                     im->gdes[ii].leg_x = leg_x;
> -                    im->gdes[ii].leg_y = leg_y;
> +                    im->gdes[ii].leg_y = leg_y + border;
>                     leg_x +=
>                         (double)gfx_get_text_width(im, leg_x,
>                                            im->
> @@ -1784,27 +1802,26 @@
>                     leg_y += im->text_prop[TEXT_PROP_LEGEND].size * 1.8;
>                 if (prt_fctn == 's')
>                     leg_y -= im->text_prop[TEXT_PROP_LEGEND].size;
> +
> +                if(calc_width && (fill > legendwidth)){
> +                    legendwidth = fill;
> +                }
>                 fill = 0;
>                 leg_c = 0;
>                 mark = ii;
>             }
> -        }
>
> -        if (im->extra_flags & FULL_SIZE_MODE) {
> -            /* now for some backpaddeling. We have to shift up all the
> -               legend items into the graph and tell the caller about the
> -               space we used up. */
> -            long shift_up = leg_y - im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 + border * 0.7;
> -            for (i = 0; i < im->gdes_c; i++) {
> -                im->gdes[i].leg_y -= shift_up;
> +            if(calc_width){
> +                strcpy(im->gdes[i].legend, saved_legend);
>             }
> -            im->yorigin = im->yorigin - leg_y + im->yimg - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 - border;
> -            *gY = im->yorigin;
> -        } else {
> -            im->yimg =
> -                leg_y - im->text_prop[TEXT_PROP_LEGEND].size * 1.8 +
> -                border * 0.6;
>         }
> +
> +        if(calc_width){
> +            im->legendwidth = legendwidth + 2 * border;
> +        }
> +        else{
> +            im->legendheight = leg_y + border * 0.6;
> +        }
>         free(legspace);
>     }
>     return 0;
> @@ -2590,24 +2607,20 @@
>     /* yaxis unit description */
>     if (im->ylegend[0] != '\0'){
>         gfx_text(im,
> -                 10,
> -                 (im->yorigin -
> -                  im->ysize / 2),
> +                 im->xOriginLegendY+10,
> +                 im->yOriginLegendY,
>                  im->graph_col[GRC_FONT],
>                  im->
>                  text_prop[TEXT_PROP_UNIT].
>                  font_desc,
>                  im->tabwidth,
>                  RRDGRAPH_YLEGEND_ANGLE, GFX_H_CENTER, GFX_V_CENTER, im->ylegend);
> +
>     }
>     if (im->second_axis_legend[0] != '\0'){
> -            double Xylabel=gfx_get_text_width(im, 0,
> -                        im->text_prop[TEXT_PROP_AXIS].font_desc,
> -                        im->tabwidth,
> -                        "0") * im->unitslength
> -                    + im->text_prop[TEXT_PROP_UNIT].size *2;
>             gfx_text( im,
> -                  im->xorigin+im->xsize+Xylabel+8, (im->yorigin - im->ysize/2),
> +                  im->xOriginLegendY2+10,
> +                  im->yOriginLegendY2,
>                   im->graph_col[GRC_FONT],
>                   im->text_prop[TEXT_PROP_UNIT].font_desc,
>                   im->tabwidth,
> @@ -2618,7 +2631,7 @@
>
>     /* graph title */
>     gfx_text(im,
> -             im->ximg / 2, 6,
> +             im->xOriginTitle, im->yOriginTitle+6,
>              im->graph_col[GRC_FONT],
>              im->
>              text_prop[TEXT_PROP_TITLE].
> @@ -2628,7 +2641,8 @@
>     if (!(im->extra_flags & NO_RRDTOOL_TAG)){
>         water_color = im->graph_col[GRC_FONT];
>         water_color.alpha = 0.3;
> -        gfx_text(im, im->ximg - 4, 5,
> +        double xpos = im->legendposition == EAST ? im->xOriginLegendY : im->ximg - 4;
> +        gfx_text(im, xpos, 5,
>                  water_color,
>                  im->
>                  text_prop[TEXT_PROP_WATERMARK].
> @@ -2652,8 +2666,8 @@
>             if (im->gdes[i].legend[0] == '\0')
>                 continue;
>             /* im->gdes[i].leg_y is the bottom of the legend */
> -            X0 = im->gdes[i].leg_x;
> -            Y0 = im->gdes[i].leg_y;
> +            X0 = im->xOriginLegend + im->gdes[i].leg_x;
> +            Y0 = im->legenddirection == TOP_DOWN ? im->yOriginLegend + im->gdes[i].leg_y : im->yOriginLegend + im->legendheight - im->gdes[i].leg_y;
>             gfx_text(im, X0, Y0,
>                      im->graph_col[GRC_FONT],
>                      im->
> @@ -2770,22 +2784,12 @@
>     /* The actual size of the image to draw is determined from
>      ** several sources.  The size given on the command line is
>      ** the graph area but we need more as we have to draw labels
> -     ** and other things outside the graph area
> +     ** and other things outside the graph area. If the option
> +     ** --full-size-mode is selected the size defines the total
> +     ** image size and the size available for the graph is
> +     ** calculated.
>      */
>
> -    int       Xvertical = 0, Ytitle =
> -        0, Xylabel = 0, Xmain = 0, Ymain =
> -        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
> -
> -    if (im->extra_flags & ONLY_GRAPH) {
> -        im->xorigin = 0;
> -        im->ximg = im->xsize;
> -        im->yimg = im->ysize;
> -        im->yorigin = im->ysize;
> -        ytr(im, DNAN);
> -        return 0;
> -    }
> -
>     /** +---+-----------------------------------+
>      ** | y |...............graph title.........|
>      ** |   +---+-------------------------------+
> @@ -2806,10 +2810,33 @@
>      ** +---------------------------------------+
>      */
>
> +    int       Xvertical = 0, Xvertical2 = 0, Ytitle =
> +        0, Xylabel = 0, Xmain = 0, Ymain =
> +        0, Yxlabel = 0, Xspacing = 15, Yspacing = 15, Ywatermark = 4;
> +
> +    // no legends and no the shall be plotted it's easy
> +    if (im->extra_flags & ONLY_GRAPH) {
> +        im->xorigin = 0;
> +        im->ximg = im->xsize;
> +        im->yimg = im->ysize;
> +        im->yorigin = im->ysize;
> +        ytr(im, DNAN);
> +        return 0;
> +    }
> +
> +    // calculate the width of the left vertical legend
>     if (im->ylegend[0] != '\0') {
>         Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
>     }
>
> +    // calculate the width of the right vertical legend
> +    if (im->second_axis_legend[0] != '\0') {
> +        Xvertical2 = im->text_prop[TEXT_PROP_UNIT].size * 2;
> +    }
> +    else{
> +        Xvertical2 = Xspacing;
> +    }
> +
>     if (im->title[0] != '\0') {
>         /* The title is placed "inbetween" two text lines so it
>          ** automatically has some vertical spacing.  The horizontal
> @@ -2818,92 +2845,112 @@
>         /* if necessary, reduce the font size of the title until it fits the image width */
>         Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
>     }
> +    else{
> +        // we have no title; get a little clearing from the top
> +        Ytitle = 1.5 * Yspacing;
> +    }
>
>     if (elements) {
>         if (im->draw_x_grid) {
> +            // calculate the height of the horizontal labelling
>             Yxlabel = im->text_prop[TEXT_PROP_AXIS].size * 2.5;
>         }
>         if (im->draw_y_grid || im->forceleftspace) {
> +            // calculate the width of the vertical labelling
>             Xylabel =
>                 gfx_get_text_width(im, 0,
> -                                   im->
> -                                   text_prop
> -                                   [TEXT_PROP_AXIS].
> -                                   font_desc,
> +                                   im->text_prop[TEXT_PROP_AXIS].font_desc,
>                                    im->tabwidth, "0") * im->unitslength;
>         }
>     }
>
> +    // add some space to the labelling
> +    Xylabel += Xspacing;
> +
> +    /* If the legend is printed besides the graph the width has to be
> +     ** calculated first. Placing the legend north or south of the
> +     ** graph requires the width calculation first, so the legend is
> +     ** skipped for the moment.
> +     */
> +    im->legendheight = 0;
> +    im->legendwidth = 0;
> +    if (!(im->extra_flags & NOLEGEND)) {
> +        if(im->legendposition == WEST || im->legendposition == EAST){
> +            if (leg_place(im, true) == -1){
> +                return -1;
> +            }
> +        }
> +    }
> +
>     if (im->extra_flags & FULL_SIZE_MODE) {
> +
>         /* The actual size of the image to draw has been determined by the user.
>          ** The graph area is the space remaining after accounting for the legend,
>          ** the watermark, the axis labels, and the title.
>          */
> -        im->xorigin = 0;
>         im->ximg = im->xsize;
>         im->yimg = im->ysize;
> -        im->yorigin = im->ysize;
>         Xmain = im->ximg;
>         Ymain = im->yimg;
> +
>         /* Now calculate the total size.  Insert some spacing where
>            desired.  im->xorigin and im->yorigin need to correspond
>            with the lower left corner of the main graph area or, if
>            this one is not set, the imaginary box surrounding the
>            pie chart area. */
>         /* Initial size calculation for the main graph area */
> -        Xmain = im->ximg - Xylabel - 3 * Xspacing;
>
> -        im->xorigin = Xspacing + Xylabel;
> -
> -        if (Xvertical) {    /* unit description */
> -            Xmain -= Xvertical;
> -            im->xorigin += Xvertical;
> +        Xmain -= Xylabel;// + Xspacing;
> +        if((im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
> +            Xmain -= im->legendwidth;// + Xspacing;
>         }
> -
> -        /* adjust space for second axis */
>         if (im->second_axis_scale != 0){
> -            Xmain -= Xylabel + Xspacing;
> +            Xmain -= Xylabel;
>         }
> -        if (im->extra_flags & NO_RRDTOOL_TAG){
> -            Xmain += Xspacing;
> +        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
> +            Xmain -= Xspacing;
>         }
> -        if (im->second_axis_legend[0] != '\0' ) {
> -            Xmain -= im->text_prop[TEXT_PROP_UNIT].size * 1.5;
> +
> +        Xmain -= Xvertical + Xvertical2;
> +
> +        /* limit the remaining space to 0 */
> +        if(Xmain < 1){
> +            Xmain = 1;
>         }
> -
>         im->xsize = Xmain;
>
> -        xtr(im, 0);
> -        /* The vertical size of the image is known in advance.  The main graph area
> -         ** (Ymain) and im->yorigin must be set according to the space requirements
> -         ** of the legend and the axis labels.
> -         */
> -        if (im->extra_flags & NOLEGEND) {
> -            im->yorigin = im->yimg -
> -                im->text_prop[TEXT_PROP_AXIS].size * 2.5 - Yspacing;
> -            Ymain = im->yorigin;
> +        /* Putting the legend north or south, the height can now be calculated */
> +        if (!(im->extra_flags & NOLEGEND)) {
> +            if(im->legendposition == NORTH || im->legendposition == SOUTH){
> +                im->legendwidth = im->ximg;
> +                if (leg_place(im, false) == -1){
> +                    return -1;
> +                }
> +            }
>         }
> -        else {
> -            /* Determine where to place the legends onto the image.
> -             ** Set Ymain and adjust im->yorigin to match the space requirements.
> -             */
> -            if (leg_place(im, &Ymain) == -1)
> -                return -1;
> +
> +        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
> +            Ymain -=  Yxlabel + im->legendheight;
>         }
> +        else{
> +            Ymain -= Yxlabel;
> +        }
> +
> +        /* reserve space for the title *or* some padding above the graph */
> +        Ymain -= Ytitle;
>
> -
> -        /* remove title space *or* some padding above the graph from the main graph area */
> -        if (Ytitle) {
> -            Ymain -= Ytitle;
> -        } else {
> -            Ymain -= 1.5 * Yspacing;
> +            /* reserve space for padding below the graph */
> +        if (im->extra_flags & NOLEGEND) {
> +            Ymain -= Yspacing;
>         }
>
> -        /* watermark doesn't seem to effect the vertical size of the main graph area, oh well! */
>         if (im->watermark[0] != '\0') {
>             Ymain -= Ywatermark;
>         }
> -
> +        /* limit the remaining height to 0 */
> +        if(Ymain < 1){
> +            Ymain = 1;
> +        }
>         im->ysize = Ymain;
>     } else {            /* dimension options -width and -height refer to the dimensions of the main graph area */
>
> @@ -2913,94 +2960,171 @@
>          ** and other things outside the graph area.
>          */
>
> -        if (im->ylegend[0] != '\0') {
> -            Xvertical = im->text_prop[TEXT_PROP_UNIT].size * 2;
> +        if (elements) {
> +            Xmain = im->xsize; // + Xspacing;
> +            Ymain = im->ysize;
>         }
>
> -
> -        if (im->title[0] != '\0') {
> -            /* The title is placed "inbetween" two text lines so it
> -             ** automatically has some vertical spacing.  The horizontal
> -             ** spacing is added here, on each side.
> -             */
> -            /* don't care for the with of the title
> -               Xtitle = gfx_get_text_width(im->canvas, 0,
> -               im->text_prop[TEXT_PROP_TITLE].font_desc,
> -               im->tabwidth,
> -               im->title, 0) + 2*Xspacing; */
> -            Ytitle = im->text_prop[TEXT_PROP_TITLE].size * 2.6 + 10;
> +        im->ximg = Xmain + Xylabel;
> +        if (!(im->extra_flags & NO_RRDTOOL_TAG)){
> +            im->ximg += Xspacing;
>         }
>
> -        if (elements) {
> -            Xmain = im->xsize;
> -            Ymain = im->ysize;
> +        if( (im->legendposition == WEST || im->legendposition == EAST) && !(im->extra_flags & NOLEGEND) ){
> +            im->ximg += im->legendwidth;// + Xspacing;
>         }
> -        /* Now calculate the total size.  Insert some spacing where
> -           desired.  im->xorigin and im->yorigin need to correspond
> -           with the lower left corner of the main graph area or, if
> -           this one is not set, the imaginary box surrounding the
> -           pie chart area. */
> +        if (im->second_axis_scale != 0){
> +            im->ximg += Xylabel;
> +        }
>
> -        /* The legend width cannot yet be determined, as a result we
> -         ** have problems adjusting the image to it.  For now, we just
> -         ** forget about it at all; the legend will have to fit in the
> -         ** size already allocated.
> -         */
> -        im->ximg = Xylabel + Xmain + 2 * Xspacing;
> +        im->ximg += Xvertical + Xvertical2;
>
> -        if (im->second_axis_scale != 0){
> -            im->ximg += Xylabel + Xspacing;
> +        if (!(im->extra_flags & NOLEGEND)) {
> +            if(im->legendposition == NORTH || im->legendposition == SOUTH){
> +                im->legendwidth = im->ximg;
> +                if (leg_place(im, false) == -1){
> +                    return -1;
> +                }
> +            }
>         }
> -        if (im->extra_flags & NO_RRDTOOL_TAG){
> -            im->ximg -= Xspacing;
> +
> +        im->yimg = Ymain + Yxlabel;
> +        if( (im->legendposition == NORTH || im->legendposition == SOUTH)  && !(im->extra_flags & NOLEGEND) ){
> +             im->yimg += im->legendheight;
>         }
>
> -        if (Xmain)
> -            im->ximg += Xspacing;
> -        im->xorigin = Xspacing + Xylabel;
> -        /* the length of the title should not influence with width of the graph
> -           if (Xtitle > im->ximg) im->ximg = Xtitle; */
> -        if (Xvertical) {    /* unit description */
> -            im->ximg += Xvertical;
> -            im->xorigin += Xvertical;
> -        }
> -        if (im->second_axis_legend[0] != '\0' ) {
> -            im->ximg += Xvertical;
> -        }
> -
> -        xtr(im, 0);
> -        /* The vertical size is interesting... we need to compare
> -         ** the sum of {Ytitle, Ymain, Yxlabel, Ylegend, Ywatermark} with
> -         ** Yvertical however we need to know {Ytitle+Ymain+Yxlabel}
> -         ** in order to start even thinking about Ylegend or Ywatermark.
> -         **
> -         ** Do it in three portions: First calculate the inner part,
> -         ** then do the legend, then adjust the total height of the img,
> -         ** adding space for a watermark if one exists;
> -         */
> -        /* reserve space for main and/or pie */
> -        im->yimg = Ymain + Yxlabel;
> -        im->yorigin = im->yimg - Yxlabel;
>         /* reserve space for the title *or* some padding above the graph */
>         if (Ytitle) {
>             im->yimg += Ytitle;
> -            im->yorigin += Ytitle;
>         } else {
>             im->yimg += 1.5 * Yspacing;
> -            im->yorigin += 1.5 * Yspacing;
>         }
>         /* 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.
> -         */
> -        if (leg_place(im, 0) == -1)
> -            return -1;
> +        if (im->extra_flags & NOLEGEND) {
> +            im->yimg += Yspacing;
> +        }
> +
>         if (im->watermark[0] != '\0') {
>             im->yimg += Ywatermark;
>         }
>     }
>
> +
> +    /* In case of putting the legend in west or east position the first
> +     ** legend calculation might lead to wrong positions if some items
> +     ** are not aligned on the left hand side (e.g. centered) as the
> +     ** legendwidth wight have been increased after the item was placed.
> +     ** In this case the positions have to be recalculated.
> +     */
> +    if (!(im->extra_flags & NOLEGEND)) {
> +        if(im->legendposition == WEST || im->legendposition == EAST){
> +            if (leg_place(im, false) == -1){
> +                return -1;
> +            }
> +        }
> +    }
> +
> +    /* After calculating all dimensions
> +     ** it is now possible to calculate
> +     ** all offsets.
> +     */
> +    switch(im->legendposition){
> +        case NORTH:
> +            im->xOriginTitle   = Xvertical + Xylabel + (im->xsize / 2);
> +            im->yOriginTitle   = 0;
> +
> +            im->xOriginLegend  = 0;
> +            im->yOriginLegend  = Ytitle;
> +
> +            im->xOriginLegendY = 0;
> +            im->yOriginLegendY = Ytitle + im->legendheight + (Ymain + Yxlabel) / 2;
> +
> +            im->xorigin        = Xvertical + Xylabel;
> +            im->yorigin        = Ytitle + im->legendheight + Ymain;
> +
> +            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
> +            if (im->second_axis_scale != 0){
> +                im->xOriginLegendY2 += Xylabel;
> +            }
> +            im->yOriginLegendY2 = Ytitle + im->legendheight + (Ymain + Yxlabel) / 2;
> +
> +            break;
> +
> +        case WEST:
> +            im->xOriginTitle   = im->legendwidth + Xvertical + Xylabel + im->xsize / 2;
> +            im->yOriginTitle   = 0;
> +
> +            im->xOriginLegend  = 0;
> +            im->yOriginLegend  = Ytitle;
> +
> +            im->xOriginLegendY = im->legendwidth;
> +            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            im->xorigin        = im->legendwidth + Xvertical + Xylabel;
> +            im->yorigin        = Ytitle + Ymain;
> +
> +            im->xOriginLegendY2 = im->legendwidth + Xvertical + Xylabel + Xmain;
> +            if (im->second_axis_scale != 0){
> +                im->xOriginLegendY2 += Xylabel;
> +            }
> +            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            break;
> +
> +        case SOUTH:
> +            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
> +            im->yOriginTitle   = 0;
> +
> +            im->xOriginLegend  = 0;
> +            im->yOriginLegend  = Ytitle + Ymain + Yxlabel;
> +
> +            im->xOriginLegendY = 0;
> +            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            im->xorigin        = Xvertical + Xylabel;
> +            im->yorigin        = Ytitle + Ymain;
> +
> +            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
> +            if (im->second_axis_scale != 0){
> +                im->xOriginLegendY2 += Xylabel;
> +            }
> +            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            break;
> +
> +        case EAST:
> +            im->xOriginTitle   = Xvertical + Xylabel + im->xsize / 2;
> +            im->yOriginTitle   = 0;
> +
> +            im->xOriginLegend  = Xvertical + Xylabel + Xmain + Xvertical2;
> +            if (im->second_axis_scale != 0){
> +                im->xOriginLegend += Xylabel;
> +            }
> +            im->yOriginLegend  = Ytitle;
> +
> +            im->xOriginLegendY = 0;
> +            im->yOriginLegendY = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            im->xorigin        = Xvertical + Xylabel;
> +            im->yorigin        = Ytitle + Ymain;
> +
> +            im->xOriginLegendY2 = Xvertical + Xylabel + Xmain;
> +            if (im->second_axis_scale != 0){
> +                im->xOriginLegendY2 += Xylabel;
> +            }
> +            im->yOriginLegendY2 = (Ytitle + Ymain + Yxlabel) / 2;
> +
> +            if (!(im->extra_flags & NO_RRDTOOL_TAG)){
> +                im->xOriginTitle    += Xspacing;
> +                im->xOriginLegend   += Xspacing;
> +                im->xOriginLegendY  += Xspacing;
> +                im->xorigin         += Xspacing;
> +                im->xOriginLegendY2 += Xspacing;
> +            }
> +            break;
> +    }
> +
> +    xtr(im, 0);
>     ytr(im, DNAN);
>     return 0;
> }
> @@ -3827,6 +3951,10 @@
>     im->imgformat = IF_PNG;
>     im->imginfo = NULL;
>     im->lazy = 0;
> +    im->legenddirection = TOP_DOWN;
> +    im->legendheight = 0;
> +    im->legendposition = SOUTH;
> +    im->legendwidth = 0;
>     im->logarithmic = 0;
>     im->maxval = DNAN;
>     im->minval = 0;
> @@ -3848,6 +3976,10 @@
>     im->ximg = 0;
>     im->xlab_user.minsec = -1;
>     im->xorigin = 0;
> +    im->xOriginLegend = 0;
> +    im->xOriginLegendY = 0;
> +    im->xOriginLegendY2 = 0;
> +    im->xOriginTitle = 0;
>     im->xsize = 400;
>     im->ygridstep = DNAN;
>     im->yimg = 0;
> @@ -3857,6 +3989,10 @@
>     im->second_axis_legend[0] = '\0';
>     im->second_axis_format[0] = '\0';
>     im->yorigin = 0;
> +    im->yOriginLegend = 0;
> +    im->yOriginLegendY = 0;
> +    im->yOriginLegendY2 = 0;
> +    im->yOriginTitle = 0;
>     im->ysize = 100;
>     im->zoom = 1;
>
> @@ -3941,6 +4077,8 @@
>         { "lazy",               no_argument,       0, 'z'},
>         { "zoom",               required_argument, 0, 'm'},
>         { "no-legend",          no_argument,       0, 'g'},
> +        { "legend-position",    required_argument, 0, 1005},
> +        { "legend-direction",   required_argument, 0, 1006},
>         { "force-rules-legend", no_argument,       0, 'F'},
>         { "only-graph",         no_argument,       0, 'j'},
>         { "alt-y-grid",         no_argument,       0, 'Y'},
> @@ -4005,6 +4143,30 @@
>         case 'g':
>             im->extra_flags |= NOLEGEND;
>             break;
> +        case 1005:
> +            if (strcmp(optarg, "north") == 0) {
> +                im->legendposition = NORTH;
> +            } else if (strcmp(optarg, "west") == 0) {
> +                im->legendposition = WEST;
> +            } else if (strcmp(optarg, "south") == 0) {
> +                im->legendposition = SOUTH;
> +            } else if (strcmp(optarg, "east") == 0) {
> +                im->legendposition = EAST;
> +            } else {
> +                rrd_set_error("unknown legend-position '%s'", optarg);
> +                return;
> +            }
> +            break;
> +        case 1006:
> +            if (strcmp(optarg, "topdown") == 0) {
> +                im->legenddirection = TOP_DOWN;
> +            } else if (strcmp(optarg, "bottomup") == 0) {
> +                im->legenddirection = BOTTOM_UP;
> +            } else {
> +                rrd_set_error("unknown legend-position '%s'", optarg);
> +                return;
> +            }
> +            break;
>         case 'F':
>             im->extra_flags |= FORCE_RULES_LEGEND;
>             break;
> ----------------------- END   ----------------------------------------
>
>
> _______________________________________________
> rrd-developers mailing list
> rrd-developers at lists.oetiker.ch
> https://lists.oetiker.ch/cgi-bin/listinfo/rrd-developers
>
>

-- 
Tobi Oetiker, OETIKER+PARTNER AG, Aarweg 15 CH-4600 Olten, Switzerland
http://it.oetiker.ch tobi at oetiker.ch ++41 62 775 9902 / sb: -9900



More information about the rrd-developers mailing list