1 | """ |
---|
2 | WFS controller for OGC Web Services (OWS). |
---|
3 | |
---|
4 | @author: Dominic Lowe |
---|
5 | """ |
---|
6 | |
---|
7 | import re |
---|
8 | from cStringIO import StringIO |
---|
9 | from sets import Set |
---|
10 | from matplotlib.cm import get_cmap |
---|
11 | from pylons import request, response, c, config |
---|
12 | import ConfigParser |
---|
13 | |
---|
14 | import logging |
---|
15 | log = logging.getLogger(__name__) |
---|
16 | |
---|
17 | import Image |
---|
18 | |
---|
19 | from cows.model.wfs import WfsFeatureSummary |
---|
20 | from cows.model.filterencoding import FEQuery, FEQueryProcessor |
---|
21 | from cows.model import PossibleValues, WGS84BoundingBox, BoundingBox, Contents |
---|
22 | from cows.pylons import ows_controller |
---|
23 | from cows.exceptions import * |
---|
24 | from cows import bbox_util |
---|
25 | |
---|
26 | class 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']) |
---|
43 | validVersions = ['1.1.0'] |
---|
44 | |
---|
45 | #------------------------------------------------------------------------- |
---|
46 | |
---|
47 | def __before__(self, **kwargs): |
---|
48 | """ |
---|
49 | This default implementation of __before__() will pass all routes |
---|
50 | arguments to the layer mapper to retrieve a list of layers for |
---|
51 | this WFS. |
---|
52 | |
---|
53 | It will be called automatically by pylons before each action method. |
---|
54 | |
---|
55 | |
---|
56 | """ |
---|
57 | log.debug('loading layers') |
---|
58 | self.layers, self.featureset = self.layerMapper.map(**kwargs) |
---|
59 | log.debug('Feature instances %s'%self.featureset.featureinstances) |
---|
60 | |
---|
61 | #------------------------------------------------------------------------- |
---|
62 | # Methods implementing stubs in OWSController |
---|
63 | |
---|
64 | def _renderCapabilities(self, version, format): |
---|
65 | """ |
---|
66 | Renders capabilities document. |
---|
67 | """ |
---|
68 | if version == '1.1.0': |
---|
69 | t = ows_controller.templateLoader.load('wfs_capabilities_1_1_0.xml') |
---|
70 | else: |
---|
71 | # We should never get here! The framework should raise an exception before now. |
---|
72 | raise RuntimeError("Version %s not supported" % version) |
---|
73 | |
---|
74 | return t.generate(c=c).render() |
---|
75 | |
---|
76 | def _loadCapabilities(self): |
---|
77 | """ |
---|
78 | @note: Assumes self.layers has already been created by __before__(). |
---|
79 | Builds capabilities document. |
---|
80 | |
---|
81 | """ |
---|
82 | log.info('Loading WFS Capabilites') |
---|
83 | |
---|
84 | ows_controller.addOperation('GetFeature') |
---|
85 | ows_controller.addOperation('DescribeFeature') |
---|
86 | |
---|
87 | |
---|
88 | featureInfoFormats = Set() |
---|
89 | |
---|
90 | log.debug('Loading capabilities contents') |
---|
91 | c.capabilities.contents = Contents() |
---|
92 | |
---|
93 | |
---|
94 | ftlist={} |
---|
95 | # |
---|
96 | |
---|
97 | for layerName, layer in self.layers.items(): |
---|
98 | log.info('Loading layer %s' % layerName) |
---|
99 | # log.info('feature type %s'%layer._feature) |
---|
100 | |
---|
101 | wgs84BBox = WGS84BoundingBox(layer.wgs84BBox[:2], |
---|
102 | layer.wgs84BBox[2:]) |
---|
103 | |
---|
104 | ds = WfsFeatureSummary(keywords=layer.keywords, |
---|
105 | outputformats=layer.outputformats, |
---|
106 | identifier=layerName, |
---|
107 | titles=[layer.title], |
---|
108 | abstracts=[layer.abstract], |
---|
109 | wgs84BoundingBoxes=[wgs84BBox]) |
---|
110 | |
---|
111 | c.capabilities.contents.datasetSummaries.append(ds) |
---|
112 | |
---|
113 | def _getSchema(self, typename): |
---|
114 | namespace = typename.split(':')[0] |
---|
115 | schemalocation = conf |
---|
116 | |
---|
117 | def _parsetypename(self, typename): |
---|
118 | """ parse feature type name into schema and name""" |
---|
119 | if typename not in self.layers.keys(): |
---|
120 | 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') |
---|
121 | |
---|
122 | namespace, ft = typename.split(':') |
---|
123 | wfsconfiglocation=config['wfsconfig'] |
---|
124 | wfscfg = ConfigParser.ConfigParser() |
---|
125 | wfscfg.read(wfsconfiglocation) |
---|
126 | xmlschema=open(wfscfg.get('application_schemas', namespace)).read() |
---|
127 | log.debug('location of application schema %s' %(xmlschema)) |
---|
128 | return xmlschema |
---|
129 | |
---|
130 | |
---|
131 | def DescribeFeatureType(self): |
---|
132 | """ DescribeFeatureType """ |
---|
133 | version = self.getOwsParam('version', default=self.validVersions[0]) |
---|
134 | if version not in self.validVersions: |
---|
135 | raise InvalidParameterValue('Version %s not supported' % version, |
---|
136 | 'version') |
---|
137 | typename=self.getOwsParam('typename') |
---|
138 | ftschema =self._parsetypename(typename) |
---|
139 | log.debug(self.layers.items()) |
---|
140 | |
---|
141 | outputformat=self.getOwsParam('outputformat', default='text/xml') |
---|
142 | |
---|
143 | #temporarily returns entire schema |
---|
144 | #TODO: return single featuretype definition |
---|
145 | msg = ftschema |
---|
146 | response.headers['content-type'] = 'text/xml' |
---|
147 | return msg |
---|
148 | |
---|
149 | def GetFeature(self): |
---|
150 | """ GetFeature request |
---|
151 | """ |
---|
152 | #TODO QUERY model |
---|
153 | version = self.getOwsParam('version', default=self.validVersions[0]) |
---|
154 | if version not in self.validVersions: |
---|
155 | raise InvalidParameterValue('Version %s not supported' % version, |
---|
156 | 'version') |
---|
157 | |
---|
158 | #Parse the query to analyse the filters it contains |
---|
159 | query=FEQuery(self.getOwsParam('query')) |
---|
160 | filters=query.getFilters() |
---|
161 | |
---|
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 |
---|
167 | response.headers['content-type'] = 'text/xml' |
---|
168 | t = ows_controller.templateLoader.load('wfs_featurecollection.xml') |
---|
169 | return t.generate(c=c).render() |
---|
170 | |
---|
171 | |
---|
172 | # featureid=self.getOwsParam('featureid') |
---|
173 | # #TODO populate self.featureinstances! |
---|
174 | # feature=self.featureset.featureinstances[featureid] |
---|
175 | # response.headers['content-type'] = 'text/xml' |
---|
176 | # return feature.toGML() |
---|
177 | # |
---|
178 | |
---|
179 | |
---|