Skip to content

Commit

Permalink
Add support for filters on group layers in WMS requests (fixes #5919)
Browse files Browse the repository at this point in the history
* 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 ba27152
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 40 deletions.
6 changes: 2 additions & 4 deletions mapobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;

Expand Down
7 changes: 5 additions & 2 deletions mapows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions mapows.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
72 changes: 39 additions & 33 deletions mapwms.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand All @@ -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 */
Expand All @@ -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)
Expand All @@ -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;
}

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -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()");
Expand All @@ -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()");
Expand Down Expand Up @@ -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 &&
Expand All @@ -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++;

}
Expand All @@ -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];

Expand Down Expand Up @@ -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"); */
}
}
Expand Down
Binary file modified msautotest/wxs/expected/wms_filter_getmap130.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -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>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions msautotest/wxs/expected/wms_filter_getmap130_wrong_group.txt
Original file line number Diff line number Diff line change
@@ -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>
16 changes: 15 additions & 1 deletion msautotest/wxs/wms_filter.map
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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"
Expand All @@ -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."
Expand Down

0 comments on commit ba27152

Please sign in to comment.