Legend size calculation, WMS LegendURL size

Bart van den Eijnden bartvde at GMAIL.COM
Thu Dec 13 03:01:26 EST 2007


Steve, Shawn,

I think this makes a lot of sense (conceptually, I haven't looked at the
code).

The current approach of Mapserver is far from perfect, and I've asked
previously on the wms-dev list if it is allowed to leave out width and
height in the LegendURL or use values of -1, but as you noticed WMS 1.3 has
this possibility where 1.1 hasn't.

But if the server knows the legend image size correctly, it will always be
the preferable way to include it in the GetCapabilities output.

So I'm +1.

Best regards,
Bart

On Dec 11, 2007 11:40 PM, Shawn Gervais <project10 at project10.net> wrote:

> Hi devs,
>
> I have attached a patch which:
>        - Refactors legend size calculation out of msDrawLegend and into
> its
> own function
>        - Uses the legend size calculating function to better estimate the
> height/width of LegendURLs advertised in GetCapabilities responses
>
>
> There seems (to me) to be some ambiguity in the WMS specs regarding
> LegendURL. WMS 1.3.0 clears this up a lot, and states (03-109r1, 7.2.4.6.5
> ):
>
> "Servers should provide the width and height attributes if known at the
> time of processing the GetCapabilities request."
>
> And WMS 1.3.0 makes those attributes optional. However, under WMS 1.1.1
> MapServer advertises a GetLegendGraphic-using OnlineResource for the
> LegendURL, but provides dimensions that are only sufficient for drawing
> the legend 'key', but not the label.
>
> Of course, the dimensions will (potentially) be incorrect if the client
> issues RULE or SCALE parameters, but they should be correct for the URL
> that is advertised.
>
> Any comments? Flames?
>
> -Shawn
>
> Index: maplegend.c
> ===================================================================
> --- maplegend.c (revision 7163)
> +++ maplegend.c (working copy)
> @@ -110,6 +110,104 @@
>  }
>
>  /*
> + * Calculates the optimal size for the legend
> + *
> + * Returns one of:
> + *   MS_SUCCESS
> + *   MS_FAILURE
> + */
> +int msLegendCalcSize(mapObj *map, int scale_independent, int *size_x,
> +                     int *size_y) {
> +    int i, j;
> +    int status, maxwidth=0, nLegendItems=0;
> +    char *transformedText; // Label text after applying wrapping,
> +                           // encoding if necessary
> +    layerObj *lp;
> +    rectObj rect;
> +
> +    /* Reset sizes */
> +    *size_x = 0;
> +    *size_y = 0;
> +
> +    /* Enable scale-dependent calculations */
> +    if (!scale_independent) {
> +        map->cellsize = msAdjustExtent(&(map->extent), map->width,
> map->height);
> +        status = msCalculateScale(map->extent, map->units, map->width,
> +                                  map->height, map->resolution,
> &map->scaledenom);
> +        if (status != MS_SUCCESS) return MS_FAILURE;
> +    }
> +
> +    /*
> +     * step through all map classes, and for each one that will be
> displayed
> +     * calculate the label size
> +     */
> +    for (i=0; i<map->numlayers; i++) {
> +        lp = (GET_LAYER(map, map->layerorder[i]));
> +
> +        if ((lp->status == MS_OFF) || (lp->type == MS_LAYER_QUERY)) /*
> skip it */
> +            continue;
> +
> +        if (!scale_independent && map->scaledenom > 0) {
> +            if ((lp->maxscaledenom > 0) && (map->scaledenom >
> lp->maxscaledenom))
> +                continue;
> +            if ((lp->minscaledenom > 0) && (map->scaledenom <=
> lp->minscaledenom))
> +                continue;
> +        }
> +
> +        for (j=lp->numclasses-1; j>=0; j--) {
> +            if (!lp->class[j]->name) continue; /* skip it */
> +
> +            /* Verify class scale */
> +            if (!scale_independent && map->scaledenom > 0) {
> +                if (   (lp->class[j]->maxscaledenom > 0)
> +                    && (map->scaledenom > lp->class[j]->maxscaledenom))
> +                    continue;
> +
> +                if (   (lp->class[j]->minscaledenom > 0)
> +                    && (map->scaledenom <= lp->class[j]->minscaledenom))
> +                    continue;
> +            }
> +
> +            /*
> +             * apply encoding and line wrapping to the legend label if
> requested
> +             * this is done conditionnally as the text transformation
> function
> +             * does some memory allocations that can be avoided in most
> cases.
> +             * the transformed text must be freed once finished, this
> must be done
> +             * conditionnally by testing if the transformed text pointer
> is the
> +             * same as the class name pointer
> +             */
> +            if (map->legend.label.encoding || map->legend.label.wrap)
> +                transformedText = msTransformLabelText(&map->legend.label
> ,
> +
> lp->class[j]->name);
> +            else
> +                transformedText = lp->class[j]->name;
> +
> +            if (   transformedText == NULL
> +                || msGetLabelSize(transformedText, &map->legend.label,
> +                                  &rect, &(map->fontset), 1.0, MS_FALSE)
> != 0)
> +            { /* something bad happened */
> +                if (transformedText != lp->class[j]->name)
> +                    free(transformedText);
> +
> +                return MS_FAILURE;
> +            }
> +
> +            maxwidth = MS_MAX(maxwidth, MS_NINT(rect.maxx - rect.minx));
> +            *size_y += MS_MAX(MS_NINT(rect.maxy - rect.miny), map->
> legend.keysizey);
> +            nLegendItems++;
> +        }
> +    }
> +
> +    /* Calculate the size of the legend: */
> +    /*   - account for the Y keyspacing */
> +    *size_y += (2*VMARGIN) + ((nLegendItems-1) * map->legend.keyspacingy
> );
> +    /*   - determine the legend width */
> +    *size_x = (2*HMARGIN) + maxwidth + map->legend.keyspacingx + map->
> legend.keysizex;
> +
> +    return MS_SUCCESS;
> +}
> +
> +/*
>  ** Creates a GD image of a legend for a specific map. msDrawLegend()
>  ** respects the current scale, and classes without a name are not
>  ** added to the legend.
> @@ -121,14 +219,11 @@
>  */
>  imageObj *msDrawLegend(mapObj *map, int scale_independent)
>  {
> -    int status;
> -
>     gdImagePtr img; /* image data structure */
>     int i,j; /* loop counters */
>     pointObj pnt;
>     int size_x, size_y=0;
>     layerObj *lp;
> -    int maxwidth=0,nLegendItems=0;
>     rectObj rect;
>     imageObj *image = NULL;
>     outputFormatObj *format = NULL;
> @@ -142,14 +237,12 @@
>     typedef struct legend_struct legendlabel;
>     legendlabel *head=NULL,*cur=NULL;
>
> -    if (!scale_independent) {
> -        map->cellsize = msAdjustExtent(&(map->extent), map->width,
> map->height);
> -        status = msCalculateScale(map->extent, map->units, map->width,
> map->height, map->resolution, &map->scaledenom);
> -        if(status != MS_SUCCESS) return(NULL);
> -    }
> +
>
>     if(msValidateContexts(map) != MS_SUCCESS) return NULL; /* make sure
> there are no recursive REQUIRES or LABELREQUIRES expressions */
>
> +    if(msLegendCalcSize(map, scale_independent, &size_x, &size_y) !=
> MS_SUCCESS) return NULL;
> +
>     /*
>      * step through all map classes, and for each one that will be
> displayed
>      * keep a reference to its label size and text
> @@ -201,19 +294,10 @@
>                 }
>                 return(NULL);
>             }
> -            maxwidth = MS_MAX(maxwidth, MS_NINT(rect.maxx - rect.minx));
>             cur->height = MS_MAX(MS_NINT(rect.maxy - rect.miny), map->
> legend.keysizey);
> -            size_y+=cur->height;
> -            nLegendItems++;
>         }
>     }
>
> -    /*
> -     ** Calculate the optimal image size for the legend
> -     */
> -    size_y += (2*VMARGIN) + ((nLegendItems-1)*map->legend.keyspacingy);
> /*initial vertical size*/
> -    size_x = (2*HMARGIN)+(maxwidth)+(map->legend.keyspacingx)+(map->
> legend.keysizex);
> -
>     /* ensure we have an image format representing the options for the
> legend. */
>     msApplyOutputFormat(&format, map->outputformat, map->
> legend.transparent, map->legend.interlace, MS_NOOVERRIDE);
>
> Index: mapserver.h
> ===================================================================
> --- mapserver.h (revision 7163)
> +++ mapserver.h (working copy)
> @@ -1648,6 +1648,7 @@
>  MS_DLL_EXPORT void freeImageCache(struct imageCacheObj *ic);
>
>  MS_DLL_EXPORT imageObj *msDrawLegend(mapObj *map, int scale_independent);
> /* in maplegend.c */
> +MS_DLL_EXPORT int msLegendCalcSize(mapObj *map, int scale_independent,
> int *size_x, int *size_y);
>  MS_DLL_EXPORT int msEmbedLegend(mapObj *map, imageObj *img);
>  MS_DLL_EXPORT int msDrawLegendIcon(mapObj* map, layerObj* lp, classObj*
> myClass, int width, int height, imageObj *img, int dstX, int dstY);
>  MS_DLL_EXPORT imageObj *msCreateLegendIcon(mapObj* map, layerObj* lp,
> classObj* myClass, int width, int height);
> Index: mapwms.c
> ===================================================================
> --- mapwms.c    (revision 7163)
> +++ mapwms.c    (working copy)
> @@ -1029,6 +1029,24 @@
>   }
>  }
>
> +/*
> + * msWMSGetLegendURLSize() - Estimates the size of a GetLegendGraphic
> result,
> + *                           for a specific layer
> + */
> +void msWMSGetLegendURLSize(mapObj *map, layerObj *lp, int *height, int
> *width) {
> +    int i;
> +
> +    /* Turn off all layers other than the requested layer, required */
> +    /* for msLegendCalcSize() */
> +    for (i=0; i<map->numlayers; i++) {
> +        if (GET_LAYER(map, i) == lp)
> +            GET_LAYER(map, i)->status = MS_ON;
> +        else
> +            GET_LAYER(map, i)->status = MS_OFF;
> +    }
> +
> +    msLegendCalcSize(map, 1, height, width); // Calculate
> scale-independent legend dimensions
> +}
>
>  /*
>  ** msDumpLayer()
> @@ -1286,16 +1304,11 @@
>                    }
>                    if (classnameset)
>                    {
> -                       if (map->legend.keysizex > 0)
> -                         sprintf(width, "%d", map->legend.keysizex);
> -                       else
> -                         sprintf(width, "%d", 20);/* default; */
> +                       int size_x, size_y;
> +                       msWMSGetLegendURLSize(map, lp, &size_x, &size_y);
> +                       sprintf(width,  "%d", size_x);
> +                       sprintf(height, "%d", size_y);
>
> -                       if (map->legend.keysizey > 0)
> -                          sprintf(height, "%d", map->legend.keysizey);
> -                       else
> -                         sprintf(height, "%d", 20);/* default; */
> -
>                        legendurl =
> (char*)malloc(strlen(script_url_encoded)+200);
>
>  #ifdef USE_GD_PNG
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.osgeo.org/pipermail/mapserver-dev/attachments/20071213/55d1f349/attachment.html


More information about the mapserver-dev mailing list