source: TI05-delivery/ows_framework/trunk/ows_server/ows_server/lib/render.py @ 2582

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/ows_framework/trunk/ows_server/ows_server/lib/render.py@2582
Revision 2582, 4.8 KB checked in by spascoe, 13 years ago (diff)

Render routines ported from ddc-vis. Untested.

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"""
6Code to render CDMS variables to PIL images
7
8@author: Stephen Pascoe
9
10"""
11
12import ImageColor, Image
13from ows_server.lib.grid_util import *
14from matplotlib import cm, colors
15
16class BBox(object):
17    """Class to encapsulate a WMS BBOX and do some basic arrithmatic.
18
19    Openlayers will ask for BBOXs out of the CRS:84 bounds.  Methods in this
20    class help handle this.
21    """
22
23    def __init__(self, bbox):
24        # sanity check
25        (x1,y1,x2,y2) = bbox
26
27        self.bbox = bbox
28
29    def _getCRS84(self):
30        """
31        @return: the BBOX tuple truncated to CRS:84
32        """
33
34        (x1,y1,x2,y2) = self.bbox
35         
36        x1 = max(-180., min(180., x1))
37        x2 = max(-180., min(180., x2))
38        y1 = max(-90., min(90., y1))
39        y2 = max(-90., min(90., y2))
40
41        return (x1,y1,x2,y2)
42    crs84 = property(_getCRS84)
43
44    def getOffset(self):
45        """
46        @return: the offset of the bottom left coordinate of the CRS:84 bounding box from the raw bbox
47        """
48        (x1,y1,x2,y2) = self.bbox
49        (x3,y3,x4,y4) = self.crs84
50
51        return (x3-x1, y3-y1)
52       
53    def getCrs84OffsetInImage(self, width, height):
54        """
55        @param width: width of image in pixels
56        @param height: height of image in pixels
57        @return: the (x,y) offset of the CRS:84 bbox in the full bbox for an image of given dimensions.
58            Note this the offset from the top-left of the image.
59        """
60        (x1,y1,x2,y2) = self.bbox
61        (x3,y3,x4,y4) = self.crs84
62        bwidth = x2 - x1
63        bheight = y2 - y1
64
65        # The upper left corner relative to full bbox
66        ulx, uly = x3-x1, y2-y4
67
68        # Scale to image size
69        return (int(round(ulx / bwidth * width)), int(round(uly / bheight * height)))
70
71    def getCrs84ImageSize(self, width, height):
72        """
73        @param width: width of the image covering the full bbox
74        @param height: height of the image covering the full bbox
75        @return: (width, height) in pixels
76        """
77        (x1,y1,x2,y2) = self.bbox
78        (x3,y3,x4,y4) = self.crs84
79
80        xscale = width / (x2 - x1)
81        yscale = height / (y2 - y1)
82
83        return (int(round((x4-x3) * xscale)), int(round((y4-y3) * yscale)))
84
85    def _getCrs84Width(self):
86        (x1,y1,x2,y2) = self.crs84
87        return x2-x1
88    crs84Width = property(_getCrs84Width)
89
90    def _getCrs84Height(self):
91        (x1,y1,x2,y2) = self.crs84
92        return y2-y1
93    crs84Height = property(_getCrs84Height)
94
95
96def render_variable(var, bbox, width, height, cmap, varmin, varmax):
97    """
98    Creates RGBA PIL images with a selectable matplotlib colour scale.
99
100    """
101
102
103    # Check var is a single lat/lon slice
104    for axis in var.getAxisList():
105        if not (axis.isLatitude() or axis.isLongitude()):
106            raise ValueError('Variable must be a single lat/lon slice')
107
108    # If var isn't defined on a uniform grid regrid
109    if not is_uniform_grid(var):
110        var = var_to_uniform_grid(var)
111
112    # Get grid spacing
113    lat0, lat1 = var.getLatitude()[:2]; dlat = lat1 - lat0
114    lon0, lon1 = var.getLongitude()[:2]; dlon = lon1 - lon0
115
116    def render(var, width, height):
117        # Normalise variable to varmin, varmax
118        norm = colors.normalize(varmin, varmax)
119        a = norm(var.getValue())
120
121        # Render the normalised variable by converting it into a byte array then to a PIL image
122        img_buf = (cmap(a) * 255).astype('b')
123        (y, x, c) = img_buf.shape
124        img = Image.frombuffer("RGBA", (x, y), img_buf.tostring(), "raw", "RGBA", 0. 1)
125
126        # Ensure lat & lon increase from the bottom left
127        if dlat > 0:
128            img = img.transpose(Image.FLIP_TOP_BOTTOM)
129        if dlon < 0:
130            img = img.transpose(Image.FLIT_LEFT_RIGHT)
131
132        return img
133
134
135    # Use BBox object for some of the bbox arithmatic
136    bbox_obj = BBox(bbox)
137
138    # If a non-CRS:84 bbox is requested we must retrieve the valid
139    # sub-bbox and paste it into an image of the correct size.
140    # It is assumed that grid can wrap around in the longitude and therefore only
141    # needs adjusting in the latitude.
142    if bbox != bboxObj.crs84:
143        img = Image.new('RGBA', (width, height))
144        (ox, oy) = bboxObj.getCrs84OffsetInImage(width, height)
145        nwidth, nheight = bboxObj.getCrs84ImageSize(width, height)
146        # If the image is too small just send a blank image
147        if bboxObj.crs84Width > abs(dlon) and bboxObj.crs84Height > abs(dlat):
148            img1 = render(var, width, nheight)
149            img.paste(img1, (0, oy))
150    else:
151        img = render(var, width, height)
152
153    return img
154
155
Note: See TracBrowser for help on using the repository browser.