Changeset 4344 for cows


Ignore:
Timestamp:
17/10/08 15:07:01 (11 years ago)
Author:
domlowe
Message:

Refactored filterencoding to make it more flexible, including adding logical operators.

Location:
cows/trunk/cows
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • cows/trunk/cows/model/filterencoding.py

    r4311 r4344  
    55 
    66""" Classes to handle filter encoding queries as used in WFS """ 
    7  
    87 
    98from xml.etree import ElementTree as etree 
     
    1918def GML(tag): 
    2019    return "{"+nsGML+"}"+tag 
     20 
     21 
     22 
     23class AndOperator(object): 
     24    """ Return the intersection of two filters""" 
     25    def __init__(self, elem): 
     26        self.children=elem.getchildren() 
     27 
     28    def evaluate(self, featureset): 
     29        qp=QueryProcessor() 
     30        filter=qp.getFilterOrOperator(self.children[0]) 
     31        set1=filter.evaluate(featureset) 
     32        filter2=qp.getFilterOrOperator(self.children[1]) 
     33        set2=filter2.evaluate(featureset) 
     34        resultset = set1.intersection(set2) 
     35        return resultset 
    2136         
    22 class FEQuery(object): 
    23     """ Represents a WFS Query string which can contain feature type names and a variety of filters """ 
    24      
    25     def __init__(self, querystring): 
    26         self.querystring=str(querystring) #may be unicode, so convert to string 
    27         self.elem=etree.fromstring(self.querystring) 
    28         log.debug("Query String: %s"%self.querystring) 
    29         log.debug("Query Element: %s"%self.elem) 
    3037         
    31     def getTypeName(self): 
    32         typename=self.elem.get('typeName')    
    33         log.debug('TypeName = %s'%typename) 
    34         return typename    
    35      
    36     def getFilters(self): 
    37         """returns parts of query as individual filters which can then be translated into 
    38         method calls on the wfs_iface. Examples of filters include, getByGMLid, getByBBox, getByTimeRange""" 
    39         queryfilters=[] 
    40         for child in self.elem.getchildren(): 
    41             if child.tag==OGC('Filter'): 
    42                 #break <ogc:Filter> down into single filters. 
    43                 fp=FilterParser(child) 
    44                 for filter in fp.filters: 
    45                     queryfilters.append(filter) 
    46         return queryfilters      
     38class OrOperator(object): 
     39    """ Return the union of two filters""" 
     40    def __init__(self, elem): 
     41        self.children=elem.getchildren() 
    4742 
    48 class FilterParser(object): 
    49     """ Handles an OGC Filter object which in reality can contain several 'sub-filters'. Breaks this down into individual 'Filters' """ 
    50     def __init__(self, filterelem): 
    51         self.filters=[] 
    52         for elem in filterelem.getchildren(): 
    53             if elem.tag == OGC('GmlObjectId'): 
    54                 value=elem.get(GML('id')) 
    55                 f=Filter('GmlObjectId', value) 
    56                 self.filters.append(f) 
    57             elif elem.tag==OGC('BBOX'): 
    58                 value=(1,2,3,4) 
    59                 srsname='WGS84' 
    60                 f=BBoxFilter(srsname, 'BBOX', value) 
    61                 self.filters.append(f) 
    62                  
     43    def evaluate(self, featureset): 
     44        qp=QueryProcessor() 
     45        filter=qp.getFilterOrOperator(self.children[0]) 
     46        set1=filter.evaluate(featureset) 
     47        filter2=qp.getFilterOrOperator(self.children[1]) 
     48        set2=filter2.evaluate(featureset) 
     49        resultset = set1.union(set2) 
     50        return resultset 
     51 
     52#TODO: NOT operator 
    6353         
    6454class Filter(object): 
    65     """represents an individual WFS filter (i.e. a filter containing 'And' will be modelled as 2 filters). 
    66     @ivar filtertype: Type of filter (e.g. 'getByBBox') 
    67     @ivar fitlervalue: Appropriate value on which to filter (e.g. Bounding box tuple) 
    68     """ 
    69     def __init__(self, filtertype, filtervalue): 
    70         self.filtertype = filtertype 
    71         self.filtervalue = filtervalue 
     55    def __init__(self, elem): 
     56        self.elem=elem 
     57 
     58 
     59class GMLIdFilter(Filter): 
     60    def evaluate(self, featureset):         
     61        log.debug('Value of gml id filter %s'%self.elem.get(GML('id'))) 
     62        resultset=set([featureset.getFeatureByGMLid(self.elem.get(GML('id')))]) 
     63        return resultset 
    7264 
    7365class BBoxFilter(Filter): 
    74     """ As Filter, but with additional srsname attribute """ 
    75     def __init__(self, srsname, *args): 
    76         super(BBoxFilter, self).__init__(*args) 
    77         self.srsname=srsname 
     66    def evaluate(self, featureset): 
     67        #parse the bbox xml envelope to get values 
     68        envelope=self.elem.getchildren()[0] 
     69        srsname=envelope.get('srsName') 
     70        lowercorner=envelope.getchildren()[0].text.split() 
     71        uppercorner=envelope.getchildren()[1].text.split() 
     72        bbtuple=(float(lowercorner[0]),float(lowercorner[1]), float(uppercorner[0]), float(uppercorner[1])) 
     73        resultset=set(featureset.getFeaturesByBBox(bbtuple, srsname))                               
     74        return resultset         
     75 
     76class FEQueryProcessor(object): 
     77    def __init__(self): 
     78        pass 
     79     
     80    def evaluate(self, featureset, queryxml): 
     81        #The root elem should only have one direct child elemtent. Evaluating this  
     82        #child element should return the resultset for the entire nested filter. 
     83        self.rootelem=etree.fromstring(queryxml) 
     84        log.debug('filter root element %s'%self.rootelem) 
     85        log.debug('child elements %s'%self.rootelem.getchildren()) 
     86        filterelem=self.rootelem.getchildren()[0] 
     87        resultset=set() 
     88        for filterdef in filterelem.getchildren(): 
     89            filter=self.getFilterOrOperator(filterdef) 
     90            filterresult=filter.evaluate(featureset) 
     91            resultset=resultset.union(filterresult) 
     92        return resultset 
     93     
     94    def getFilterOrOperator(self, filterelem): 
     95        if filterelem.tag=='And': 
     96            f=AndOperator(filterelem) 
     97        elif filterelem.tag=='Or': 
     98            f=OrOperator(filterelem) 
     99        elif filterelem.tag ==OGC('GmlObjectId'): 
     100            f=GMLIdFilter(filterelem) 
     101        elif filterelem.tag ==OGC('BBOX'): 
     102            f=BBoxFilter(filterelem) 
     103        log.debug('Filter tag = %s'%filterelem.tag) 
     104        return f 
    78105         
    79 class FEQueryProcessor(object): 
    80     """ Given a Featureset and a list of of Filters, applies the filters  
    81     to the Featureset and creates a resultset  
    82     Assumptions: 
    83         1) GmlObjectId is only used as a filter alone or with other GmlObjectId filters, not with other filter types. 
    84         2) Other filter types, such as bounding box or temporal range filters only appear once.  
    85      
    86     """ 
    87      
    88     def __init__(self, featureset, filters): 
    89         self.featureset=featureset 
    90         self.resultset=[]               
    91         temporalrangeresults=[] 
    92         bboxresults=[] 
    93         temporalflag=False 
    94         bboxflag = False 
    95         for filter in filters: 
    96             log.debug('Filtertype %s'%filter.filtertype) 
    97             if filter.filtertype=='GmlObjectId':            
    98                 feature=self.featureset.getFeatureByGMLid(filter.filtervalue) 
    99                 if feature: 
    100                     self.resultset.append(feature)     
    101             else: 
    102                 #handle other filtertypes, bbox & temporal for now: 
    103                 if filter.filtertype=='BBOX': 
    104                     bboxflag=True 
    105                     bboxresults=self.featureset.getFeaturesByBBox(filter.filtervalue, filter.srsname)                     
    106                 elif filter.filtertype=='TemporalRange': 
    107                     temporalflag=True 
    108                     temporalrangeresults=self.featureset.getFeaturesByTemporalRange(filter.filtervalue)            
    109          
    110         #Now work out what the overlapping (if appropriate) resultset is. 
    111         log.debug('bboxflag %s'%bboxflag) 
    112         if temporalflag and bboxflag: #both filters applied 
    113             log.debug('results from temporal & bbox filter %s'%self.resultset) 
    114             self.resultset=self._combineresults([set(temporalrangeresults), set(bboxresults)]) 
    115         elif temporalflag: #only temporal filter 
    116             self.resultset=temporalrangeresults 
    117             log.debug('results from temporal only filter %s'%self.resultset) 
    118         elif bboxflag: #only bbox filter             
    119             self.resultset=bboxresults 
    120             log.debug('results from bbox only filter %s'%self.resultset) 
     106 
    121107             
    122         log.debug('results from gml filter %s'%self.resultset) 
    123         #TODO, handle null filter                          
    124              
    125                  
    126     def _combineresults(self, results):       
    127         """ combine (AND operation) results from two or more filters """ 
    128         baseset=[] 
    129         for set in results: 
    130             if baseset == []: 
    131                 baseset = set 
    132             else: 
    133                 baseset=baseset.intersection(set)                         
    134         return baseset 
    135              
    136              
  • cows/trunk/cows/pylons/wfs_controller.py

    r4298 r4344  
    1818 
    1919from cows.model.wfs import WfsFeatureSummary 
    20 from cows.model.filterencoding import FEQuery, FEQueryProcessor 
     20from cows.model.filterencoding import FEQueryProcessor 
    2121from cows.model import PossibleValues, WGS84BoundingBox, BoundingBox, Contents 
    2222from cows.pylons import ows_controller 
     
    157157 
    158158        #Parse the query to analyse the filters it contains 
    159         query=FEQuery(self.getOwsParam('query')) 
    160         filters=query.getFilters() 
     159        queryxml=self.getOwsParam('query') 
     160        qp=FEQueryProcessor() 
     161        c.resultset=qp.evaluate(self.featureset, queryxml) 
     162        log.debug('Final resultset from query processor %s'%c.resultset) 
    161163         
    162         #now apply the filters to the featureset 
    163         qp=FEQueryProcessor(self.featureset, filters) 
    164         #TODO: Group resultset together in a wfs feature collection (use template) 
    165 #        return qp.resultset 
    166         c.resultset=qp.resultset 
     164        
     165        #Group resultset together in a wfs feature collection (use template) 
    167166        response.headers['content-type'] = 'text/xml' 
    168167        t = ows_controller.templateLoader.load('wfs_featurecollection.xml') 
Note: See TracChangeset for help on using the changeset viewer.