Skip to content
Permalink
Browse files
Implement SLD "painter's model"
  • Loading branch information
jbo-ads committed Feb 21, 2020
1 parent 2b80a45 commit 85fcbdd0929a4178803f2e13dc458a0b23fae779
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 10 deletions.
@@ -541,6 +541,7 @@ int msCopyClass(classObj *dst, classObj *src, layerObj *layer)
}

MS_COPYSTELEM(status);
MS_COPYSTELEM(isfallback);

/* free any previous styles on the dst layer */
for(i=0; i<dst->numstyles; i++) { /* each style */
@@ -1033,6 +1034,7 @@ int msCopyLayer(layerObj *dst, layerObj *src)
MS_COPYSTRING(dst->data, src->data);
MS_COPYSTRING(dst->encoding, src->encoding);

MS_COPYSTELEM(rendermode);
MS_COPYSTELEM(status);
MS_COPYSTELEM(type);
MS_COPYSTELEM(tolerance);
@@ -924,11 +924,11 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
int drawmode=MS_DRAWMODE_FEATURES;
char annotate=MS_TRUE;
shapeObj shape;
shapeObj savedShape;
rectObj searchrect;
char cache=MS_FALSE;
int maxnumstyles=1;
featureListNodeObjPtr shpcache=NULL, current=NULL;
int classindex = -1;
int nclasses = 0;
int *classgroup = NULL;
double minfeaturesize = -1;
@@ -1016,8 +1016,10 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)

/* step through the target shapes and their classes */
msInitShape(&shape);
classindex = -1;
int classindex = -1;
int classcount = 0;
for (;;) {
int rendermode;
if (classindex == -1) {
status = msLayerNextShape(layer, &shape);
if (status != MS_SUCCESS) {
@@ -1030,15 +1032,29 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
msDebug("msDrawVectorLayer(): Skipping shape (%ld) because LAYER::MINFEATURESIZE is bigger than shape size\n", shape.index);
continue;
}
classcount = 0;
}

classindex = msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses);
if((classindex == -1) || (layer->class[classindex]->status == MS_OFF)) {
continue;
}
shape.classindex = classindex;
classindex = -1; // This value indicates that no more class is to be
// fetched from current shape

// When only one class is applicable, rendering mode is forced to its default,
// i.e. only the first applicable class is actually applied. As a consequence,
// cache can be enabled when relevant.
classcount++;
rendermode = layer->rendermode;
if ((classcount == 1) && (msShapeGetNextClass(classindex, layer, map, &shape, classgroup, nclasses) == -1))
{
rendermode = MS_FIRST_MATCHING_CLASS;
}

if (rendermode == MS_FIRST_MATCHING_CLASS)
{
classindex = -1;
}

if(maxfeatures >=0 && featuresdrawn >= maxfeatures) {
status = MS_DONE;
@@ -1077,6 +1093,13 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
cache = MS_FALSE;
}

if (rendermode == MS_ALL_MATCHING_CLASSES)
{
// Cache is designed to handle only one class. Therefore it is
// disabled when using SLD "painters model" rendering mode.
cache = MS_FALSE;
}

/* RFC77 TODO: check return value, may need a more sophisticated if-then test. */
if(annotate && layer->class[shape.classindex]->numlabels > 0) {
drawmode |= MS_DRAWMODE_LABELS;
@@ -1089,6 +1112,16 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)
drawmode |= MS_DRAWMODE_UNCLIPPEDLINES;
}

if (rendermode == MS_ALL_MATCHING_CLASSES)
{
// In SLD "painters model" rendering mode, all applicable classes are actually applied.
// Coordinates stored in the shape must keep their original values for
// the shape to be drawn multiple times.
// Here the original shape is saved.
msInitShape(&savedShape);
msCopyShape(&shape, &savedShape);
}

if (cache) {
styleObj *pStyle = layer->class[shape.classindex]->styles[0];
if (pStyle->outlinewidth > 0) {
@@ -1115,6 +1148,17 @@ int msDrawVectorLayer(mapObj *map, layerObj *layer, imageObj *image)

else
status = msDrawShape(map, layer, &shape, image, -1, drawmode); /* all styles */

if (rendermode == MS_ALL_MATCHING_CLASSES)
{
// In SLD "painters model" rendering mode, all applicable classes are actually applied.
// Coordinates stored in the shape must keep their original values for
// the shape to be drawn multiple times.
// Here the original shape is restored.
msCopyShape(&savedShape, &shape);
msFreeShape(&savedShape);
}

if(status != MS_SUCCESS) {
retcode = MS_FAILURE;
break;
@@ -2829,6 +2829,7 @@ int initClass(classObj *class)
class->status = MS_ON;
class->debug = MS_OFF;
MS_REFCNT_INIT(class);
class->isfallback = FALSE;

msInitExpression(&(class->expression));
class->name = NULL;
@@ -3507,6 +3508,7 @@ int initLayer(layerObj *layer, mapObj *map)
layer->group = NULL;
layer->status = MS_OFF;
layer->data = NULL;
layer->rendermode = MS_FIRST_MATCHING_CLASS;

layer->map = map; /* point back to the encompassing structure */

@@ -235,6 +235,7 @@ int msSLDApplySLD(mapObj *map, const char *psSLDXML, int iLayer, const char *psz
int bSLDHasNamedClass = MS_FALSE;

lp->type = sldLayer->type;
lp->rendermode = MS_ALL_MATCHING_CLASSES;

for (k=0; k < sldLayer->numclasses; k++) {
if( sldLayer->class[k]->group ) {
@@ -826,6 +827,7 @@ static void msSLDParseUserStyle(CPLXMLNode* psUserStyle, layerObj *psLayer)
if (psElseFilter) {
msSLDParseRule(psRule, psLayer, pszUserStyleName);
_SLDApplyRuleValues(psRule, psLayer, 1);
psLayer->class[psLayer->numclasses-1]->isfallback = TRUE;
}
}
}
@@ -489,6 +489,7 @@ extern "C" {
enum MS_SHAPE_TYPE {MS_SHAPE_POINT, MS_SHAPE_LINE, MS_SHAPE_POLYGON, MS_SHAPE_NULL};
enum MS_LAYER_TYPE {MS_LAYER_POINT, MS_LAYER_LINE, MS_LAYER_POLYGON, MS_LAYER_RASTER, MS_LAYER_ANNOTATION /* only used for parser backwards compatibility */, MS_LAYER_QUERY, MS_LAYER_CIRCLE, MS_LAYER_TILEINDEX, MS_LAYER_CHART};
enum MS_FONT_TYPE {MS_TRUETYPE, MS_BITMAP};
enum MS_RENDER_MODE {MS_FIRST_MATCHING_CLASS, MS_ALL_MATCHING_CLASSES};

#define MS_POSITIONS_LENGTH 14
enum MS_POSITIONS_ENUM {MS_UL=101, MS_LR, MS_UR, MS_LL, MS_CR, MS_CL, MS_UC, MS_LC, MS_CC, MS_AUTO, MS_XY, MS_NONE, MS_AUTO2,MS_FOLLOW};
@@ -1182,6 +1183,8 @@ typedef struct labelObj labelObj;
#endif

int status;
int isfallback; // TRUE if this class should be applied if and only if
// no other class is applicable (e.g. SLD <ElseFilter/>)

#ifndef SWIG
styleObj **styles;
@@ -1647,6 +1650,9 @@ typedef struct labelObj labelObj;
char *group; /* shouldn't be unique it's supposed to be a group right? */

int status; /* on or off */
enum MS_RENDER_MODE rendermode;
// MS_FIRST_MATCHING_CLASS: Default and historic MapServer behavior
// MS_ALL_MATCHING_CLASSES: SLD behavior

#ifndef SWIG
/* RFC86 Scale-dependent token replacements */
@@ -698,7 +698,18 @@ int msShapeGetNextClass(int currentclass, layerObj *layer, mapObj *map,
}

if(layer->class[iclass]->status != MS_DELETE && msEvalExpression(layer, shape, &(layer->class[iclass]->expression), layer->classitemindex) == MS_TRUE)
return(iclass);
{
if (layer->class[iclass]->isfallback && currentclass != -1)
{
// Class is not applicable if it is flagged as fallback (<ElseFilter/> tag in SLD)
// but other classes have been applied before.
return -1;
}
else
{
return(iclass);
}
}
}
}

Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -3,7 +3,7 @@
#
# RUN_PARMS: map_painter_reference.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=800&HEIGHT=800&LAYERS=multipolygon" > [RESULT_DEMIME]
#
# RUN_PARMS: sld_painter_reference.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=800&HEIGHT=800&LAYERS=multipolygon&SLD_BODY=<StyledLayerDescriptor version=%221.1.0%22><NamedLayer><se:Name>multipolygon</se:Name><UserStyle><se:FeatureTypeStyle><se:Rule><se:Name>all_green</se:Name><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#00ff00</se:SvgParameter></se:Fill><se:Stroke><se:SvgParameter name=%22stroke%22>#ffff00</se:SvgParameter><se:SvgParameter name=%22stroke-width%22>5.00</se:SvgParameter></se:Stroke></se:PolygonSymbolizer></se:Rule><se:Rule><se:Name>red_3_5</se:Name><ogc:Filter><ogc:And><ogc:PropertyIsLessThanOrEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>5</ogc:Literal></ogc:PropertyIsLessThanOrEqualTo><ogc:PropertyIsGreaterThanOrEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>3</ogc:Literal></ogc:PropertyIsGreaterThanOrEqualTo></ogc:And></ogc:Filter><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#ff0000</se:SvgParameter></se:Fill></se:PolygonSymbolizer></se:Rule><se:Rule><se:Name>blue_quadrilateral</se:Name><ogc:Filter><ogc:PropertyIsEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>4</ogc:Literal></ogc:PropertyIsEqualTo></ogc:Filter><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#0000ff</se:SvgParameter></se:Fill></se:PolygonSymbolizer></se:Rule></se:FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
# RUN_PARMS: sld_painter_reference.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=800&HEIGHT=800&LAYERS=multipolygon&SLD_BODY=<StyledLayerDescriptor version=%221.1.0%22><NamedLayer><se:Name>multipolygon</se:Name><UserStyle><se:FeatureTypeStyle><se:Rule><se:Name>all_green</se:Name><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#00ff00</se:SvgParameter></se:Fill><se:Stroke><se:SvgParameter name=%22stroke%22>#ffff00</se:SvgParameter><se:SvgParameter name=%22stroke-width%22>5.00</se:SvgParameter></se:Stroke></se:PolygonSymbolizer></se:Rule><se:Rule><se:Name>red_3_5</se:Name><ogc:Filter><ogc:And><ogc:PropertyIsLessThanOrEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>5</ogc:Literal></ogc:PropertyIsLessThanOrEqualTo><ogc:PropertyIsGreaterThanOrEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>3</ogc:Literal></ogc:PropertyIsGreaterThanOrEqualTo></ogc:And></ogc:Filter><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#ff0000</se:SvgParameter></se:Fill><se:Stroke><se:SvgParameter name=%22stroke%22>#ffff00</se:SvgParameter><se:SvgParameter name=%22stroke-width%22>5.00</se:SvgParameter></se:Stroke></se:PolygonSymbolizer></se:Rule><se:Rule><se:Name>blue_quadrilateral</se:Name><ogc:Filter><ogc:PropertyIsEqualTo><ogc:PropertyName>N_EDGES</ogc:PropertyName><ogc:Literal>4</ogc:Literal></ogc:PropertyIsEqualTo></ogc:Filter><se:PolygonSymbolizer><se:Fill><se:SvgParameter name=%22fill%22>#0000ff</se:SvgParameter></se:Fill><se:Stroke><se:SvgParameter name=%22stroke%22>#ffff00</se:SvgParameter><se:SvgParameter name=%22stroke-width%22>5.00</se:SvgParameter></se:Stroke></se:PolygonSymbolizer></se:Rule></se:FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
#


@@ -50,13 +50,17 @@ MAP
NAME red_3_5
EXPRESSION ([N_EDGES] >= 3 and [N_EDGES] <= 5)
STYLE
WIDTH 5
OUTLINECOLOR 255 255 0
COLOR 255 0 0
END
END
CLASS
NAME blue_quadrilateral
EXPRESSION ([N_EDGES] == 4)
STYLE
WIDTH 5
OUTLINECOLOR 255 255 0
COLOR 0 0 255
END
END
@@ -1,14 +1,14 @@
#
# REQUIRES: INPUT=GDAL OUTPUT=PNG SUPPORTS=WMS

# No IsDefault in SLD, and no STYLES= parameter ==> will use the first UserStyle, that is red
# RUN_PARMS: sld_named_userstyle_1.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
# No IsDefault in SLD, and no STYLES= parameter ==> will use all applicables UserStyle, that is blue on top of green on top of red
# RUN_PARMS: sld_named_userstyle_1.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>blue</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>1</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#0000FF</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
#
# Selection of class with STYLES=green
# RUN_PARMS: sld_named_userstyle_2.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&STYLES=green&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
# RUN_PARMS: sld_named_userstyle_2.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&STYLES=green&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>blue</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>1</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#0000FF</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
#
# Selection of class with IsDefault set on the 'green' UserStyle
# RUN_PARMS: sld_named_userstyle_3.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><IsDefault>true</IsDefault><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
# RUN_PARMS: sld_named_userstyle_3.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3&REQUEST=GetMap&CRS=EPSG:4326&BBOX=42,-5,52,9&FORMAT=image/png&WIDTH=200&HEIGHT=200&LAYERS=lline&SLD_BODY=<StyledLayerDescriptor version='1.1.0'><NamedLayer><Name>lline</Name><UserStyle><Name>red</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>5</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#FF0000</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>green</Name><IsDefault>true</IsDefault><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>3</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#00FF00</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle><UserStyle><Name>blue</Name><FeatureTypeStyle><Rule><LineSymbolizer><Stroke><SvgParameter name='stroke-width'>1</SvgParameter><SvgParameter name='stroke-opacity'>0.5</SvgParameter><SvgParameter name='stroke'>#0000FF</SvgParameter></Stroke></LineSymbolizer></Rule></FeatureTypeStyle></UserStyle></NamedLayer></StyledLayerDescriptor>" > [RESULT_DEMIME]
#

MAP

0 comments on commit 85fcbdd

Please sign in to comment.