Permalink
Browse files

Implementation of RFC101 content dependant legend rendering (#4713)

adds cgi modes maplegend and maplegendicon
allow changing legend label by url
  • Loading branch information...
tbonfort committed Jul 26, 2013
1 parent 7a036be commit 471cdf41c6b13a0e5488dbfecc42ad8dbcee2f9c
Showing with 715 additions and 217 deletions.
  1. +1 −1 CMakeLists.txt
  2. +273 −0 hittest.c
  3. +69 −0 hittest.h
  4. +1 −1 legend.c
  5. +5 −1 mapfile.c
  6. +3 −1 maplayer.c
  7. +57 −35 maplegend.c
  8. +1 −0 mapows.h
  9. +2 −2 mapscript/php/mapscript_i.c
  10. +1 −1 mapscript/swiginc/class.i
  11. +1 −1 mapscript/swiginc/map.i
  12. +9 −4 mapserver.h
  13. +31 −8 mapservutil.c
  14. +31 −1 maptemplate.c
  15. +3 −1 maptemplate.h
  16. +226 −159 mapwms.c
  17. +1 −1 msautotest
View
@@ -203,7 +203,7 @@ maphttp.c mapparser.c mapstring.c mapxmp.c mapcairo.c mapimageio.c
mappluginlayer.c mapsymbol.c mapchart.c mapimagemap.c mappool.c maptclutf.c
mapcluster.c mapio.c mappostgis.c maptemplate.c mapcontext.c mapjoin.c
mappostgresql.c mapthread.c mapcopy.c maplabel.c mapprimitive.c maptile.c
mapcpl.c maplayer.c mapproject.c maptime.c mapcrypto.c maplegend.c
mapcpl.c maplayer.c mapproject.c maptime.c mapcrypto.c maplegend.c hittest.c
mapprojhack.c maptree.c mapdebug.c maplexer.c mapquantization.c mapunion.c
mapdraw.c maplibxml2.c mapquery.c maputil.c strptime.c mapdrawgdal.c
mapraster.c mapuvraster.c mapdummyrenderer.c mapobject.c maprasterquery.c
View
273 hittest.c
@@ -0,0 +1,273 @@
/*****************************************************************************
*
* Project: MapServer
* Purpose: Content Dependant Legend rendering support
* Author: Thomas Bonfort (tbonfort@terriscope.fr)
*
******************************************************************************
* Copyright (c) 1996-2013 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#include "mapserver.h"
void initStyleHitTests(styleObj *s, style_hittest *sh, int default_status) {
sh->status = default_status;
}
void initLabelHitTests(labelObj *l, label_hittest *lh, int default_status) {
int i;
lh->stylehits = msSmallCalloc(l->numstyles,sizeof(style_hittest));
lh->status = default_status;
for(i=0; i<l->numstyles; i++) {
initStyleHitTests(l->styles[i],&lh->stylehits[i],default_status);
}
}
void initClassHitTests(classObj *c, class_hittest *ch, int default_status) {
int i;
ch->stylehits = msSmallCalloc(c->numstyles,sizeof(style_hittest));
ch->labelhits = msSmallCalloc(c->numlabels,sizeof(label_hittest));
ch->status = default_status;
for(i=0; i<c->numstyles; i++) {
initStyleHitTests(c->styles[i],&ch->stylehits[i],default_status);
}
for(i=0; i<c->numlabels; i++) {
initLabelHitTests(c->labels[i],&ch->labelhits[i],default_status);
}
}
void initLayerHitTests(layerObj *l, layer_hittest *lh) {
int i,default_status;
lh->classhits = msSmallCalloc(l->numclasses,sizeof(class_hittest));
lh->status = default_status;
switch(l->type) {
case MS_LAYER_POLYGON:
case MS_LAYER_POINT:
case MS_LAYER_LINE:
case MS_LAYER_ANNOTATION:
default_status = 0; /* needs testing */
break;
default:
default_status = 1; /* no hittesting needed, use traditional mode */
break;
}
for(i=0; i<l->numclasses; i++) {
initClassHitTests(l->class[i],&lh->classhits[i], default_status);
}
}
void initMapHitTests(mapObj *map, map_hittest *mh) {
int i;
mh->layerhits = msSmallCalloc(map->numlayers,sizeof(layer_hittest));
for(i=0; i<map->numlayers; i++) {
initLayerHitTests(GET_LAYER(map,i),&mh->layerhits[i]);
}
}
void freeLabelHitTests(labelObj *l, label_hittest *lh) {
free(lh->stylehits);
}
void freeClassHitTests(classObj *c, class_hittest *ch) {
int i;
for(i=0; i<c->numlabels; i++) {
freeLabelHitTests(c->labels[i],&ch->labelhits[i]);
}
free(ch->stylehits);
free(ch->labelhits);
}
void freeLayerHitTests(layerObj *l, layer_hittest *lh) {
int i;
for(i=0; i<l->numclasses; i++) {
freeClassHitTests(l->class[i],&lh->classhits[i]);
}
free(lh->classhits);
}
void freeMapHitTests(mapObj *map, map_hittest *mh) {
int i;
for(i=0; i<map->numlayers; i++) {
freeLayerHitTests(GET_LAYER(map,i),&mh->layerhits[i]);
}
free(mh->layerhits);
}
int msHitTestShape(mapObj *map, layerObj *layer, shapeObj *shape, int drawmode, class_hittest *hittest) {
int i;
classObj *cp = layer->class[shape->classindex];
if(MS_DRAW_FEATURES(drawmode)) {
for(i=0;i<cp->numstyles;i++) {
styleObj *sp = cp->styles[i];
if(msScaleInBounds(map->scaledenom,sp->minscaledenom,sp->maxscaledenom)) {
hittest->stylehits[i].status = 1;
}
}
}
if(MS_DRAW_LABELS(drawmode)) {
for(i=0;i<cp->numlabels;i++) {
labelObj *l = cp->labels[i];
if(l->status == MS_ON) {
int s;
hittest->labelhits[i].status = 1;
for(s=0; s<l->numstyles;s++) {
hittest->labelhits[i].stylehits[s].status = 1;
}
}
}
}
return MS_SUCCESS;
}
int msHitTestLayer(mapObj *map, layerObj *layer, layer_hittest *hittest) {
int status;
if(!msLayerIsVisible(map,layer)) {
hittest->status = 0;
return MS_SUCCESS;
}
if(layer->type == MS_LAYER_LINE || layer->type == MS_LAYER_POLYGON || layer->type == MS_LAYER_POINT || layer->type == MS_LAYER_ANNOTATION) {
int maxfeatures=msLayerGetMaxFeaturesToDraw(layer, NULL);
int annotate = msEvalContext(map, layer, layer->labelrequires);
shapeObj shape;
int nclasses,featuresdrawn = 0;
int *classgroup;
rectObj searchrect;
int minfeaturesize=-1;
if(map->scaledenom > 0) {
if((layer->labelmaxscaledenom != -1) && (map->scaledenom >= layer->labelmaxscaledenom)) annotate = MS_FALSE;
if((layer->labelminscaledenom != -1) && (map->scaledenom < layer->labelminscaledenom)) annotate = MS_FALSE;
}
status = msLayerOpen(layer);
if(status != MS_SUCCESS) return MS_FAILURE;
/* build item list */
status = msLayerWhichItems(layer, MS_FALSE, NULL);
if(status != MS_SUCCESS) {
msLayerClose(layer);
return MS_FAILURE;
}
/* identify target shapes */
if(layer->transform == MS_TRUE) {
searchrect = map->extent;
#ifdef USE_PROJ
if((map->projection.numargs > 0) && (layer->projection.numargs > 0))
msProjectRect(&map->projection, &layer->projection, &searchrect); /* project the searchrect to source coords */
#endif
}
else {
searchrect.minx = searchrect.miny = 0;
searchrect.maxx = map->width-1;
searchrect.maxy = map->height-1;
}
status = msLayerWhichShapes(layer, searchrect, MS_FALSE);
if(status == MS_DONE) { /* no overlap */
msLayerClose(layer);
hittest->status = 0;
return MS_SUCCESS;
} else if(status != MS_SUCCESS) {
msLayerClose(layer);
return MS_FAILURE;
}
/* step through the target shapes */
msInitShape(&shape);
nclasses = 0;
classgroup = NULL;
if(layer->classgroup && layer->numclasses > 0)
classgroup = msAllocateValidClassGroups(layer, &nclasses);
if(layer->minfeaturesize > 0)
minfeaturesize = Pix2LayerGeoref(map, layer, layer->minfeaturesize);
while((status = msLayerNextShape(layer, &shape)) == MS_SUCCESS) {
int drawmode = MS_DRAWMODE_FEATURES;
if(shape.type == MS_SHAPE_POLYGON) {
msClipPolygonRect(&shape, map->extent);
} else {
msClipPolylineRect(&shape, map->extent);
}
if(shape.numlines == 0) {
msFreeShape(&shape);
continue;
}
/* Check if the shape size is ok to be drawn, we need to clip */
if((shape.type == MS_SHAPE_LINE || shape.type == MS_SHAPE_POLYGON) && (minfeaturesize > 0)) {
msTransformShape(&shape, map->extent, map->cellsize, NULL);
msComputeBounds(&shape);
if (msShapeCheckSize(&shape, minfeaturesize) == MS_FALSE) {
msFreeShape(&shape);
continue;
}
}
shape.classindex = msShapeGetClass(layer, map, &shape, classgroup, nclasses);
if((shape.classindex == -1) || (layer->class[shape.classindex]->status == MS_OFF)) {
msFreeShape(&shape);
continue;
}
hittest->classhits[shape.classindex].status = 1;
hittest->status = 1;
if(maxfeatures >=0 && featuresdrawn >= maxfeatures) {
status = MS_DONE;
break;
}
featuresdrawn++;
if(annotate && layer->class[shape.classindex]->numlabels > 0) {
msShapeGetAnnotation(layer, &shape);
drawmode |= MS_DRAWMODE_LABELS;
}
status = msHitTestShape(map, layer, &shape, drawmode, &hittest->classhits[shape.classindex]); /* all styles */
msFreeShape(&shape);
}
if (classgroup)
msFree(classgroup);
if(status != MS_DONE) {
msLayerClose(layer);
return MS_FAILURE;
}
msLayerClose(layer);
return MS_SUCCESS;
} else {
/* we don't hittest these layers, skip as they already have been initialised */
return MS_SUCCESS;
}
}
int msHitTestMap(mapObj *map, map_hittest *hittest) {
int i,status;
for(i=0; i<map->numlayers; i++) {
layerObj *lp = map->layers[i];
status = msHitTestLayer(map,lp,&hittest->layerhits[i]);
if(status != MS_SUCCESS) {
return MS_FAILURE;
}
}
return MS_SUCCESS;
}
View
@@ -0,0 +1,69 @@
/*****************************************************************************
*
* Project: MapServer
* Purpose: Content Dependant Legend rendering support
* Author: Thomas Bonfort (tbonfort@terriscope.fr)
*
******************************************************************************
* Copyright (c) 1996-2013 Regents of the University of Minnesota.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies of this Software or works derived from this Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
****************************************************************************/
#ifndef HITTEST_H
#define HITTEST_H
typedef struct {
int status;
} style_hittest;
typedef struct {
style_hittest *stylehits;
int status;
} label_hittest;
typedef struct {
style_hittest *stylehits;
label_hittest *labelhits;
int status;
} class_hittest;
typedef struct {
class_hittest *classhits;
int status;
} layer_hittest;
typedef struct map_hittest{
layer_hittest *layerhits;
} map_hittest;
int msHitTestMap(mapObj *map, map_hittest *hittest);
int msHitTestLayer(mapObj *map, layerObj *layer, layer_hittest *hittest);
void initStyleHitTests(styleObj *s, style_hittest *sh, int default_status);
void initLabelHitTests(labelObj *l, label_hittest *lh, int default_status);
void initClassHitTests(classObj *c, class_hittest *ch, int default_status);
void initLayerHitTests(layerObj *l, layer_hittest *lh);
void initMapHitTests(mapObj *map, map_hittest *mh);
void freeLabelHitTests(labelObj *l, label_hittest *lh);
void freeClassHitTests(classObj *c, class_hittest *ch);
void freeLayerHitTests(layerObj *l, layer_hittest *lh);
void freeMapHitTests(mapObj *map, map_hittest *mh);
#endif
View
@@ -52,7 +52,7 @@ int main(int argc, char *argv[])
exit(0);
}
img = msDrawLegend(map, MS_FALSE);
img = msDrawLegend(map, MS_FALSE, NULL);
if(!img) {
msWriteError(stderr);
exit(0);
View
@@ -6683,7 +6683,11 @@ int msUpdateMapFromURL(mapObj *map, char *variable, char *string)
break;
case(LEGEND):
return msUpdateLegendFromString(&(map->legend), string, MS_TRUE);
if(msyylex() == LABEL) {
return msUpdateLabelFromString(&map->legend.label, string, MS_TRUE);
} else {
return msUpdateLegendFromString(&(map->legend), string, MS_TRUE);
}
case(PROJECTION):
msLoadProjectionString(&(map->projection), string);
break;
View
@@ -978,7 +978,7 @@ int msLayerGetMaxFeaturesToDraw(layerObj *layer, outputFormatObj *format)
{
int nMaxFeatures = -1;
const char *pszTmp = NULL;
if (layer && format) {
if (layer) {
pszTmp = msLookupHashTable(&layer->metadata, "maxfeaturestodraw");
if (pszTmp)
nMaxFeatures = atoi(pszTmp);
@@ -987,6 +987,8 @@ int msLayerGetMaxFeaturesToDraw(layerObj *layer, outputFormatObj *format)
if (pszTmp)
nMaxFeatures = atoi(pszTmp);
}
}
if(format) {
if (nMaxFeatures < 0)
nMaxFeatures = atoi(msGetOutputFormatOption( format, "maxfeaturestodraw", "-1"));
}
Oops, something went wrong.

0 comments on commit 471cdf4

Please sign in to comment.