source: cows/trunk/cows/service/imps/csmlbackend/wfs_csmllayer.py @ 4394

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/trunk/cows/service/imps/csmlbackend/wfs_csmllayer.py@4394
Revision 4394, 10.1 KB checked in by domlowe, 11 years ago (diff)

Minor changes to 2.0.0

Line 
1"""
2implementation of ILayerMapper, IwfsLayer, IDimension, ILayerSlab interfaces, as defined in wfs_iface.py & wxs_iface.py
3
4"""
5
6from cows.service.imps.csmlbackend.csmlcommon import CSMLLayerMapper, CSMLConnector
7from cows.service.wfs_iface import *
8import csml
9from xml.etree import ElementTree as etree
10from shapely.geometry import Polygon, Point
11from cows.bbox_util import union
12
13import logging
14log = logging.getLogger(__name__)
15
16class CSMLwfsLayerMapper(CSMLLayerMapper):
17    """
18    Map keyword arguments to a collection of layers.
19    Supports the retrieval of sets of layers according to arbitrary
20    keyword/value pairs.
21    Implements  ILayerMapper
22   
23    WFS differs from WMS/WCS in that the 'layers' are feature types, not instances.
24    So the CSMLwfsLayerMapper map method returns both a map of feature types and a map
25    of feature instances as both are needed for the GetFeature method to work.
26   
27    """
28    def __init__(self):
29        super(CSMLwfsLayerMapper, self).__init__()
30        self.featureinstancecache={}
31   
32
33    def map(self, **kwargs):
34        """
35        Given csml.parser.Dataset object list the names of
36        all layers available.
37       
38        @return: A mapping of layer names to ILayer implementations.
39        @raise ValueError: If no layers are available for these keywords.
40        """
41        fileoruri=kwargs['fileoruri']
42        if fileoruri in self.layermapcache.keys():
43            #we've accessed this layer map before, get it from the cache dictionary
44            return self.layermapcache[fileoruri], self.featureinstancecache[fileoruri]
45         
46        ds = self.connector.getCsmlDoc(fileoruri)
47       
48        #The WFS differs from WMS & WCS in that the contents layer is a list of
49        #feature *types* not *instances*. However a record of instances is also
50        #needed to fulfil GetFeature requests:
51        featureset=CSMLFeatureSet() #holds feature instances               
52        layermap={} #feature types       
53        aggregatedBBoxes={}#for aggregations of bounding boxes.
54       
55
56        for feature in csml.csmllibs.csmlextra.listify(ds.featureCollection.featureMembers):
57            title, abstract=self.getInfo(feature)
58            featureset.featureinstances[feature.id]=CSMLFeatureInstance(title, abstract, feature)             
59        for id, instance in featureset.featureinstances.iteritems():
60            ftype='csml:' + instance.featuretype
61            if ftype not in layermap.keys():
62                layermap[ftype]=CSMLwfsLayer(ftype, instance.wgs84BBox)
63                #instantiate an aggregator to compare future bounding boxes with.
64                aggregatedBBoxes[ftype]= instance.wgs84BBox               
65            else:
66                #the featuretype has already been stored in the dictionary.
67                #but, the bounding box may need changing to accommodate this new feature instance.
68#                log.debug('Checking bbox for feature id: %s and title: %s'%(instance._feature.id, instance.title))
69                currentbbox=aggregatedBBoxes[ftype]
70                newbbox=union(currentbbox, instance.wgs84BBox)
71                aggregatedBBoxes[ftype]= newbbox
72                layermap[ftype]=CSMLwfsLayer(ftype, newbbox)
73           
74
75        if len(layermap) > 0:
76            #cache results
77            self.layermapcache[fileoruri]=layermap
78            self.featureinstancecache[fileoruri]=featureset
79            return layermap, featureset
80        else:
81            raise ValueError
82
83class CSMLFeatureSet(IFeatureSet):
84    """ A set of features available via a WFS. Supports querying methods as used by OGG filters """
85    def __init__(self):
86        self.featureinstances={}
87   
88    def getFeatureByGMLid(self, gmlid):
89        return self.featureinstances[gmlid]
90   
91    def _checkforOverlap(self, filterbbox, featurebbox):
92        """ Uses Shapely Polygons to calculate bounding box intersections """
93        log.debug('comparing against %s'%str(filterbbox))       
94        filterpolygon=Polygon(((filterbbox[0],filterbbox[1]),(filterbbox[0],filterbbox[3]),(filterbbox[2],filterbbox[3]),(filterbbox[2],filterbbox[1])))
95        featurepolygon=Polygon(((featurebbox[0],featurebbox[1]),(featurebbox[0],featurebbox[3]),(featurebbox[2],featurebbox[3]),(featurebbox[2],featurebbox[1])))       
96        log.debug(dir(filterpolygon))
97        log.debug('intersect result%s'%featurepolygon.intersects(filterpolygon))
98        return filterpolygon.intersects(featurepolygon)
99   
100    def _checkforPointinBox(self, filterbbox, location):
101        """ Uses Shapely Polygons to calculate bounding box intersections """
102        log.debug('comparing against %s'%str(filterbbox))       
103        filterpolygon=Polygon(((filterbbox[0],filterbbox[1]),(filterbbox[0],filterbbox[3]),(filterbbox[2],filterbbox[3]),(filterbbox[2],filterbbox[1])))
104        featurepoint=Point(float(location[0]), float(location[1]))
105        log.debug(featurepoint.within(filterpolygon))
106        log.debug('intersect result%s'%featurepoint.intersects(filterpolygon))
107        return featurepoint.intersects(filterpolygon)
108   
109    def getFeaturesByBBox(self,bboxtuple, srsname):         
110        log.debug('GET FEATURES BY BBOX')
111        result=[]
112        log.debug('Request BBOX: %s %s'%(bboxtuple,srsname))
113        for featureid,feature in self.featureinstances.iteritems():
114            if hasattr(feature._feature, 'boundedBy'):
115                log.debug('Checking bbox for feature %s: '%feature._feature.id)
116                lowercorner=feature._feature.boundedBy.lowerCorner.CONTENT.split()
117                uppercorner=feature._feature.boundedBy.upperCorner.CONTENT.split()
118                featurebbox=(float(lowercorner[0]), float(lowercorner[1]), float(uppercorner[0]), float(uppercorner[1]))               
119                if self._checkforOverlap(bboxtuple, featurebbox):
120                    result.append(feature) 
121            elif hasattr(feature._feature, 'location'):
122                log.debug('Checking location for feature %s: '%feature._feature.id)
123                log.debug(feature._feature.location)
124                location=feature._feature.location.CONTENT.split()
125                if self._checkforPointinBox(bboxtuple, location):
126                    result.append(feature)                       
127        return result
128
129    def getFeaturesByPropertyBetween(self, propertyname, minvalue, maxvalue):
130        log.debug('GET FEATURES BY PropertyBetween')
131        log.debug('propertyname, min, max: %s, %s, %s'%(propertyname, minvalue, maxvalue))
132        result = []
133        #Identifies times overlapping between filter and feature times
134        if propertyname=='csml:value/csml:PointSeriesCoverage/csml:pointSeriesDomain/csml:TimeSeries/csml:timePositionList':
135            for featureid, feature in self.featureinstances.iteritems():
136                featuretimes=feature._feature.value.pointSeriesDomain.timePositionList.CONTENT.split()
137                featureMinTime=featuretimes[0]
138                featureMaxTime=featuretimes[len(featuretimes)-1]
139                if csml.csmllibs.csmltime.compareTimes(featureMinTime, minvalue ,featureMaxTime) == 1:
140                    result.append(feature)
141                elif csml.csmllibs.csmltime.compareTimes(featureMinTime, maxvalue ,featureMaxTime) == 1:
142                    result.append(feature)   
143        return result
144       
145    def getFeaturesByPropertyEqualTo(self, propertyname, value):
146        log.debug('GET FEATURES BY PropertyEqualTo')
147        result=[]
148        if propertyname == 'csml:parameter':
149            log.debug('filtering on csml:parameter')         
150            for featureid,feature in self.featureinstances.iteritems():
151                if feature._feature.parameter.getStandardName() == value:
152                    result.append(feature)       
153        return result
154   
155class CSMLFeatureInstance(IFeatureInstance):
156    def __init__(self, title, abstract, feature):
157        """ representing a CSML Feature Instance
158        @ivar title: The title of the feature instance
159        @ivar abstract: The abstract of the feature instance
160        @ivar feature: the csml feature instance object
161         """
162        self.title=title
163        self.abstract=abstract
164        self._feature=feature
165        self.featuretype=self._feature.__class__.__name__
166        bb= self._feature.getCSMLBoundingBox().getBox()
167        #convert 0 - 360 to -180, 180 as per common WMS convention
168        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
169            bb[0], bb[2]=-180, 180
170        self.wgs84BBox = bb
171       
172    def toGML(self):
173        """ Create a GML (CSML) representation of the feature """
174       
175        #TODO un-hardcode this:
176        emptyelem=etree.Element('{http://ndg.nerc.ac.uk/csml}GridSeriesFeature')
177        csmlelem=self._feature.toXML(emptyelem)
178        return etree.tostring(csmlelem)
179       
180       
181     
182class CSMLwfsLayer(IwfsLayer):
183    """ representing a WFS FeatureType (termed layer here). Implements IwfsLayer
184    @ivar featuretype: The namespaced name of the feature type, e.g. csml:PointSeriesFeature
185   
186    """
187    def __init__(self, featuretype, wgs84bb):
188        self.type=featuretype
189        #Have to hard code some definitions for CSML feature types to
190        #use in the capabilities document as they don't exist anywhere else.
191        #Hardcoding is okay as this is a CSML specific interface, so will only ever deal
192        #with CSML feature types.
193        #TODO: However, might be better to move to some sort of schema.cfg file?
194        self.wgs84BBox=wgs84bb     
195        self.outputformats=['text/xml: subtype=csml/2.0']   
196        if self.type=='csml:GridSeriesFeature':
197            self.title='GridSeriesFeature as defined in Climate Science Modelling Language.'
198            self.abstract='The CSML GridSeriesFeature is used to represent 4D gridded data such as atmospheric model output.'
199            self.keywords=['Grid', 'Climate', 'CSML']
200        elif self.type=='csml:PointSeriesFeature':
201            self.title='PointSeriesFeature as defined in Climate Science Modelling Language.'
202            self.abstract='The CSML PointSeriesFeature represents a time series of measurements at a single point in space.'
203            self.keywords=['Point', 'Timeseries', 'Climate', 'CSML']
204        #TODO: definitions for all feature types.
205       
206       
207
Note: See TracBrowser for help on using the repository browser.