source: cows/trunk/cows/model/filterencoding.py @ 4311

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/model/filterencoding.py@4311
Revision 4311, 5.6 KB checked in by domlowe, 11 years ago (diff)

Supporting code for bbox and temporal filtering. Methods not yet implemented.

Line 
1# Copyright (C) 2008 STFC & NERC (Science and Technology Facilities Council).
2# This software may be distributed under the terms of the
3# Q Public License, version 1.0 or later.
4# http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5
6""" Classes to handle filter encoding queries as used in WFS """
7
8
9from xml.etree import ElementTree as etree
10import logging
11log = logging.getLogger(__name__)
12
13""" utility functions to save writing out fully qualified names """
14nsOGC = 'http://www.opengis.net/ogc'
15def OGC(tag):
16    return "{"+nsOGC+"}"+tag
17
18nsGML = 'http://www.opengis.net/gml'
19def GML(tag):
20    return "{"+nsGML+"}"+tag
21       
22class 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)
30       
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     
47
48class 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               
63       
64class 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
72
73class 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
78       
79class 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)
121           
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           
Note: See TracBrowser for help on using the repository browser.