diff --git a/mapquery.c b/mapquery.c index 8cbc8966db..745b386233 100644 --- a/mapquery.c +++ b/mapquery.c @@ -55,6 +55,7 @@ int msInitQuery(queryObj *query) query->op = -1; query->item = query->str = NULL; + query->filter = NULL; return MS_SUCCESS; } @@ -68,6 +69,10 @@ void msFreeQuery(queryObj *query) if(query->item) free(query->item); if(query->str) free(query->str); + if(query->filter) { + freeExpression(query->filter); + free(query->filter); + } } /* @@ -93,6 +98,9 @@ int msExecuteQuery(mapObj *map) case MS_QUERY_BY_ATTRIBUTE: status = msQueryByAttributes(map); break; + case MS_QUERY_BY_FILTER: + status = msQueryByFilter(map); + break; case MS_QUERY_BY_OPERATOR: status = msQueryByOperator(map); break; @@ -685,6 +693,169 @@ int msQueryByAttributes(mapObj *map) return(MS_FAILURE); } +/* +** Query using common expression syntax. +*/ +int msQueryByFilter(mapObj *map) +{ + int l; + int start, stop=0; + + layerObj *lp; + + char status; + + expressionObj old_filter; + + rectObj search_rect; + + shapeObj shape; + + int nclasses = 0; + int *classgroup = NULL; + + if(map->query.type != MS_QUERY_BY_FILTER) { + msSetError(MS_QUERYERR, "The query is not properly defined.", "msQueryByFilter()"); + return(MS_FAILURE); + } + if(!map->query.filter) { // TODO: check filter type too + msSetError(MS_QUERYERR, "Filter is not set.", "msQueryByFilter()"); + return(MS_FAILURE); + } + + msInitShape(&shape); + + if(map->query.layer < 0 || map->query.layer >= map->numlayers) + start = map->numlayers-1; + else + start = stop = map->query.layer; + + for(l=start; l>=stop; l--) { + lp = (GET_LAYER(map, l)); + + /* conditions may have changed since this layer last drawn, so set + layer->project true to recheck projection needs (Bug #673) */ + lp->project = MS_TRUE; + + /* free any previous search results, do it now in case one of the next few tests fail */ + if(lp->resultcache) { + if(lp->resultcache->results) free(lp->resultcache->results); + free(lp->resultcache); + lp->resultcache = NULL; + } + + if(!msIsLayerQueryable(lp)) continue; + if(lp->status == MS_OFF) continue; + if(lp->type == MS_LAYER_RASTER) continue; /* ok to skip? */ + + if(map->scaledenom > 0) { + if((lp->maxscaledenom > 0) && (map->scaledenom > lp->maxscaledenom)) continue; + if((lp->minscaledenom > 0) && (map->scaledenom <= lp->minscaledenom)) continue; + } + + if (lp->maxscaledenom <= 0 && lp->minscaledenom <= 0) { + if((lp->maxgeowidth > 0) && ((map->extent.maxx - map->extent.minx) > lp->maxgeowidth)) continue; + if((lp->mingeowidth > 0) && ((map->extent.maxx - map->extent.minx) < lp->mingeowidth)) continue; + } + + initExpression(&old_filter); + msCopyExpression(&old_filter, &lp->filter); /* save existing filter */ + if(msLayerSupportsCommonFilters(lp)) { + msCopyExpression(&lp->filter, map->query.filter); /* apply new filter */ + } + + status = msLayerOpen(lp); + if(status != MS_SUCCESS) goto query_error; + + /* build item list, we want *all* items */ + status = msLayerWhichItems(lp, MS_TRUE, NULL); + if(status != MS_SUCCESS) goto query_error; + + if(!msLayerSupportsCommonFilters(lp)) { + freeExpression(&lp->filter); /* clear existing filter */ + status = msTokenizeExpression(map->query.filter, lp->items, &(lp->numitems)); + if(status != MS_SUCCESS) goto query_error; + } + + search_rect = map->query.rect; +#ifdef USE_PROJ + if(lp->project && msProjectionsDiffer(&(lp->projection), &(map->projection))) + msProjectRect(&(map->projection), &(lp->projection), &search_rect); /* project the searchrect to source coords */ + else + lp->project = MS_FALSE; +#endif + + status = msLayerWhichShapes(lp, search_rect); + if(status == MS_DONE) { /* no overlap */ + msLayerClose(lp); + continue; + } else if(status != MS_SUCCESS) goto query_error; + + lp->resultcache = (resultCacheObj *)malloc(sizeof(resultCacheObj)); /* allocate and initialize the result cache */ + initResultCache( lp->resultcache); + + nclasses = 0; + classgroup = NULL; + if (lp->classgroup && lp->numclasses > 0) + classgroup = msAllocateValidClassGroups(lp, &nclasses); + + while((status = msLayerNextShape(lp, &shape)) == MS_SUCCESS) { /* step through the shapes */ + + if(!msLayerSupportsCommonFilters(lp)) { /* we have to apply the filter here instead of within the driver */ + if(msEvalExpression(lp, &shape, map->query.filter, -1) != MS_TRUE) { /* next shape */ + msFreeShape(&shape); + continue; + } + } + + shape.classindex = msShapeGetClass(lp, &shape, map->scaledenom, classgroup, nclasses); + if(!(lp->template) && ((shape.classindex == -1) || (lp->class[shape.classindex]->status == MS_OFF))) { /* not a valid shape */ + msFreeShape(&shape); + continue; + } + + if(!(lp->template) && !(lp->class[shape.classindex]->template)) { /* no valid template */ + msFreeShape(&shape); + continue; + } + +#ifdef USE_PROJ + if(lp->project && msProjectionsDiffer(&(lp->projection), &(map->projection))) + msProjectShape(&(lp->projection), &(map->projection), &shape); + else + lp->project = MS_FALSE; +#endif + + addResult(lp->resultcache, &shape); + msFreeShape(&shape); + } /* next shape */ + + if(classgroup) msFree(classgroup); + + msCopyExpression(&lp->filter, &old_filter); /* restore old filter */ + freeExpression(&old_filter); + + if(status != MS_DONE) goto query_error; + if(lp->resultcache->numresults == 0) msLayerClose(lp); /* no need to keep the layer open */ + + } /* next layer */ + + /* was anything found? */ + for(l=start; l>=stop; l--) { + if(GET_LAYER(map, l)->resultcache && GET_LAYER(map, l)->resultcache->numresults > 0) + return MS_SUCCESS; + } + + msSetError(MS_NOTFOUND, "No matching record(s) found.", "msQueryByFilter()"); + return MS_FAILURE; + + query_error: + msCopyExpression(&lp->filter, &old_filter); /* restore old filter */ + freeExpression(&old_filter); + msLayerClose(lp); + return MS_FAILURE; +} + int msQueryByRect(mapObj *map) { int l; /* counters */ diff --git a/mapserver.h b/mapserver.h index c7d4ed86b2..8389ba2573 100644 --- a/mapserver.h +++ b/mapserver.h @@ -443,7 +443,7 @@ enum MS_JOIN_TYPE {MS_JOIN_ONE_TO_ONE, MS_JOIN_ONE_TO_MANY}; #define MS_MULTIPLE 1 enum MS_QUERY_MODE {MS_QUERY_SINGLE, MS_QUERY_MULTIPLE}; -enum MS_QUERY_TYPE {MS_QUERY_IS_NULL, MS_QUERY_BY_POINT, MS_QUERY_BY_RECT, MS_QUERY_BY_SHAPE, MS_QUERY_BY_ATTRIBUTE, MS_QUERY_BY_INDEX, MS_QUERY_BY_OPERATOR}; +enum MS_QUERY_TYPE {MS_QUERY_IS_NULL, MS_QUERY_BY_POINT, MS_QUERY_BY_RECT, MS_QUERY_BY_SHAPE, MS_QUERY_BY_ATTRIBUTE, MS_QUERY_BY_INDEX, MS_QUERY_BY_OPERATOR, MS_QUERY_BY_FILTER}; enum MS_ALIGN_VALUE {MS_ALIGN_LEFT, MS_ALIGN_CENTER, MS_ALIGN_RIGHT}; @@ -733,6 +733,8 @@ typedef struct { char *item; /* by attribute */ char *str; + expressionObj *filter; /* by filter */ + int op; /* by GEOS operator */ int slayer; /* selection layer, used for msQueryByFeatures() (note this is not a query mode per se) */ @@ -1682,8 +1684,8 @@ MS_DLL_EXPORT int loadExpressionString(expressionObj *exp, char *value); /* Use this next, thread safe wrapper, function everywhere else */ MS_DLL_EXPORT int msLoadExpressionString(expressionObj *exp, char *value); MS_DLL_EXPORT char *msGetExpressionString(expressionObj *exp); +MS_DLL_EXPORT void initExpression(expressionObj *exp); MS_DLL_EXPORT void freeExpression(expressionObj *exp); -MS_DLL_EXPORT char *msGetExpressionString(expressionObj *exp); MS_DLL_EXPORT void msLayerSubstituteString(layerObj *layer, const char *from, const char *to); MS_DLL_EXPORT void msApplyDefaultSubstitutions(mapObj *map);