source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/csml_wcs.py @ 2700

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/controllers/csml_wcs.py@2700
Revision 2700, 10.2 KB checked in by domlowe, 12 years ago (diff)

resolving conflicts

Line 
1# Copyright (C) 2007 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"""
6WCS controller driven by CSML.
7
8@author: DominicLowe, Stephen Pascoe
9"""
10
11#NOTE, much of this is straight from WMS and needs to be overhauled.
12import os, time, string, StringIO
13
14from ows_server.lib.base import *
15from ows_server.lib.decorators import *
16import ows_server.lib.validators as V
17
18from elementtree import ElementTree as ET
19from ows_common import exceptions as OWS_E
20from ows_common.wcs import *
21from ows_common.common import BoundingBox
22from ows_common.domain import ValuesUnit, PossibleValues
23
24from ows_server.lib.csml_util import get_csml_doc, extractToNetCDF
25from ows_server.lib.csml_cache import csmlCache, extractCache
26
27from ows_server.controllers.status import StatusController
28
29from email.mime.multipart import MIMEMultipart
30from email.mime.base import MIMEBase
31from email.mime.text import MIMEText
32from email import encoders
33
34class CsmlWcsController(OwsController):
35    _ows_parameters = {
36        'Format': make_domain(['text/xml']),
37        'ExceptionFormat': make_domain(['text/xml']),
38        }
39
40
41
42    def _createMultipartMime(self, xml, netcdf):
43        #returns a multipart mime file containing the coverage xml + a netcdf file
44       
45        # Create the container (outer) email message.
46        msg = MIMEMultipart()       
47       
48        xmlfile =StringIO.StringIO(xml)
49        xmlfile.readline(), xmlfile.readline() #don't read in first 2 lines (the content type) as MIMEBase also provides it.
50           
51       
52        #add the XML part
53        submsg=MIMEText(xmlfile.read(), _subtype='xml')
54        submsg.add_header('Content-ID', '<coverage.xml>')
55        submsg.add_header('Content-Disposition', 'attachment; filename="coverage.xml"')
56        #submsg.set_type('text/xml; name="coverage.xml"')
57        msg.attach(submsg)
58
59       
60        #add the NetCDF part
61        submsg= MIMEBase('application', 'CF-netcdf') #check in ogc docs
62        submsg.set_payload(netcdf.read())
63        submsg.set_type('application/CF-netcdf')
64        submsg.add_header('Content-Disposition', 'attachment; filename="coverage.nc"')
65        submsg.add_header('Content-ID', '<coverage.nc>')
66        netcdf.close()
67        # Encode the payload using Base64
68        encoders.encode_base64(submsg)
69        msg.attach(submsg)
70       
71        #return the message
72        return msg
73       
74
75    def _loadFeatureDimensions(self, feature):
76        dims = {}
77        #!WARNING
78        # This bit is a hack until the CSML API implements a mechanism
79        # to determine which elements of a domain are longitude and latitude.
80        lon=feature.getLongitudeAxis()
81        lat=feature.getLongitudeAxis()
82        domain=feature.getDomain()
83        for axis_name, axis in domain.iteritems():
84            if axis_name in [lon,lat]:
85                continue           
86            dims[axis_name] =            Domain(possibleValues=PossibleValues.fromAllowedValues(axis),
87                                 #!TODO: this is a fudge until we can deduce UOM.
88                                 valuesUnit=ValuesUnit(uoms=[''],
89                                                       referenceSystems=['']))
90        return dims
91
92    def _loadFeatureSummary(self, feature):
93        dims = self._loadFeatureDimensions(feature)
94        #TODO - need to ensure proper values for Name. ID and Description are populated.
95        cvgTitle=[feature.description.CONTENT]
96        cvgDescription=feature.description.CONTENT
97        cvgAbstract=[feature.description.CONTENT]
98        bbox=c.dataset.getBoundingBox()  #TODO, use the bounding box of the feature not the dataset.
99        return WcsDatasetSummary(identifier=feature.id,
100                                 titles=cvgTitle,
101                                 boundingBoxes=[BoundingBox([bbox[0],bbox[1]], [bbox[2],bbox[3]],
102                                 crs='CRS:84')], dimensions=dims,  description=cvgDescription,abstracts=cvgAbstract
103                                 )
104
105    def _loadCapabilities(self):
106        """
107        Overriding subclass to add layer capabilities
108
109        """
110        # Get default capabilities from superclass
111        sm = super(CsmlWcsController, self)._loadCapabilities()
112        ds = WcsDatasetSummary(titles=['Root Dataset'], datasetSummaries=[], CRSs=['CRS:84'])
113        # Add a DatasetSummary for each feature       
114        for f_n in c.dataset.getFeatureList():       
115            feature_ds = self._loadFeatureSummary(c.dataset.getFeature(f_n))
116            ds.datasetSummaries.append(feature_ds)
117        sm.contents = Contents(datasetSummaries=[ds])
118        return sm
119
120   
121    @operation
122    @parameter('Format', possibleValues=['text/xml'])
123    @parameter('Service', possibleValues=['WCS'], required=True)
124    @parameter('Version', possibleValues=['1.1.0'])
125    def GetCapabilities(self, fileoruri, service=None, version=None):
126        """
127        @note: format and updatesequence parameters are not supported
128            by this WMS.
129
130        """
131        # Populate the context object with information required by the template
132       
133        #get doc from cache or disk:
134        c.dataset = csmlCache[fileoruri]
135        if type(c.dataset) is str:
136            #If not a csml datset is some message from exist such as 'access denied'           
137            return Response(c.dataset)
138        return self._renderCapabilities('ows/wcs_capabilities')
139
140   
141    @operation
142    @parameter('Version', possibleValues=['1.1.0'], required=True)
143    @parameter('Identifier', required=True)
144    @parameter('BoundingBox', required=True, validator=V.bbox_2or3d)
145    @parameter('TimeSequence',required=True, validator=V.iso8601_time)
146    @parameter('Format', possibleValues=['application/netcdf'], required=True)
147    @parameter('Store', validator = V.boolean('Store'))
148    @parameter('Status', validator = V.boolean('Status'))
149    #TODO some more parameters to add here
150    # Dimension parameters Time, Elevation, etc. are handled separately
151    def GetCoverage(self, fileoruri, version, format, identifier, boundingbox, timesequence, store=False, status=False):
152        try:
153                    # Retrieve dataset and selected feature           
154            dataset = csmlCache[fileoruri]
155            feature = dataset.getFeature(identifier)
156            if feature is None:
157                raise OWS_E.InvalidParameterValue('Coverage not found', 'identifier')
158           
159                       
160            #set bounding box TODO
161            sel = dict(latitude=(boundingbox[1], boundingbox[3]), longitude=(boundingbox[0], boundingbox[2]))           
162            sel['time']=timesequence
163           
164            lon=feature.getLongitudeAxis()
165            lat=feature.getLatitudeAxis()
166            t=feature.getTimeAxis()
167           
168            if None in [lon, lat, time]:
169                #TODO need to return a suitable wcs error.
170                pass
171           
172            #create selection dictionary:
173            sel={}
174            sel[lat]=(boundingbox[1], boundingbox[3])
175            sel[lon]=(boundingbox[0], boundingbox[2])
176            sel[t]=timesequence
177           
178            #z is the 4th axis (eg height or pressure).
179            #NOTE, need to decide whether bounding box is of form: x1,y1,z1,x2,y2,z2 or x1,y1,x2,y2,z1,z2
180            #currently the latter is implemented.
181           
182            if len(boundingbox)  == 6:
183                for ax in feature.getAxisLabels():
184                    if ax not in [lat, lon, t]:
185                        #must be Z 
186                        z=str(ax)
187                        sel[z]=(boundingbox[4], boundingbox[5])
188           
189           
190            axisNames=feature.getAxisLabels()
191       
192                       
193            # Extract via CSML.subsetToGridSeries()
194            if store:
195                #need to farm off to WPS
196                #but for now...
197                filename = extractToNetCDF(feature, sel, publish = True) 
198            else:
199                filename = extractToNetCDF(feature, sel)
200
201               
202            #use the randomly allocated filename as a basis for an identifier           
203            f=os.path.basename(filename)
204            c.fileID=os.path.splitext(f)[0]
205           
206            #Depending on if the 'store' parameter is set, either return the netcdf file + coverage xml as a multipart mime or return a coverage document containing a link.
207                     
208            if store:
209                if status:
210                    #STORE = true, STATUS = true:
211                    #hand off file "id" to StatusController to determine correct ExectuteResponse type response.
212                    status=StatusController()
213                    jobID=os.path.splitext(os.path.basename(filename)[9:])[0] #remove the 'csml_wxs_' prefix and file extension to create a jobID
214                    return status.getStatus(jobID)                   
215                else:
216                    #STORE=true, STATUS = false: Return Coverage XML document with link to file.
217                    #use the temp file name (minus extension) as an ID
218                    c.hyperlink = 'http://'+request.environ['HTTP_HOST']+'/'+os.path.basename(request.environ['paste.config']['app_conf']['publish_dir'])+'/'+os.path.basename(filename)
219                    r=render_response('wcs_getCoverageResponse', format='xml')
220                    r.headers['content-type'] = 'text/xml'
221                    return r                                 
222            else:               
223                #STORE = FALSE, STATUS therefore irrelevant, return Multipart Mime.
224                netcdfFile=open(filename, 'r')
225                c.hyperlink="cid:coverage.nc"
226                xmlfile=render_response('wcs_getCoverageResponse', format='xml')
227                xmlfile.headers['content-type'] = 'text/xml'
228                multipart=self._createMultipartMime(xmlfile, netcdfFile)       
229                msg=multipart
230                #msg=open(multipart, 'r').readlines()
231                return Response(content=msg, mimetype='multipart') 
232                 
233        except Exception, e:
234            if isinstance(e, OWS_E.OwsError):
235                raise e
236            else:
237                raise e
238                #raise OWS_E.NoApplicableCode(e)
239       
240       
Note: See TracBrowser for help on using the repository browser.