root/PCL/trunk/PCL-Core/cartography/context/rendering.py

Revision 544, 9.2 KB (checked in by seang, 4 years ago)

merged changes from r464:543 of the minimal-dependencies branch, added a few missing module inits, a couple fixes to build and pass the tests

Line 
1# $Id: rendering.py 498 2006-10-08 05:48:49Z seang $
2
3# =============================================================================
4# Python Cartographic Library. Copyright (C) 2004 Sean C. Gillies
5#
6# This program is free software; you can redistribute it and/or modify it
7# under the terms of the GNU General Public License as published by the Free
8# Software Foundation; either version 2 of the License, or (at your option)
9# any later version.
10#
11# This program is distributed in the hope that it will be useful, but WITHOUT
12# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14# details.
15#
16# You should have received a copy of the GNU General Public License along with
17# this program; if not, write to the Free Software Foundation, Inc., 59 Temple
18# Place, Suite 330, Boston, MA 02111-1307 USA
19#
20# Contact email: sgillies@frii.com
21# =============================================================================
22
23from cStringIO import StringIO
24
25from cartography.styles import Style
26from cartography.proj import SpatialReference
27from cartography.geometry import Point
28
29METERS_PER_DD = 111319.490793
30
31class View:
32   
33    """Defines the reference system and bounding values of a map view.
34
35    Attributes
36    ----------
37    srs : SpatialReference
38        spatial reference system
39   
40    ul : Point
41        upper left point of bounding box
42   
43    lr : Point
44        lower right point of bounding box
45
46    units : string
47        'DD', 'METERS', or 'FEET'
48
49    """
50
51    def __init__(self, srs, bbox, units='METERS'):
52        """Initialize a View.
53       
54        Expects a SpatialReference, an upper left Point, a lower right Point,
55        and a units string.
56
57        Example
58        -------
59        >>> srs = SpatialReference(epsg=4326)
60        >>> view = View(srs, [-110.0, 38.0, -105.0, 42.0], 'DD')
61
62        """
63        self.srs = srs
64        self.ul = Point(bbox[0], bbox[3])
65        self.lr = Point(bbox[2], bbox[1])
66        self.units = units.upper()
67
68    def getaspectratio(self):
69        """Return spatial width to height ratio"""
70        return (self.lr.x - self.ul.x) / (self.ul.y - self.lr.y)
71
72    def getviewforscale(self, scale, size):
73        """Returns a new View object initialized at the given scale.
74       
75        Parameters
76        ----------
77        scale : float
78            Scale denominator.
79       
80        size : tuple
81            Two tuple containing the (width, height) screen size of the view.
82        """
83        assert scale > 0, "Scale must have value > 0."
84       
85        # map "screen" width in meters based on a standard 0.28 mm pixel
86        half_screen_width = size[0] * 0.00028 * 0.5
87        half_screen_height = size[1] * 0.00028 * 0.5
88       
89        # width on the ground in map units
90        ground_width = self.lr.x - self.ul.x
91        ground_height = self.ul.y - self.lr.y
92       
93        # Maintain the same center point with the current view
94        center_x = self.ul.x + (ground_width / 2.0)
95        center_y = self.lr.y + (ground_height / 2.0)
96       
97        if self.units == 'METERS':
98            dx = half_screen_width * scale
99            dy = half_screen_height * scale
100        elif self.units == 'DD':
101            dx = (half_screen_width * scale) / METERS_PER_DD
102            dy = (half_screen_height * scale) / METERS_PER_DD
103        elif self.units == 'FEET':
104            dx = half_screen_width * scale * 3.28
105            dy = half_screen_height * scale * 3.28
106        else:
107            raise Exception, "Cannot compute new view for %s units" % (self.units)
108
109        bounds = (center_x - dx, center_y - dy, center_x + dx, center_y + dy)
110        return View(self.srs, bounds, self.units)
111   
112    def getscaledenominator(self, size):
113        """Return denominator of the view scale
114
115        We assume a standard "screen" pixel size of 0.28 mm x 0.28 mm
116        and do not correct for latitude in the case of lat/long projections.
117       
118        Parameters
119        ----------
120        size : []
121            output image dimensions like [pixels, lines]
122        """
123        # map "screen" width in meters based on a standard 0.28 mm pixel
124        screen_width = size[0] * 0.00028
125       
126        # width on the ground in map units
127        ground_width = self.lr.x - self.ul.x
128       
129        if self.units == 'METERS':
130            return ground_width / screen_width
131        elif self.units == 'DD':
132            return METERS_PER_DD * ground_width / screen_width
133        elif self.units == 'FEET':
134            return ground_width / (3.28 * screen_width)
135        else:
136            raise Exception, "Cannot compute scale for %s units" % (self.units)
137
138    def contains(self, point):
139        """Return true if the point is within or on boundary, else false"""
140        if point.x <= self.lr.x and point.x >= self.ul.x \
141        and point.y <= self.ul.y and point.y >= self.lr.y:
142            return True
143        else: return False
144
145
146class Image:
147   
148    """Map imagery.
149   
150    Attributes
151    ----------
152    mimetype : string
153        'image/png', 'image/jpeg', 'image/gif'
154    data : StringIO
155        the image data.
156    size : []
157        image dimensions in pixels, [width, height]
158    """
159
160    def __init__(self, mimetype=''):
161        """Initialize an Image.  The mimetype should be like 'image/png'."""
162        self.mimetype = mimetype
163        self.data = StringIO()
164        self.size = []
165
166    def getdata(self):
167        """Return image data"""
168        return self.data.getvalue()
169
170
171class ContextLayer(object):
172    """A layer of a map context.
173    """
174
175    def __init__(self, name, layer, style=None, title=None, visible=True):
176        self.name = name
177        self.title = title or name
178        self.layer = layer
179        self.style = style or Style()
180        self._visible = bool(visible)
181
182    def _set_visibility(self, v):
183        self._visible = bool(v)
184        try:
185            self.layer.updateVisibility(v)
186        except AttributeError:
187            pass
188
189    def _get_visibility(self):
190        return self._visible
191
192    # Managed attribute
193    visible = property(_get_visibility, _set_visibility)
194   
195       
196class MapContext(object):
197    """A map context.
198    """
199
200    def __init__(self, layers=None):
201        """Initialize.
202
203        Parameters
204        ----------
205        layers : list
206            List of ContextLayers.
207        """
208        if layers is None:
209            self._layers = []
210        else:
211            self._layers = layers
212        self._names = [l.name for l in self._layers]
213       
214        #for layer, style in layering:
215        #    # Munge PCL styled layer into a MapServer-like tree
216        #    self.ltree.append(_munge_layers(layer, style))
217       
218    def __getitem__(self, n):
219        return self._layers[n]
220
221    def __setitem__(self, n, o):
222        if o.name in self._names and o.name != self._layers[n].name:
223            raise KeyError, "A context layer with name %s already exists" \
224                % o.name
225        self._layers[n] = o
226        self._names[n] = o.name
227
228    def __delitem__(self, n):
229        del self._layers[n]
230        del self._names[n]
231       
232    def __iter__(self):
233        return iter(self._layers)
234
235    def __len__(self):
236        return len(self._layers)
237
238    def index(self, name):
239        return self._names.index(name)
240
241    def insert(self, n, o):
242        if o.name in self._names:
243            raise KeyError, "A context layer with name %s already exists" \
244                % o.name
245        self._layers.insert(n, o)
246        self._names.insert(n, o.name)
247
248    def append(self, o):
249        if o.name in self._names:
250            raise KeyError, "A context layer with name %s already exists" \
251                % o.name
252        self._layers.append(o)
253        self._names.append(o.name)
254
255
256def MapRenderer(engine='mapserver', format='image', **kw):
257    """Factory for map renderers.
258
259    Calls the MapRender class of the specified engine module.
260   
261    Arguments
262    ---------
263    engine : string
264        Mapping engine module name, such as 'mapserver'.  Either case is
265        acceptable.
266    format : string
267        Output format such as 'image'.  Acceptable values depend on the
268        particular rendering engine.
269    **kw : mapping
270        keyword arguments passed to the specific engine's renderer.
271    """
272   
273    try:
274        #from cartography.engine.mapserver.rendering import ImageRenderer
275        #try:
276        #    import pkg_resources
277        #    pkg_resources.require("PCL-%s" % engine)
278        #except:
279        #    pass
280        name = 'cartography.engine.%s' % (engine.lower())
281        engine_module = __import__(name, globals(), locals(), ['rendering'])
282        # presently all we've implemented is an image renderer
283        return engine_module.rendering.ImageRenderer(**kw)
284    except ImportError:
285        raise
286        #raise Exception, "No such rendering engine: %s" % (engine.lower())
287    except AttributeError, e:
288        # a possible cause of this error is lack of mapscript/_mapscript
289        if 'mapscript' not in dir(engine_module.rendering):
290            raise Exception, \
291            "Python mapscript module could not be imported during load of mapping engine. Check that it has been properly installed on your system."
292        else:
293            raise AttributeError, (str(e), engine.lower(), engine_module.__file__, engine_module.rendering.__file__, engine_module.rendering.__dict__)
294
295
Note: See TracBrowser for help on using the browser.