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

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

wfs filtering by bbox working with pointseries

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   
130    def getFeaturesByTemporalRange(self, range):
131        #TODO: getFeaturesByTemporalRange
132        return []
133   
134    def getFeaturesByPropertyEqualTo(self, propertyname, value):
135        log.debug('GET FEATURES BY PropertyEqualTo')
136        result=[]
137        if propertyname == 'csml:parameter':
138            log.debug('filtering on csml:parameter')         
139            for featureid,feature in self.featureinstances.iteritems():
140                if feature._feature.parameter.getStandardName() == value:
141                    result.append(feature)       
142        return result
143   
144class CSMLFeatureInstance(IFeatureInstance):
145    def __init__(self, title, abstract, feature):
146        """ representing a CSML Feature Instance
147        @ivar title: The title of the feature instance
148        @ivar abstract: The abstract of the feature instance
149        @ivar feature: the csml feature instance object
150         """
151        self.title=title
152        self.abstract=abstract
153        self._feature=feature
154        self.featuretype=self._feature.__class__.__name__
155        bb= self._feature.getCSMLBoundingBox().getBox()
156        #convert 0 - 360 to -180, 180 as per common WMS convention
157        if abs(bb[2]-bb[0]) >= 359 and abs(bb[2]-bb[0]) < 361:
158            bb[0], bb[2]=-180, 180
159        self.wgs84BBox = bb
160       
161    def toGML(self):
162        """ Create a GML (CSML) representation of the feature """
163       
164        #TODO un-hardcode this:
165        emptyelem=etree.Element('{http://ndg.nerc.ac.uk/csml}GridSeriesFeature')
166        csmlelem=self._feature.toXML(emptyelem)
167        return etree.tostring(csmlelem)
168       
169       
170     
171class CSMLwfsLayer(IwfsLayer):
172    """ representing a WFS FeatureType (termed layer here). Implements IwfsLayer
173    @ivar featuretype: The namespaced name of the feature type, e.g. csml:PointSeriesFeature
174   
175    """
176    def __init__(self, featuretype, wgs84bb):
177        self.type=featuretype
178        #Have to hard code some definitions for CSML feature types to
179        #use in the capabilities document as they don't exist anywhere else.
180        #Hardcoding is okay as this is a CSML specific interface, so will only ever deal
181        #with CSML feature types.
182        #TODO: However, might be better to move to some sort of schema.cfg file?
183        self.wgs84BBox=wgs84bb     
184        self.outputformats=['text/xml: subtype=csml/2.0']   
185        if self.type=='csml:GridSeriesFeature':
186            self.title='GridSeriesFeature as defined in Climate Science Modelling Language.'
187            self.abstract='The CSML GridSeriesFeature is used to represent 4D gridded data such as atmospheric model output.'
188            self.keywords=['Grid', 'Climate', 'CSML']
189        elif self.type=='csml:PointSeriesFeature':
190            self.title='PointSeriesFeature as defined in Climate Science Modelling Language.'
191            self.abstract='The CSML PointSeriesFeature represents a time series of measurements at a single point in space.'
192            self.keywords=['Point', 'Timeseries', 'Climate', 'CSML']
193        #TODO: definitions for all feature types.
194       
195       
196
Note: See TracBrowser for help on using the repository browser.