Ignore:
Timestamp:
13/07/09 13:31:56 (10 years ago)
Author:
pnorton
Message:

Made some more modifications to the ddc WMS. I've tried to reduce the amount of code duplication between the code and the cows egg.

Location:
qesdi/wms_ddc_vis/trunk/lib
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • qesdi/wms_ddc_vis/trunk/lib

    • Property svn:ignore set to
      wms_ddc_vis.egg-info
  • qesdi/wms_ddc_vis/trunk/lib/wms_ddc_vis/controllers/csmlwms.py

    r5403 r5479  
    11import logging 
    22import Image 
    3 import thread 
    43import re 
    54from StringIO import StringIO 
    6 from sets import Set 
    7 from matplotlib.cm import get_cmap 
    85from pylons import request, response, c 
    9 from routes import url_for 
    10 from routes.util import GenerationException 
    11 from genshi.template import NewTextTemplate 
     6 
     7from cows import bbox_util 
     8 
     9from wms_ddc_vis.model.ddc_layer_mapper import DDCLayerMapper 
     10from wms_ddc_vis.model.ddc_wms_layer import STYLES, RENDERING_OPTIONS 
     11 
    1212from cows.pylons.wms_controller import WMSController 
    13 from cows.model.wms import WmsDatasetSummary, Dimension, DataURL 
    14 from cows.model import PossibleValues, WGS84BoundingBox, BoundingBox, Contents 
    15 from cows.pylons import ows_controller 
    16 from cows.exceptions import * 
    17 from cows import bbox_util 
    18  
    19 from cows.service.imps.csmlbackend.wms_csmllayer import CSMLwmsLayerMapper 
    20  
    21 from wms_ddc_vis.model.ddc_layer_mapper import DDCLayerMapper 
     13 
     14 
    2215log = logging.getLogger(__name__) 
    2316 
    24 class CsmlwmsController(ows_controller.OWSController): 
     17class CsmlwmsController(WMSController): 
    2518    layerMapper = DDCLayerMapper() 
    2619 
    27     #layers = {}     
    28     _pilImageFormats = { 
    29         'image/png': 'PNG', 
    30         'image/jpg': 'JPEG', 
    31         'image/gif': 'GIF', 
    32         'image/tiff': 'TIFF' 
    33         } 
    34      
    35     _layerSlabCache = {} 
    36  
    37     #------------------------------------------------------------------------- 
    38     # Attributes required by OWSController 
    39  
    40     service = 'WMS' 
    41     owsOperations = (ows_controller.OWSController.owsOperations + 
    42         ['GetMap', 'GetContext', 'GetLegend', 'GetFeatureInfo', 'GetInfo']) 
    43     validVersions = ['1.1.1', '1.3.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 WMS. 
    52  
    53         It will be called automatically by pylons before each action method. 
    54  
    55         @todo: The layer mapper needs to come from somewhere. 
    56  
    57         """ 
    58         #self.updateSequence = "hello" 
    59         log.debug("loading layers") 
    60         #print self.layers 
    61         self.layers = self.layerMapper.map(**kwargs) 
    62      
    63         #------------------------------------------------------------------------- 
    64         # Methods implementing stubs in OWSController 
    65  
    66     def _renderCapabilities(self, version, format): 
    67         if format == 'application/json': 
    68             t = ows_controller.templateLoader.load('wms_capabilities_json.txt', 
    69                                                    cls=NewTextTemplate) 
    70         elif version == '1.1.1': 
    71             t = ows_controller.templateLoader.load('wms_capabilities_1_1_1.xml') 
    72         elif version == '1.3.0': 
    73             t = ows_controller.templateLoader.load('wms_capabilities_1_3_0.xml') 
    74         else: 
    75             # We should never get here!  The framework should raise an exception before now. 
    76             raise RuntimeError("Version %s not supported" % version) 
    77          
    78         return t.generate(c=c).render() 
    79  
    80     def _loadCapabilities(self): 
    81         """ 
    82         @note: Assumes self.layers has already been created by __before__(). 
    83  
    84         """ 
    85         #!TODO: Add json format to GetCapabilities operation 
    86  
    87         ows_controller.addOperation('GetMap', formats=self._pilImageFormats.keys()) 
    88         ows_controller.addOperation('GetContext', formats=['text/xml', 'application/json']) 
    89         ows_controller.addOperation('GetLegend', 
    90                                     formats=['image/png']) 
    91         ows_controller.addOperation('GetInfo') 
    92          
    93         featureInfoFormats = Set() 
    94  
    95         log.debug('Loading capabilities contents') 
    96         c.capabilities.contents = Contents() 
    97         for layerName, layer in self.layers.items(): 
    98             log.debug('LayerName: %s' % layerName) 
    99             log.debug('Loading layer %s' % layerName) 
    100  
    101             wgs84BBox = WGS84BoundingBox(layer.wgs84BBox[:2], 
    102                                          layer.wgs84BBox[2:]) 
    103             # Get CRS/BBOX pairs 
    104             bboxObjs = [] 
    105             for crs in layer.crss: 
    106                 bbox = layer.getBBox(crs) 
    107                 bboxObjs.append(BoundingBox(bbox[:2], bbox[2:], crs=crs)) 
    108             # Get dimensions 
    109             dims = {} 
    110             for dimName, dim in layer.dimensions.items(): 
    111                 dimParam = self._mapDimToParam(dimName) 
    112                 dims[dimParam] = Dimension(valuesUnit=dim.units, 
    113                                           unitSymbol=dim.units, 
    114                                           possibleValues= 
    115                                             PossibleValues.fromAllowedValues(dim.extent)) 
    116             # Does the layer implement GetFeatureInfo? 
    117             if layer.featureInfoFormats: 
    118                 queryable = True 
    119                 featureInfoFormats.union_update(layer.featureInfoFormats) 
    120             else: 
    121                 queryable = False 
    122                  
    123             #URL to WCS - uses named route 'wcsroute' 
    124             #TODO: Allow for a WCS blacklist to opt out of providing dataurls for certain datasets? 
    125             #TODO: How to make this more configurable - what if WCS is not coupled with WMS? 
    126             try: 
    127                 version='1.0.0' #wcs version 
    128                 wcsbaseurl=url_for('wcsroute', fileoruri=c.fileoruri,qualified=True)+'?' 
    129                 dataURLs=[DataURL(format='WCS:CoverageDescription', onlineResource='%sService=WCS&Request=DescribeCoverage&Coverage=%s&Version=%s'%(wcsbaseurl, layerName, version))] 
    130             except GenerationException: 
    131                 log.info("dataURLs not populated: could not generate WCS url with url_for('wcsroute', filedoruri=%s,qualified=True)"%c.fileoruri) 
    132                 dataURLs=[] 
    133             # Create the cows object 
    134             ds = WmsDatasetSummary(identifier=layerName, 
    135                                    titles=[layer.title], 
    136                                    CRSs=layer.crss, 
    137                                    wgs84BoundingBoxes=[wgs84BBox], 
    138                                    boundingBoxes=bboxObjs, 
    139                                    abstracts=[layer.abstract], 
    140                                    dimensions=dims, 
    141                                    queryable=queryable, 
    142                                    dataURLs=dataURLs) 
    143  
    144             # Stuff that should go in the capabilities tree eventually 
    145             ds.legendSize = layer.legendSize 
    146             ds.legendFormats = ['image/png'] 
    147  
    148             c.capabilities.contents.datasetSummaries.append(ds) 
    149  
    150         # Add this operation here after we have found all formats 
    151         ows_controller.addOperation('GetFeatureInfo', 
    152                                     formats = list(featureInfoFormats)) 
    153  
    154     def _getLayerParamInfo(self, paramName='layers'): 
     20    def _getLayerParam(self, paramName='layers'): 
    15521        """ 
    15622        Retrieve the layers parameter enforcing the rule of only 
     
    16127 
    16228        """ 
    163         layerName = self.getOwsParam(paramName) 
    164  
     29        layers = {} 
     30        layerNames = self.getOwsParam(paramName) 
     31         
    16532        # Select the first layer if several are requested. 
    16633        # This plays nicer with mapClient. 
    167         if ',' in layerName: 
    168             #layerName = layerName.split(',')[0] 
    169             raise InvalidParameterValue( 
    170                 'Multi-layer GetLegend requests are not supported', 'layers') 
    171         try: 
    172             layerObj = self.layers[layerName] 
    173         except KeyError: 
    174             raise InvalidParameterValue('Layer %s not found' % layerName, 
    175                                         paramName) 
    176  
    177         return layerName, layerObj 
    178  
    179     def _getLayerParam(self, paramName='layers'): 
    180         """ 
    181         Retrieve the layers parameter enforcing the rule of only 
    182         selecting one layer. 
    183  
    184         @param paramName: Overrides the query string parameter name to 
    185             look for.  This is usefull for implementing GetFeatureInfo. 
    186  
    187         """ 
    188         layers = {} 
    189         layerNames = self.getOwsParam(paramName) 
    190  
    191         # Select the first layer if several are requested. 
    192         # This plays nicer with mapClient. 
    193         #if ',' in layerName: 
    19434        layerNames = layerNames.split(',') 
    195         #raise InvalidParameterValue( 
    196         #    'Multi-layer GetMap requests are not supported', 'layers') 
     35         
     36        layerObjects = [] 
     37         
    19738        for layerName in layerNames: 
    19839            try: 
    19940                layerObj = self.layers[layerName] 
    200                 layers[layerName] = layerObj 
     41                layerObjects.append(layerObj) 
     42#                layers[layerName] = layerObj 
    20143            except KeyError: 
    20244                raise InvalidParameterValue('Layer %s not found' % layerName, 
    20345                                        paramName) 
    20446 
    205         #return layerName, layerObj 
    206         return layers 
    207  
    208     def _getFormatParam(self): 
    209         format = self.getOwsParam('format', default='image/png') 
    210         if format not in self._pilImageFormats: 
    211             raise InvalidParameterValue( 
    212                 'Format %s not supported' % format, 'format') 
    213  
    214         return format 
    215  
    216     _escapedDimNames = ['width', 'height', 'version', 'request', 
    217                         'layers', 'styles', 'crs', 'srs', 'bbox', 
    218                         'format', 'transparent', 'bgcolor', 
    219                         'exceptions'] 
    220  
    221     def _getDimValues(self, layerObj): 
    222         dimValues = {} 
    223         for dimName, dim in layerObj.dimensions.items(): 
    224             defaultValue = dim.extent[0] 
    225             escapedDimName=self._mapDimToParam(dimName) 
    226             dimValues[escapedDimName] = self.getOwsParam(escapedDimName, 
    227                                                   default=defaultValue) 
    228         return dimValues 
    229  
    230     def _mapDimToParam(self, dimName): 
    231         """ 
    232         Dimension names might clash with WMS parameter names, making 
    233         them inaccessible in WMS requests.  This method maps a 
    234         dimension name to a parameter name that appears in the 
    235         capabilities document and WMS requests. 
    236  
    237         """ 
    238         if dimName.lower() in self._escapedDimNames: 
    239             return dimName+'_dim' 
    240         else: 
    241             return dimName 
    242          
    243     def _mapParamToDim(self, dimParam): 
    244         """ 
    245         Maps a dimension parameter name to it's real dimension name. 
    246  
    247         @see: _mapDimToParam() 
    248  
    249         """ 
    250         try: 
    251             dimName = re.match(r'(.*)_dim$', dimParam).group(1) 
    252             if dimName.lower() in self._escapedDimNames: 
    253                 return dimName 
    254             else: 
    255                 return dimParam 
    256         except AttributeError: 
    257             return dimParam 
    258  
    259  
    260     def _retrieveSlab(self, layerObj, srs, dimValues, renderOpts): 
     47        return layerNames, layerObjects 
     48        #return layers 
     49 
     50 
     51         
     52    def _retrieveSlab(self, layerObj, srs, dimValues, style, transparent, renderOpts): 
     53         
    26154        # Find the slab in the cache first 
    262         cacheKey = layerObj.getCacheKey(srs, dimValues) 
     55        cacheKey = layerObj.getCacheKey(srs, style, transparent, dimValues, renderOpts) 
    26356        slab = self._layerSlabCache.get(cacheKey) 
     57         
    26458        if slab is None: 
    265             slab = layerObj.getSlab(srs, dimValues, renderOpts) 
     59             
     60            slab = layerObj.getSlab(srs, style, transparent, dimValues, renderOpts) 
     61             
    26662            if cacheKey is not None: 
    26763                self._layerSlabCache[cacheKey] = slab 
     
    27672        # Housekeeping 
    27773        version = self.getOwsParam('version', default=self.validVersions[0]) 
     74         
    27875        if version not in self.validVersions: 
    27976            raise InvalidParameterValue('Version %s not supported' % version, 
    28077                                        'version') 
     78         
     79         
     80        renderingOptions = {} 
     81        for opt in RENDERING_OPTIONS: 
     82            if self.getOwsParam(opt, default=None) != None: 
     83                renderingOptions[opt] = self.getOwsParam(opt) 
     84         
     85        log.debug("renderingOptions = %s" % (renderingOptions,)) 
     86         
     87 
     88         
     89        transparent = self.getOwsParam('transparent', default='FALSE') 
     90         
     91        transparent = transparent.lower() == 'true' 
     92         
     93        bgcolor = self.getOwsParam('bgcolor', default='0xFFFFFF') 
     94 
     95        # Layer handling 
     96        layerNames, layerObjects = self._getLayerParam() 
     97#        layers = self._getLayerParam() 
     98         
     99        log.debug("layerNames = %s" % (layerNames,)) 
     100        log.debug("layerObjects = %s" % (layerObjects,)) 
     101         
    281102        styles = self.getOwsParam('styles', default='') 
    282         transparent = self.getOwsParam('transparent', default='FALSE') 
    283         bgcolor = self.getOwsParam('bgcolor', default='0xFFFFFF') 
    284  
    285         # Layer handling 
    286         #layerName, layerObj = self._getLayerParam() 
    287         layers = self._getLayerParam() 
    288         log.debug('GetMap request for layer(s) %s  #%s ' % (layers, thread.get_ident(),) ) 
     103         
     104        if styles != None: 
     105            styles = styles.split(',') 
     106            assert len(styles) == len(layerNames) 
     107         
     108        log.debug("style = %s" % (styles,))         
     109         
    289110        # Coordinate parameters 
    290111        bbox = tuple(float(x) for x in self.getOwsParam('bbox').split(',')) 
     
    306127                'Format %s not supported' % format, 'format') 
    307128 
     129 
    308130        finalImg = Image.new('RGBA', (width, height), (0,0,0,0)) 
    309      
     131 
    310132        # Multiple Layers handling..   
    311         for layerName, layerObj in layers.iteritems(): 
     133#        for layerName, layerObj in layers.iteritems(): 
     134        for i in range(len(layerNames)): 
     135             
     136            layerName = layerNames[i] 
     137            layerObj = layerObjects[i] 
     138            if styles != None: 
     139                style = styles[i] 
     140            else: 
     141                style = "" 
     142                 
     143            if style == "": 
     144                style = STYLES.DEFAULT 
     145                 
     146             
    312147            if srs not in layerObj.crss: 
    313148                raise InvalidParameterValue('Layer %s does not support SRS %s' % (layerName, srs)) 
     
    325160            #!TODO: Minimum and maximum values 
    326161 
    327             slab = self._retrieveSlab(layerObj, srs, restoredDimValues, 
    328                                       dict(minValue=0, maxValue=100)) 
     162            slab = self._retrieveSlab(layerObj, srs, restoredDimValues, style, 
     163                                      transparent, renderingOptions) 
    329164 
    330165            # We must request a bbox within the layer's bbox. 
     
    336171            log.debug('ibbox = %s' % (ibbox,)) 
    337172 
    338  
    339             finalImg = slab.getImage(bbox, width, height) 
    340              
    341 #            # If bbox is not within layerObj.bbox then we need to calculate the 
    342 #            # pixel offset of the inner bbox, request the right width/height 
    343 #            # and paste the image into a blank background 
    344 #            if bbox == ibbox: 
    345 #                img = slab.getImage(bbox, width, height) 
    346 #                log.debug('slab image.size = %s' % (img.size,)) 
    347 #             
    348 #            else: 
    349 #                 
    350 #                ix0, iy0 = bbox_util.geoToPixel(ibbox[0], ibbox[3], bbox, width, height, 
    351 #                                                roundUpY=True) 
    352 #                ix1, iy1 = bbox_util.geoToPixel(ibbox[2], ibbox[1], bbox, width, height, 
    353 #                                                roundUpX=True) 
    354 #                iw = ix1-ix0 
    355 #                ih = iy1-iy0 
    356 #                log.debug('Deduced inner image: %s, (%d x %d)' % ((ix0, iy0, ix1, iy1), iw, ih)) 
    357 #                img1 = slab.getImage(ibbox, iw, ih) 
    358 # 
    359 #                img = Image.new('RGBA', (width, height)) 
    360 #                img.paste(img1, (ix0, iy0)) 
    361 #         
    362 #            finalImg = Image.composite(finalImg, img, finalImg)     
    363              
    364        
    365        
    366          
     173            log.debug("width, height = %s, %s" % (width, height,)) 
     174            img = slab.getImage(bbox, width, height) 
     175             
     176            log.debug("img = %s format=%s, size=%s, mode=%s" % (img, img.format, img.size, img.mode)) 
     177            log.debug("finalImg = %s format=%s, size=%s, mode=%s" % (finalImg, finalImg.format, finalImg.size, finalImg.mode)) 
     178 
     179            finalImg = Image.composite(finalImg, img, finalImg)     
     180 
     181             
    367182        # IE < 7 doesn't display the alpha layer right.  Here we sniff the 
    368183        # user agent and remove the alpha layer if necessary. 
     
    381196        response.write(buf.getvalue()) 
    382197 
    383  
    384     def GetContext(self): 
    385         """ 
    386         Return a WebMap Context document for a given set of layers. 
    387  
    388         """ 
    389         # Parameters 
    390         layers = self.getOwsParam('layers', default=None) 
    391         format = self.getOwsParam('format', default='text/xml') 
    392  
    393         # Filter self.layers for selected layers 
    394         if layers is not None: 
    395             newLayerMap = {} 
    396             for layerName in layers.split(','): 
    397                 try: 
    398                     newLayerMap[layerName] = self.layers[layerName] 
    399                 except KeyError: 
    400                     raise InvalidParameterValue('Layer %s not found' % layerName, 
    401                                                 'layers') 
    402                      
    403             self.layers = newLayerMap 
    404  
    405         # Automatically select the first bbox/crs for the first layer 
    406         aLayer = self.layers.values()[0] 
    407         crs = aLayer.crss[0] 
    408         bb = aLayer.getBBox(crs) 
    409         c.bbox = BoundingBox(bb[:2], bb[2:], crs) 
    410  
    411         # Initialise as if doing GetCapabilities 
    412         ows_controller.initCapabilities() 
    413         self._loadCapabilities() 
    414  
    415         if format == 'text/xml': 
    416             response.headers['Content-Type'] = format 
    417             t = ows_controller.templateLoader.load('wms_context_1_1_1.xml') 
    418             return t.generate(c=c).render() 
    419         elif format == 'application/json': 
    420             response.headers['Content-Type'] = format 
    421             t = ows_controller.templateLoader.load('wms_context_json.txt', 
    422                                                    cls=NewTextTemplate) 
    423             return t.generate(c=c).render() 
    424         else: 
    425             raise InvalidParameterValue('Format %s not supported' % format) 
    426  
    427     def GetFeatureInfo(self): 
    428         # Housekeeping 
    429         version = self.getOwsParam('version', default=self.validVersions[0]) 
    430         if version not in self.validVersions: 
    431             raise InvalidParameterValue('Version %s not supported' % version, 
    432                                         'version') 
    433  
    434         # Coordinate parameters 
    435         bbox = tuple(float(x) for x in self.getOwsParam('bbox').split(',')) 
    436         width = int(self.getOwsParam('width')) 
    437         height = int(self.getOwsParam('height')) 
    438        
    439         # Get pixel location 
    440         i = int(self.getOwsParam('i')) 
    441         j = int(self.getOwsParam('j')) 
    442  
    443         # Translate to geo-coordinates 
    444         x, y = bbox_util.pixelToGeo(i, j, bbox, width, height) 
    445         #start preparing GetFeatureInfo response. Assumes "HTML" output format 
    446  
    447         htmlResponse = "<html><body><p> <b>Feature Information about pixel position: "+self.getOwsParam('i')+","+self.getOwsParam('j')+"/geo position: "+str(x)+","+str(y) +"<b/></p>" 
    448          
    449          
    450         layers = self._getLayerParam('query_layers') 
    451         #Adjusts response for multiple layers 
    452         if len(layers) > 1: 
    453             htmlResponse = htmlResponse+" Multiple possible features found as follows:" 
    454    
    455         htmlResponse = htmlResponse+"<ul>" 
    456          
    457         format = self.getOwsParam('info_format', default='text/html') 
    458         for layerName, layerObj in layers.iteritems(): 
    459             log.debug('Format: %s' % format) 
    460             log.debug('Title: %s' % layerObj.title) 
    461             log.debug('FeatureInfoFormats: %s' % layerObj.featureInfoFormats) 
    462              
    463             if format not in layerObj.featureInfoFormats: 
    464                 raise InvalidParameterValue('Layer %s does not support GetFeatureInfo in format %s' %(layerName, format), 'info_format') 
    465  
    466         if version == '1.1.1': 
    467             srs = self.getOwsParam('srs') 
    468         else: 
    469             srs = self.getOwsParam('crs') 
    470  
    471         if srs not in layerObj.crss: 
    472             raise InvalidParameterValue('Layer %s does not support SRS %s' % 
    473                                         (layerName, srs)) 
    474  
    475         # Dimension handling 
    476         dimValues = {} 
    477         for dimName, dim in layerObj.dimensions.items(): 
    478             defaultValue = dim.extent[0] 
    479             dimValues[dimName] = self.getOwsParam(dimName, default=defaultValue) 
    480          
    481         response.headers['Content-Type'] = format 
    482         response.write(layerObj.getFeatureInfo(format, srs, (x, y), dimValues)) 
    483  
    484198    def GetLegend(self): 
    485199        """ 
     
    493207        # This hook alows extra arguments to be passed to the layer backend.  It 
    494208        # is required for UKCP. 
    495         renderOpts = dict(request_params=self._owsParams) 
    496  
    497         img = layerObj.getLegendImage(renderOpts=renderOpts) 
     209#        renderOpts = dict(request_params=self._owsParams) 
     210      
     211        renderingOptions = {} 
     212        for opt in RENDERING_OPTIONS: 
     213            if self.getOwsParam(opt, default=None) != None: 
     214                renderingOptions[opt] = self.getOwsParam(opt) 
     215         
     216#        log.debug("renderingOptions = %s" % (renderingOptions,)) 
     217 
     218        img = layerObj.getLegendImage(renderOpts=renderingOptions) 
    498219 
    499220        buf = StringIO() 
     
    503224        response.write(buf.getvalue()) 
    504225 
    505  
    506     def GetInfo(self): 
    507         from pprint import pformat 
    508         request.headers['Content-Type'] = 'text/ascii' 
    509         response.write('Some info about this service\n') 
    510         for layer in model.ukcip02.layers: 
    511             response.write('Layer %s: %s\n' % (layer, pformat(g.ukcip02_layers[layer].__dict__))) 
    512  
    513              
Note: See TracChangeset for help on using the changeset viewer.