source: cows/trunk/cows/pylons/wfs_controller.py @ 4422

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/pylons/wfs_controller.py@4422
Revision 4422, 11.0 KB checked in by domlowe, 12 years ago (diff)

Extended framework to allow for wfs:AdditionalObjects - which can be used for csml storage descriptors.

Line 
1"""
2WFS controller for OGC Web Services (OWS).
3
4@author: Dominic Lowe
5"""
6
7import re
8from cStringIO import StringIO
9from sets import Set
10from matplotlib.cm import get_cmap
11from pylons import request, response, c, config
12import ConfigParser
13import xml.etree.ElementTree as etree
14import logging
15log = logging.getLogger(__name__)
16
17import Image
18
19from cows.model.wfs import WfsFeatureSummary
20from cows.model.filterencoding import FEQueryProcessor
21from cows.model import PossibleValues, WGS84BoundingBox, BoundingBox, Contents
22from cows.pylons import ows_controller
23from cows.exceptions import *
24from cows import bbox_util
25
26class WFSController(ows_controller.OWSController):
27    """
28    Subclass this controller in a pylons application and set the layerMapper
29    class attribute to implement a WFS. Each layer can be mapped to a Feature for the WFS.
30
31    @cvar layerMapper: an cows.service.wxs_iface.ILayerMapper object.
32   
33
34    """
35    layerMapper = None
36    _layerSlabCache = {}
37
38    #-------------------------------------------------------------------------
39    # Attributes required by OWSController
40
41    service = 'WFS'
42    owsOperations = (ows_controller.OWSController.owsOperations + ['DescribeFeatureType', 'GetFeature', 'DescribeStoredQueries','ListStoredQueries', 'GetPropertyValue'])
43    validVersions = ['1.1.0', '2.0.0']
44   
45
46    #-------------------------------------------------------------------------
47
48    def __before__(self, **kwargs):
49        """
50        This default implementation of __before__() will pass all routes
51        arguments to the layer mapper to retrieve a list of layers for
52        this WFS.
53
54        It will be called automatically by pylons before each action method.
55
56
57        """
58        log.debug('loading layers')
59        self.layers, self.featureset = self.layerMapper.map(**kwargs)
60        log.debug('Feature instances %s'%self.featureset.featureinstances)
61   
62        #-------------------------------------------------------------------------
63        # Methods implementing stubs in OWSController
64
65    def _renderCapabilities(self, version, format):
66        """
67        Renders capabilities document.
68        """
69        if version == '1.1.0':
70            t = ows_controller.templateLoader.load('wfs_capabilities_1_1_0.xml')
71        elif version == '2.0.0':
72            t = ows_controller.templateLoader.load('wfs_capabilities_2_0_0.xml')
73        else:
74            # We should never get here!  The framework should raise an exception before now.
75            raise RuntimeError("Version %s not supported" % version)
76       
77        return t.generate(c=c).render()
78
79    def _loadCapabilities(self):
80        """
81        @note: Assumes self.layers has already been created by __before__().
82        Builds capabilities document.
83
84        """
85        log.info('Loading WFS Capabilites')
86       
87        ows_controller.addOperation('GetFeature')
88        ows_controller.addOperation('DescribeFeature')
89        ows_controller.addOperation('DescribeStoredQueries')
90        ows_controller.addOperation('ListStoredQueries')
91        ows_controller.addOperation('GetPropertyValue')
92       
93       
94        featureInfoFormats = Set()
95
96        log.debug('Loading capabilities contents')
97        c.capabilities.contents = Contents()
98       
99       
100        ftlist={}
101        #       
102       
103        for layerName, layer in self.layers.items():
104            log.info('Loading layer %s' % layerName)
105#            log.info('feature type %s'%layer._feature)
106
107            wgs84BBox = WGS84BoundingBox(layer.wgs84BBox[:2],
108                                         layer.wgs84BBox[2:])
109           
110            ds = WfsFeatureSummary(keywords=layer.keywords, 
111                                   outputformats=layer.outputformats, 
112                                   identifier=layerName,
113                                   titles=[layer.title],
114                                   abstracts=[layer.abstract],                                   
115                                   wgs84BoundingBoxes=[wgs84BBox])
116
117            c.capabilities.contents.datasetSummaries.append(ds)
118
119    def _getSchema(self, typename):
120        namespace = typename.split(':')[0]
121        schemalocation = conf
122       
123    def _parsetypename(self, typename):
124        """ parse feature type name into schema and name and return schema"""       
125        if typename not in self.layers.keys():
126            raise InvalidParameterValue('Invalid typename parameter: %s. Typename must consist of namespace and featuretype separated with a colon, as displayed in the GetCapabilities response.'%typename, 'typename')
127   
128        namespace, ft = typename.split(':')
129        wfsconfiglocation=config['wfsconfig']
130        wfscfg = ConfigParser.ConfigParser()
131        wfscfg.read(wfsconfiglocation)     
132        xmlschema=open(wfscfg.get('application_schemas', namespace)).read()     
133        log.debug('location of application schema %s' %(xmlschema))
134        return xmlschema
135   
136    def _runQuery(self, queryxml = None, storedqueryid=None, **kwargs):
137        """ this is used by both the GetFeature and GetPropertyValue methods to
138        run a wfs:query over a featurecollection and return a subset of the collection.
139        The query may be defined as an xml <wfs:query> or may be referenced by a StoredQuery_id
140        """
141        additionalobjects=[] #not implemented for filter encoding, just for storedqueries (e.g. subsetting)
142        if queryxml:
143            qp=FEQueryProcessor()
144            resultset=qp.evaluate(self.featureset, queryxml)
145            log.debug('Final resultset from query processor %s'%resultset)
146        elif storedqueryid:
147            storedquery, func=self.layerMapper.queryDescriptions.queries[storedqueryid]
148            storedqueryresult=func(self.featureset, **kwargs)
149            if len(storedqueryresult) == 2:
150                resultset=storedqueryresult[0]
151                additionalobjects=storedqueryresult[1]
152            else:
153                resultset=storedqueryresult
154            log.debug('Final resultset from stored query %s'%resultset)
155        else:           
156            #If neither query or storedquery_id are provided then return entire featureset
157            resultset=self.featureset.getFeatureList()
158        return resultset, additionalobjects
159
160    def _applyXPath(self, xpath, features):
161        """ applies an xpath expression to a set of features and returns
162        a set of wfs:members containing property values expressed in the xpath"""
163        resultset=[]       
164        for feature in features:
165            featureXML=feature._feature.elem #the underlying XML
166            valuecomponent=featureXML.find(xpath)
167            if valuecomponent is not None:
168                valstr=etree.tostring(valuecomponent)           
169                resultset.append(valstr)
170        return resultset     
171   
172   
173    def DescribeFeatureType(self):
174        """ DescribeFeatureType """
175        version = self.getOwsParam('version', default=self.validVersions[0])
176        if version not in self.validVersions:
177            raise InvalidParameterValue('Version %s not supported' % version,
178                                        'version')
179        typename=self.getOwsParam('typename')
180        ftschema =self._parsetypename(typename)
181        log.debug(self.layers.items())       
182               
183        outputformat=self.getOwsParam('outputformat', default='text/xml')
184       
185        #temporarily returns entire schema
186        #TODO: return single featuretype definition
187        msg  = ftschema
188        response.headers['content-type'] = 'text/xml'
189        return msg
190           
191 
192    def GetPropertyValue(self):
193        """ GetPropertyValue request - similar to get feature but only returns chosen property
194        values """
195        valueReference = self.getOwsParam('valueReference')
196        queryxml=self.getOwsParam('query',default=None)
197        storedqueryid=self.getOwsParam('storedquery_id', default=None)
198        #get any other parameters from self._owsParams and pass them to the stored query
199        otherparams={}
200        for key in self._owsParams.keys():
201            if key not in ['query', 'request', 'service', 'version', 'storedquery_id', 'valuereference']:
202                otherparams[key]=self._owsParams[key]     
203        featureresultset, additionalobjects =self._runQuery(queryxml, storedqueryid, **otherparams)       
204       
205        #Now need to take account of valueReferencexpath, and distill the
206        #resultset down to just the requested properties.
207        c.resultset=self._applyXPath(valueReference, featureresultset)
208       
209        response.headers['content-type'] = 'text/xml'
210        #TODO, new template for values
211        t = ows_controller.templateLoader.load('wfs_valuecollection.xml')
212        return t.generate(c=c).render() 
213 
214    def GetFeature(self):
215        """ GetFeature request
216        """
217        version = self.getOwsParam('version', default=self.validVersions[0])
218        if version not in self.validVersions:
219            raise InvalidParameterValue('Version %s not supported' % version,
220                                        'version')
221
222        #The GetFeature request may either use the 'query' filter encoding or a
223        #storedquery, but not both:     
224        #Parse the query to analyse the filters it contains
225        queryxml=self.getOwsParam('query',default=None)
226        storedqueryid=self.getOwsParam('storedquery_id', default=None)
227       
228        #retrieve any other parameters and pass them off to the stored query (they are ignored in the case of the queryxml option
229        otherparams={}
230        for key in self._owsParams.keys():
231            if key not in ['query', 'request', 'service', 'version', 'storedquery_id']:
232                otherparams[key]=self._owsParams[key]       
233        c.resultset, c.additionalobjects=self._runQuery(queryxml, storedqueryid, **otherparams)
234               
235        #Group resultset together in a wfs feature collection (use template)
236        response.headers['content-type'] = 'text/xml'
237        t = ows_controller.templateLoader.load('wfs_featurecollection.xml')
238        return t.generate(c=c).render()           
239
240   
241    def DescribeStoredQueries(self):
242        """ DescribeStoredQueries method. Takes zero or more stored query ids as args"""
243        allqueries=self.layerMapper.queryDescriptions.queries       
244        storedqueryid=self.getOwsParam('storedqueryid', default=None)
245        if storedqueryid is None:
246            c.storedqueries = self.layerMapper.queryDescriptions.queries
247        else:
248            c.storedqueries={}
249            for queryid in storedqueryid.split(','):
250                c.storedqueries[queryid]=self.layerMapper.queryDescriptions.queries[queryid]       
251        response.headers['Content-Type'] = 'text/xml'
252        t = ows_controller.templateLoader.load('wfs_describestoredqueries.xml')
253        return t.generate(c=c).render() 
254         
255    def ListStoredQueries(self):
256        """ DescribeStoredQueries method. Takes zero or more stored query ids as args"""
257        c.storedqueries = self.layerMapper.queryDescriptions.queries
258        t = ows_controller.templateLoader.load('wfs_liststoredqueries.xml')
259        response.headers['Content-Type'] = 'text/xml'
260        return t.generate(c=c).render() 
Note: See TracBrowser for help on using the repository browser.