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

Line 
1# Adapted for numpy/ma/cdms2 by convertcdms.py
2"""
3Implementation of grid rendering classes.
4
5@author: Stephen Pascoe
6"""
7
8from view import GridRenderer
9import ImageColor, Image
10from cdms_compat import Numeric as N, byteTypecode, arrayAny, arrayMask
11import utils
12import logging
13from matplotlib import cm, colors
14
15logger = logging.getLogger('pywms.render_imp')
16
17class RgbaPNGRenderer(GridRenderer):
18    """Creates an RGBA PNG with a selectable matplotlib colour scale.
19    @todo: This shouldn't be called RgbaPNGRenderer because it produces PIL RGBA images, not just PNG.
20    """
21
22    mimeType = 'image/png'
23
24    def __init__(self, varmin, varmax):
25        self.setMinMax(varmin, varmax)
26       
27    def setMinMax(self, varmin, varmax):
28        self.varmin = varmin
29        self.varmax = varmax
30        self._norm = colors.normalize(varmin, varmax)
31       
32        logger.debug('Instantiating RgbaPNGRenderer with varmin=%s, varmax=%s'
33                     % (varmin, varmax))
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
43        cbar = (cmap(a) * 255).astype(byteTypecode).tostring()
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
52        logger.info('grid %s, %s, %s, %s, %s, %s' %
53                    (grid.lon0, grid.lat0, grid.dlon, grid.dlat, grid.nlon, grid.nlat))
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
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
63
64        # Scale Img to the right size
65        img = img.resize((pxPerGrid_x * grid.nlon, pxPerGrid_y * grid.nlat))
66
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
69
70        # Find top-left corner of grid
71        if grid.dlon > 0:
72            leftLon = grid.lon0
73        else:
74            leftLon = grid.lon0 + (grid.dlon * (grid.nlon - 1))
75        if grid.dlat > 0:
76            topLat = grid.lat0 + (grid.dlat * (grid.nlat - 1))
77        else:
78            topLat = grid.lat0
79
80        logger.info('top-left = %s lon, %s lat' % (leftLon, topLat))
81
82        # Find pixel position of top left centre grid box
83        cx = width * ((leftLon - bbox[0])/ (bbox[2] - bbox[0]))
84        cy = height * ((bbox[3] - topLat) / (bbox[3] - bbox[1]))
85
86        # Apply half width of grid box
87        ox = int(cx - pxPerGrid_x / 2)
88        oy = int(cy - pxPerGrid_y / 2)
89
90        # Paste the grid image into the tile image
91        tileImg = Image.new('RGBA', (width, height))
92        logger.info('Pasting image img%s into tileImg%s at (%s,%s)' % (tileImg.size, img.size, ox, oy))
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)
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
109
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!")
113
114        if grid.ilat < grid.ilon:
115            latLonOrdering = True
116        else:
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)
125   
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)
130
131        # Flip if lon or lat are ordered the wrong way
132        if grid.dlat > 0 and latLonOrdering:
133            logger.debug('Flipping y')
134            img = img.transpose(Image.FLIP_TOP_BOTTOM)
135        if grid.dlon < 0:
136            logger.debug('Flipping x')
137            img = img.transpose(Image.FLIP_LEFT_RIGHT)
138
139        # Rotate if axis order is lon, lat
140        if not latLonOrdering:
141            img = img.transpose(Image.ROTATE_90)
142
143
144        return img
145
146
147class RainbowPNGRenderer(GridRenderer):
148    """Creates an indexed PNG with a rainbow palette.
149
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.