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

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

rolling back to Bryans last commit

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