Skip to content
Permalink
Browse files
Add support for filters on group layers in WMS requests (fixes #5919)
* Add support for group layer with filter in WMS query

* init the ows_request object in msMapLoadOWSParameters()

* Added new msautotests and fixed one of the existing WMS FILTER tests
  • Loading branch information
charles-plante authored and dmorissette committed Dec 20, 2019
1 parent d945c8b commit ba27152e140f881e11fc1a97b60086ec4d7d1ee3
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 40 deletions.
@@ -759,8 +759,7 @@ int msMapLoadOWSParameters(mapObj *map, cgiRequestObj *request,
int result, i = 0;
owsRequestObj ows_request;

ows_request.numlayers = 0;
ows_request.enabled_layers = NULL;
msOWSInitRequestObj(&ows_request);


version = msOWSParseVersionString(wmtver);
@@ -778,8 +777,7 @@ int msMapLoadOWSParameters(mapObj *map, cgiRequestObj *request,
request->ParamValues, request->NumParams, wms_exception_format,
wms_request, &ows_request);

if (ows_request.numlayers > 0)
msFree(ows_request.enabled_layers);
msOWSClearRequestObj(&ows_request);

return result;

@@ -50,10 +50,12 @@
** msOWSInitRequestObj() initializes an owsRequestObj; i.e: sets
** all internal pointers to NULL.
*/
static void msOWSInitRequestObj(owsRequestObj *ows_request)
void msOWSInitRequestObj(owsRequestObj *ows_request)
{
ows_request->numlayers = 0;
ows_request->numwmslayerargs = 0;
ows_request->enabled_layers = NULL;
ows_request->layerwmsfilterindex = NULL;

ows_request->service = NULL;
ows_request->version = NULL;
@@ -65,9 +67,10 @@ static void msOWSInitRequestObj(owsRequestObj *ows_request)
** msOWSClearRequestObj() releases all resources associated with an
** owsRequestObj.
*/
static void msOWSClearRequestObj(owsRequestObj *ows_request)
void msOWSClearRequestObj(owsRequestObj *ows_request)
{
msFree(ows_request->enabled_layers);
msFree(ows_request->layerwmsfilterindex);
msFree(ows_request->service);
msFree(ows_request->version);
msFree(ows_request->request);
@@ -129,7 +129,9 @@ typedef struct {
/* owsRequestObj: Represent a OWS specific request with its enabled layers */
typedef struct {
int numlayers;
int numwmslayerargs;
int *enabled_layers;
int *layerwmsfilterindex;

char *service;
char *version;
@@ -147,6 +149,10 @@ MS_DLL_EXPORT const char * msOWSLookupMetadata2(hashTableObj *pri,
hashTableObj *sec,
const char *namespaces,
const char *name);

void msOWSInitRequestObj(owsRequestObj *ows_request);
void msOWSClearRequestObj(owsRequestObj *ows_request);

MS_DLL_EXPORT int msOWSRequestIsEnabled(mapObj *map, layerObj *layer,
const char *namespaces, const char *name, int check_all_layers);
MS_DLL_EXPORT void msOWSRequestLayersEnabled(mapObj *map, const char *namespaces,
@@ -258,7 +258,7 @@ int msWMSApplyTime(mapObj *map, int version, char *time, char *wms_exception_for
}
} else {
/*
** Check to see if there is a list of possible patterns defined. If it is the case, use
** Check to see if there is a list of possible patterns defined. If it is the case, use
** it to set the time pattern to use for the request.
**
** Last argument is set to TRUE (checkonly) to not trigger the patterns info setting, rather
@@ -310,7 +310,7 @@ int msWMSApplyTime(mapObj *map, int version, char *time, char *wms_exception_for
** Apply the FILTER parameter to layers (RFC118)
*/
int msWMSApplyFilter(mapObj *map, int version, const char *filter,
int def_srs_needs_axis_swap, char *wms_exception_format)
int def_srs_needs_axis_swap, char *wms_exception_format, owsRequestObj *ows_request)
{
int i=0, numlayers;
int numfilters=0, curfilter=0;
@@ -324,20 +324,20 @@ int msWMSApplyFilter(mapObj *map, int version, const char *filter,
if (!map)
return MS_FAILURE;

/* Count number of requested layers
/* Count number of requested layers / groups / etc.
* Only layers with STATUS ON were in the LAYERS request param.
* Layers with STATUS DEFAULT were set in the mapfile and are
* not expected to have a corresponding filter in the request
*/
for(i=0, numlayers=0; i<map->numlayers; i++) {
layerObj *lp=NULL;
for(i=0, numlayers=0; i<map->numlayers; i++) {
layerObj *lp=NULL;

if(map->layerorder[i] != -1) {
lp = (GET_LAYER(map, map->layerorder[i]));
if (lp->status == MS_ON)
numlayers++;
}
}
if(map->layerorder[i] != -1) {
lp = (GET_LAYER(map, map->layerorder[i]));
if (lp->status == MS_ON)
numlayers++;
}
}

/* -------------------------------------------------------------------- */
/* Parse the Filter parameter. If there are several Filter */
@@ -347,25 +347,21 @@ int msWMSApplyFilter(mapObj *map, int version, const char *filter,
if (filter[0] == '(') {
paszFilters = FLTSplitFilters(filter, &numfilters);

if ( paszFilters && numfilters > 0 && numlayers != numfilters ) {
msFreeCharArray(paszFilters, numfilters);
paszFilters = NULL;
}
} else if (numlayers == 1) {
numfilters=1;
paszFilters = (char **)msSmallMalloc(sizeof(char *)*numfilters);
paszFilters[0] = msStrdup(filter);
}

if (numlayers != numfilters) {
msSetError(MS_WMSERR, "Wrong number of filter elements, one filter must be specified for each requested layer.",
if (numfilters != ows_request->numwmslayerargs) {
msSetError(MS_WMSERR, "Wrong number of filter elements, one filter must be specified for each requested layer or groups.",
"msWMSApplyFilter" );
msFreeCharArray(paszFilters, numfilters);
return msWMSException(map, version, "InvalidParameterValue", wms_exception_format);
}

/* We're good to go. Apply each filter to the corresponding layer */
for(i=0, curfilter=0; i<map->numlayers && curfilter<numfilters; i++) {
for(i=0; i<map->numlayers; i++) {
layerObj *lp=NULL;

if(map->layerorder[i] != -1)
@@ -375,9 +371,10 @@ int msWMSApplyFilter(mapObj *map, int version, const char *filter,
if (lp == NULL || lp->status != MS_ON)
continue;

curfilter = ows_request->layerwmsfilterindex[lp->index];

/* Skip empty filters */
if (paszFilters[curfilter][0] == '\0') {
curfilter++;
continue;
}

@@ -959,9 +956,9 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
outputFormatObj *format = NULL;
int validlayers = 0;
char *styles = NULL;
int numlayers = 0;
int numwmslayerargs = 0;
char **layers = NULL;
int layerfound =0;
int layerfound = MS_FALSE;
int invalidlayers = 0;
char epsgbuf[100];
char srsbuffer[100];
@@ -1033,11 +1030,11 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
layerOrder = (int*)malloc(map->numlayers * sizeof(int));
MS_CHECK_ALLOC(layerOrder, map->numlayers * sizeof(int), MS_FAILURE)

layers = msStringSplit(values[i], ',', &numlayers);
if (layers==NULL || strlen(values[i]) <=0 || numlayers < 1) {
numlayers = 0;
layers = msStringSplit(values[i], ',', &numwmslayerargs);
if (layers==NULL || strlen(values[i]) <=0 || numwmslayerargs < 1) {
numwmslayerargs = 0;
if (sld_url == NULL && sld_body == NULL) {
msFreeCharArray(layers,numlayers);
msFreeCharArray(layers,numwmslayerargs);
msFree(layerOrder);
msSetError(MS_WMSERR, "At least one layer name required in LAYERS.",
"msWMSLoadGetMapParams()");
@@ -1048,8 +1045,8 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
if (nVersion >= OWS_1_3_0) {
layerlimit = msOWSLookupMetadata(&(map->web.metadata), "MO", "layerlimit");
if(layerlimit) {
if (numlayers > atoi(layerlimit)) {
msFreeCharArray(layers,numlayers);
if (numwmslayerargs > atoi(layerlimit)) {
msFreeCharArray(layers,numwmslayerargs);
msFree(layerOrder);
msSetError(MS_WMSERR, "Number of layers requested exceeds LayerLimit.",
"msWMSLoadGetMapParams()");
@@ -1079,8 +1076,16 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
isUsedInNestedGroup = (int*)msSmallCalloc(map->numlayers, sizeof(int));
msWMSPrepareNestedGroups(map, nVersion, nestedGroups, numNestedGroups, isUsedInNestedGroup);

for (k=0; k<numlayers; k++) {
layerfound = 0;
if (ows_request->layerwmsfilterindex != NULL)
msFree(ows_request->layerwmsfilterindex);
ows_request->layerwmsfilterindex = (int*)msSmallMalloc(map->numlayers * sizeof(int));
for(j=0; j<map->numlayers; j++) {
ows_request->layerwmsfilterindex[j] = -1;
}
ows_request->numwmslayerargs = numwmslayerargs;

for (k=0; k<numwmslayerargs; k++) {
layerfound = MS_FALSE;
for (j=0; j<map->numlayers; j++) {
/* Turn on selected layers only. */
if ( ((GET_LAYER(map, j)->name &&
@@ -1096,11 +1101,12 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
GET_LAYER(map, j)->status = MS_ON;
}
}
ows_request->layerwmsfilterindex[j] = k; /* Assign the corresponding filter */
validlayers++;
layerfound = 1;
layerfound = MS_TRUE;
}
}
if (layerfound == 0 && numlayers>0)
if (layerfound == MS_FALSE && numwmslayerargs>0)
invalidlayers++;

}
@@ -1123,7 +1129,7 @@ int msWMSLoadGetMapParams(mapObj *map, int nVersion,
}

free(layerOrder);
msFreeCharArray(layers, numlayers);
msFreeCharArray(layers, numwmslayerargs);
} else if (strcasecmp(names[i], "STYLES") == 0) {
styles = values[i];

@@ -1887,7 +1893,7 @@ this request. Check wms/ows_enable_request settings.",
return msWMSException(map, nVersion, NULL, wms_exception_format);
}

if (msWMSApplyFilter(map, nVersion, filter, need_axis_swap, wms_exception_format) == MS_FAILURE) {
if (msWMSApplyFilter(map, nVersion, filter, need_axis_swap, wms_exception_format, ows_request) == MS_FAILURE) {
return MS_FAILURE;/* msWMSException(map, nVersion, "InvalidFilterRequest"); */
}
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ogc http://ogc.dmsolutions.ca/wms/1.3.0/exceptions_1_3_0.xsd">
<ServiceException code="InvalidParameterValue">
msWMSApplyFilter: WMS server error. Wrong number of filter elements, one filter must be specified for each requested layer or groups.
</ServiceException>
</ServiceExceptionReport>
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -0,0 +1,6 @@
<?xml version='1.0' encoding="UTF-8" standalone="no" ?>
<ServiceExceptionReport version="1.3.0" xmlns="http://www.opengis.net/ogc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/ogc http://ogc.dmsolutions.ca/wms/1.3.0/exceptions_1_3_0.xsd">
<ServiceException code="LayerNotDefined">
msWMSLoadGetMapParams(): WMS server error. Invalid layer(s) given in the LAYERS parameter. A layer might be disabled for this request. Check wms/ows_enable_request settings.
</ServiceException>
</ServiceExceptionReport>
@@ -22,6 +22,18 @@
# GetMap 1.3.0 two layers, one without and one with filter
# RUN_PARMS: wms_filter_getmap130_emptyfilter.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG%3A4326&BBOX=40,-70,50,-60&WIDTH=400&HEIGHT=400&LAYERS=road,popplace&STYLES=&FORMAT=image%2Fpng&BGCOLOR=0xFFFFFF&TRANSPARENT=FALSE&EXCEPTIONS=INIMAGE&FILTER=()(<Filter><PropertyIsEqualTo><PropertyName>NAME</PropertyName><Literal>Charlottetown</Literal></PropertyIsEqualTo></Filter>)" > [RESULT_DEMIME]
#
# GetMap 1.3.0 one group layer
# RUN_PARMS: wms_filter_getmap130_one_group.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG%3A4326&BBOX=40,-70,50,-60&WIDTH=400&HEIGHT=400&LAYERS=feature_group&STYLES=&FORMAT=image%2Fpng&BGCOLOR=0xFFFFFF&TRANSPARENT=FALSE&EXCEPTIONS=INIMAGE&FILTER=(<Filter><PropertyIsEqualTo><PropertyName>REG_CODE</PropertyName><Literal>12</Literal></PropertyIsEqualTo></Filter>)" > [RESULT_DEMIME]
#
# GetMap 1.3.0 one group layer and one layer
# RUN_PARMS: wms_filter_getmap130_group_and_layer.png [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG%3A4326&BBOX=40,-70,50,-60&WIDTH=400&HEIGHT=400&LAYERS=feature_group,road&STYLES=&FORMAT=image%2Fpng&BGCOLOR=0xFFFFFF&TRANSPARENT=FALSE&EXCEPTIONS=INIMAGE&FILTER=(<Filter><PropertyIsEqualTo><PropertyName>REG_CODE</PropertyName><Literal>12</Literal></PropertyIsEqualTo></Filter>)(<Filter><DWithin><PropertyName>Geometry</PropertyName><gml:Point><gml:coordinates>46,-63</gml:coordinates></gml:Point><Distance units='dd'>0.5</Distance></DWithin></Filter>)" > [RESULT_DEMIME]
#
# GetMap 1.3.0 one group one layer missing a filter
# RUN_PARMS: wms_filter_getmap130_group_missingFilter.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG%3A4326&BBOX=40,-70,50,-60&WIDTH=400&HEIGHT=400&LAYERS=feature_group,road&STYLES=&FORMAT=image%2Fpng&BGCOLOR=0xFFFFFF&TRANSPARENT=FALSE&EXCEPTIONS=XML&FILTER=(<Filter><PropertyIsEqualTo><PropertyName>REG_CODE</PropertyName><Literal>12</Literal></PropertyIsEqualTo></Filter>)" > [RESULT_DEMIME]
#
# GetMap 1.3.0 filter with invalid group name
# RUN_PARMS: wms_filter_getmap130_wrong_group.txt [MAPSERV] QUERY_STRING="map=[MAPFILE]&SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&CRS=EPSG%3A4326&BBOX=40,-70,50,-60&WIDTH=400&HEIGHT=400&LAYERS=inval_group&STYLES=&FORMAT=image%2Fpng&BGCOLOR=0xFFFFFF&TRANSPARENT=FALSE&EXCEPTIONS=XML&FILTER=(<Filter><PropertyIsEqualTo><PropertyName>REG_CODE</PropertyName><Literal>12</Literal></PropertyIsEqualTo></Filter>)" > [RESULT_DEMIME]
#

MAP

@@ -71,12 +83,13 @@ END
LAYER
NAME province
DATA province
GROUP feature_group
METADATA
"wms_title" "province"
"wms_description" "province"
"wms_result_fields" "NAME_E YEAR_EST AREA_KMSQ"
END
TYPE POINT
TYPE POLYGON
STATUS ON
PROJECTION
"init=epsg:3978"
@@ -96,6 +109,7 @@ END # Layer
LAYER
NAME popplace
DATA popplace
GROUP feature_group
METADATA
"wms_title" "popplace"
"wms_description" "Cities of I.P.E."

0 comments on commit ba27152

Please sign in to comment.