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...
1 parent 7a036be commit 471cdf41c6b13a0e5488dbfecc42ad8dbcee2f9c @tbonfort tbonfort committed Jul 26, 2013
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
@@ -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.