source: cows/branches/cows-vis/cows/service/imps/pywms/render_impddc.py @ 5265

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/cows/branches/cows-vis/cows/service/imps/pywms/render_impddc.py@5265
Revision 5265, 7.0 KB checked in by domlowe, 11 years ago (diff)

adding separate cowsclient pylons app

RevLine 
[5265]1# Adapted for numpy/ma/cdms2 by convertcdms.py
[4808]2"""
3Implementation of grid rendering classes.
4
5@author: Stephen Pascoe
6"""
7
8from view import GridRenderer
9import ImageColor, Image
[5265]10from cdms_compat import Numeric as N, byteTypecode, arrayAny, arrayMask
11import utils
[4808]12import logging
13from matplotlib import cm, colors
14
[5265]15logger = logging.getLogger('pywms.render_imp')
[4808]16
[5265]17class RgbaPNGRenderer(GridRenderer):
[4808]18    """Creates an RGBA PNG with a selectable matplotlib colour scale.
[5265]19    @todo: This shouldn't be called RgbaPNGRenderer because it produces PIL RGBA images, not just PNG.
[4808]20    """
21
22    mimeType = 'image/png'
23
24    def __init__(self, varmin, varmax):
[5265]25        self.setMinMax(varmin, varmax)
26       
27    def setMinMax(self, varmin, varmax):
[4808]28        self.varmin = varmin
29        self.varmax = varmax
30        self._norm = colors.normalize(varmin, varmax)
[5265]31       
32        logger.debug('Instantiating RgbaPNGRenderer with varmin=%s, varmax=%s'
33                     % (varmin, varmax))
[4808]34
35    def renderColourbar(self, width, height, cmap, isVertical=True):
36        a = N.arrayrange(0, 1.0, 1.0/256)
37        if isVertical:
38            a = a[-1::-1]
39            shape = (1, 256)
40        else:
41            shape = (256, 1)
42
[5265]43        cbar = (cmap(a) * 255).astype(byteTypecode).tostring()
[4808]44        img = Image.frombuffer("RGBA", shape, cbar, "raw", "RGBA", 0, 1)
45        img = img.resize((width, height))
46
47        return img
48
49    def renderGrid(self, grid, bbox, width, height, cmap):
50        cmap.set_bad('#ffffff', 0.0)
51
[5265]52        logger.info('grid %s, %s, %s, %s, %s, %s' %
53                    (grid.lon0, grid.lat0, grid.dlon, grid.dlat, grid.nlon, grid.nlat))
[4808]54
55        # Get a pixel = grid-box image for the grid
56        img = self._grid2Img(grid, cmap)
57
58        # Calculate the pixel-size of a grid box
[5265]59        pxPerDeg_lon = float(width) / (bbox[2] - bbox[0])
60        pxPerDeg_lat = float(height) / (bbox[3] - bbox[1])
61        pxPerGrid_x = grid.dlon * pxPerDeg_lon
62        pxPerGrid_y = grid.dlat * pxPerDeg_lat
[4808]63
64        # Scale Img to the right size
[5265]65        img = img.resize((pxPerGrid_x * grid.nlon, pxPerGrid_y * grid.nlat))
[4808]66
[5265]67        # We assume the grid points represent the centre of the grid boxes
68        # therefore we can calculate the upper-left position of the image
[4808]69
70        # Find top-left corner of grid
[5265]71        if grid.dlon > 0:
72            leftLon = grid.lon0
[4808]73        else:
[5265]74            leftLon = grid.lon0 + (grid.dlon * (grid.nlon - 1))
75        if grid.dlat > 0:
76            topLat = grid.lat0 + (grid.dlat * (grid.nlat - 1))
[4808]77        else:
[5265]78            topLat = grid.lat0
[4808]79
[5265]80        logger.info('top-left = %s lon, %s lat' % (leftLon, topLat))
[4808]81
82        # Find pixel position of top left centre grid box
[5265]83        cx = width * ((leftLon - bbox[0])/ (bbox[2] - bbox[0]))
84        cy = height * ((bbox[3] - topLat) / (bbox[3] - bbox[1]))
[4808]85
86        # Apply half width of grid box
[5265]87        ox = int(cx - pxPerGrid_x / 2)
88        oy = int(cy - pxPerGrid_y / 2)
[4808]89
90        # Paste the grid image into the tile image
91        tileImg = Image.new('RGBA', (width, height))
[5265]92        logger.info('Pasting image img%s into tileImg%s at (%s,%s)' % (tileImg.size, img.size, ox, oy))
[4808]93        tileImg.paste(img, (ox, oy))
94
95        return tileImg
96
97
98    def _grid2Img(self, grid, cmap):
99        """Returns the grid as an image where each pixel is one grid box.
100
101        """
102        a = self._norm(grid.value)
[5265]103        img_buf = (cmap(a) * 255).astype(byteTypecode)
104        if arrayAny(arrayMask(a)):
105            logger.info('Masking grid')
106            alpha_buf = (-arrayMask(a)*255 + 255).astype(byteTypecode)
107        else:
108            alpha_buf = None
[4808]109
[5265]110        # This code assumes the axis ordering is either (lat, lon, time) or (lon, lat, time)
111        if min(grid.ilat, grid.ilon) != 0 and max(grid.ilat, grid.ilon) != 1:
112            raise ValueError("Latitude and longitude must be the first 2 dimensions!")
[4808]113
[5265]114        if grid.ilat < grid.ilon:
115            latLonOrdering = True
[4808]116        else:
[5265]117            latLonOrdering = False
118
119        if latLonOrdering:
120            (y, x, c) = img_buf.shape
121            shape = (x, y)
122        else:
123            (x, y, c) = img_buf.shape
124            shape = (y, x)
[4808]125   
[5265]126        img = Image.frombuffer("RGBA", shape, img_buf.tostring(), "raw", "RGBA", 0, 1)
127        if alpha_buf is not None:
128            alpha = Image.frombuffer("L", shape, alpha_buf.tostring(), "raw", "L", 0, 1)
129            img.putalpha(alpha)
[4808]130
[5265]131        # Flip if lon or lat are ordered the wrong way
132        if grid.dlat > 0 and latLonOrdering:
[4808]133            logger.debug('Flipping y')
134            img = img.transpose(Image.FLIP_TOP_BOTTOM)
[5265]135        if grid.dlon < 0:
[4808]136            logger.debug('Flipping x')
137            img = img.transpose(Image.FLIP_LEFT_RIGHT)
138
[5265]139        # Rotate if axis order is lon, lat
140        if not latLonOrdering:
141            img = img.transpose(Image.ROTATE_90)
[4808]142
[5265]143
[4808]144        return img
145
146
[5265]147class RainbowPNGRenderer(GridRenderer):
148    """Creates an indexed PNG with a rainbow palette.
[4808]149
[5265]150    @deprecated
151    """
152
153    mimeType = 'image/png'
154
155    def __init__(self, varmin, varmax):
156        palette = []
157        for x in range(256):
158            palette.extend(ImageColor.getrgb('hsl(%d,100%%,50%%)' % x))
159        self._palette = palette
160
161        self.varmin = varmin
162        self.varmax = varmax
163
164    def renderColourbar(self, width, height, isVertical=True):
165        if isVertical:
166            s = N.arrayrange(255, -1, -1, typecode='b').tostring()
167            shape = (1, 256)
168        else:
169            s = N.arrayrange(256, typecode='b').tostring()
170            shape = (256, 1)
171
172        img = Image.frombuffer("P", shape, s, 'raw', 'P', 0, 1)
173        img = img.resize((width, height))
174        img.putpalette(self._palette)
175
176        return img
177
178    def renderGrid(self, grid, bbox, width, height):
179
180        bboxObj = utils.BBox(bbox)
181
182        # If a non-CRS:84 bbox is requested we must retrieve the valid
183        # sub-bbox and paste it into an image of the correct size.
184        # It is assumed that grid can wrap around in the longitude and therefore only
185        # needs adjusting in the latitude.
186        if bbox != bboxObj.crs84:
187            img = Image.new('P', (width, height))
188            img.putpalette(self._palette)
189            (ox, oy) = bboxObj.getCrs84OffsetInImage(width, height)
190            nwidth, nheight = bboxObj.getCrs84ImageSize(width, height)
191            # If the image is too small just send a blank image
192            if bboxObj.crs84Width > abs(grid.dlon) and bboxObj.crs84Height > abs(grid.dlat):
193                img1 = self._grid2Img(grid, width, nheight)
194                img.paste(img1, (0, oy))
195        else:
196            img = self._grid2Img(grid, width, height)
197
198        return img
199
200    def _grid2Img(self, grid, width, height):
201        var = grid.value
202        a = ((var - self.varmin) / self.varmax * 255).filled(0).astype(byteTypecode)
203
204        img = Image.frombuffer('P', list(var.shape)[::-1], a.tostring(), 'raw', 'P', 0, 1)
205        img.putpalette(self._palette)
206
207        if grid.dlat > 0:
208            img = img.transpose(Image.FLIP_TOP_BOTTOM)
209        if grid.dlon < 0:
210            img = img.transpose(Image.FLIP_LEFT_RIGHT)
211
212        return img.resize((width, height))
Note: See TracBrowser for help on using the repository browser.