/[thuban]/trunk/thuban/Thuban/Model/layer.py
ViewVC logotype

Annotation of /trunk/thuban/Thuban/Model/layer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1978 - (hide annotations)
Tue Nov 25 14:30:34 2003 UTC (21 years, 3 months ago) by bh
File MIME type: text/x-python
File size: 14086 byte(s)
(Layer.__getattr__): Removed. It was only
there for backwards compatibility and all code relying on that
should have been updated by now.

1 bh 701 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 bh 6 # Authors:
3     # Bernhard Herzog <[email protected]>
4 jonathan 412 # Jonathan Coles <[email protected]>
5 bh 6 #
6     # This program is free software under the GPL (>=v2)
7     # Read the file COPYING coming with Thuban for details.
8    
9     __version__ = "$Revision$"
10    
11 bh 1599 import os
12 bh 1219 import warnings
13 bh 171
14 jan 374 from Thuban import _
15 bh 6
16 bh 701 from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \
17 jonathan 1427 LAYER_CHANGED, LAYER_SHAPESTORE_REPLACED, CLASS_CHANGED
18 bh 6
19 jonathan 437 import classification
20 bh 6
21 jonathan 1338 from color import Transparent, Black
22 bh 6 from base import TitledObject, Modifiable
23 bh 1558 from data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
24 bh 6
25 jonathan 1158 import resource
26 bh 723
27 jonathan 1158
28 bh 6 shapetype_names = {SHAPETYPE_POINT: "Point",
29     SHAPETYPE_ARC: "Arc",
30     SHAPETYPE_POLYGON: "Polygon"}
31    
32     class BaseLayer(TitledObject, Modifiable):
33    
34     """Base class for the layers."""
35    
36 jonathan 929 def __init__(self, title, visible = True, projection = None):
37 bh 6 """Initialize the layer.
38    
39     title -- the title
40     visible -- boolean. If true the layer is visible.
41     """
42     TitledObject.__init__(self, title)
43     Modifiable.__init__(self)
44     self.visible = visible
45 jonathan 929 self.projection = projection
46 bh 6
47     def Visible(self):
48     """Return true if layer is visible"""
49     return self.visible
50    
51     def SetVisible(self, visible):
52     """Set the layer's visibility."""
53     self.visible = visible
54     self.issue(LAYER_VISIBILITY_CHANGED, self)
55 bh 276
56 jonathan 929 def HasClassification(self):
57     """Determine if this layer support classifications."""
58     return False
59 bh 276
60 jonathan 1273 def HasShapes(self):
61     """Determine if this layer supports shapes."""
62     return False
63    
64 jonathan 929 def GetProjection(self):
65     """Return the layer's projection."""
66     return self.projection
67    
68     def SetProjection(self, projection):
69     """Set the layer's projection"""
70     self.projection = projection
71     self.changed(LAYER_PROJECTION_CHANGED, self)
72    
73 bh 6 class Layer(BaseLayer):
74    
75     """Represent the information of one geodata file (currently a shapefile)
76    
77     All children of the layer have the same type.
78    
79     A layer has fill and stroke colors. Colors should be instances of
80 jonathan 1338 Color. They can also be Transparent, indicating no fill or no stroke.
81 bh 6
82     The layer objects send the following events, all of which have the
83     layer object as parameter:
84    
85     TITLE_CHANGED -- The title has changed.
86     LAYER_PROJECTION_CHANGED -- the projection has changed.
87     """
88    
89 bh 723 def __init__(self, title, data, projection = None,
90 jonathan 1338 fill = Transparent,
91     stroke = Black,
92 jonathan 464 lineWidth = 1,
93 jonathan 771 visible = True):
94 bh 6 """Initialize the layer.
95    
96     title -- the title
97 bh 723 data -- datastore object for the shape data shown by the layer
98 bh 6 projection -- the projection object. Its Inverse method is
99     assumed to map the layer's coordinates to lat/long
100     coordinates
101 jonathan 1338 fill -- the fill color or Transparent if the shapes are
102 jonathan 610 not filled
103 jonathan 1338 stroke -- the stroke color or Transparent if the shapes
104 jonathan 610 are not stroked
105 bh 6 visible -- boolean. If true the layer is visible.
106    
107     colors are expected to be instances of Color class
108     """
109 jonathan 929 BaseLayer.__init__(self, title,
110     visible = visible,
111     projection = projection)
112 bh 276
113 bh 723 self.__classification = None
114 jonathan 1427 self.store = None
115 jonathan 481
116 bh 723 self.SetShapeStore(data)
117 jonathan 492
118 bh 1452 self.classification_column = None
119     self.SetClassificationColumn(None)
120 jonathan 492 self.SetClassification(None)
121    
122 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
123     self.__classification.SetDefaultLineWidth(lineWidth)
124 jonathan 412 self.__classification.SetDefaultFill(fill)
125 jonathan 364
126 jonathan 389 self.UnsetModified()
127    
128 bh 723 def SetShapeStore(self, store):
129 jonathan 1427 # Set the classification to None if there is a classification
130     # and the new shapestore doesn't have a table with a suitable
131     # column, i.e one with the same name and type as before
132     # FIXME: Maybe we should keep it the same if the type is
133     # compatible enough such as FIELDTYPE_DOUBLE and FIELDTYPE_INT
134     if self.__classification is not None:
135 bh 1452 columnname = self.classification_column
136     columntype = self.GetFieldType(columnname)
137 jonathan 1427 table = store.Table()
138 bh 1452 if (columnname is not None
139     and (not table.HasColumn(columnname)
140     or table.Column(columnname).type != columntype)):
141 jonathan 1427 self.SetClassification(None)
142    
143 bh 723 self.store = store
144 bh 179
145 bh 1142 self.changed(LAYER_SHAPESTORE_REPLACED, self)
146 bh 723
147     def ShapeStore(self):
148     return self.store
149    
150 bh 258 def Destroy(self):
151 bh 260 BaseLayer.Destroy(self)
152 jonathan 1427 if self.__classification is not None:
153 bh 1452 self.__classification.Unsubscribe(CLASS_CHANGED,
154     self._classification_changed)
155 bh 258
156 bh 6 def BoundingBox(self):
157 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
158    
159     If the layer has no shapes, return None.
160     """
161 bh 1535 return self.store.BoundingBox()
162 bh 6
163     def LatLongBoundingBox(self):
164 bh 179 """Return the layer's bounding box in lat/long coordinates.
165 bh 6
166 bh 179 Return None, if the layer doesn't contain any shapes.
167     """
168     bbox = self.BoundingBox()
169     if bbox is not None:
170     llx, lly, urx, ury = bbox
171     if self.projection is not None:
172     llx, lly = self.projection.Inverse(llx, lly)
173     urx, ury = self.projection.Inverse(urx, ury)
174     return llx, lly, urx, ury
175     else:
176     return None
177    
178 jonathan 828 def ShapesBoundingBox(self, shapes):
179     """Return a bounding box in lat/long coordinates for the given
180     list of shape ids.
181    
182     If shapes is None or empty, return None.
183     """
184    
185     if shapes is None or len(shapes) == 0: return None
186    
187     llx = []
188     lly = []
189     urx = []
190     ury = []
191    
192     if self.projection is not None:
193     inverse = lambda x, y: self.projection.Inverse(x, y)
194     else:
195     inverse = lambda x, y: (x, y)
196    
197     for id in shapes:
198     left, bottom, right, top = self.Shape(id).compute_bbox()
199    
200     left, bottom = inverse(left, bottom)
201     right, top = inverse(right, top)
202    
203     llx.append(left)
204     lly.append(bottom)
205     urx.append(right)
206     ury.append(top)
207    
208     return (min(llx), min(lly), max(urx), max(ury))
209    
210 jonathan 464 def GetFieldType(self, fieldName):
211 jonathan 1427 if self.store:
212     table = self.store.Table()
213     if table.HasColumn(fieldName):
214     return table.Column(fieldName).type
215 bh 839 return None
216 jonathan 464
217 jonathan 1273 def HasShapes(self):
218     return True
219    
220 bh 6 def NumShapes(self):
221     """Return the number of shapes in the layer"""
222 bh 1535 return self.store.NumShapes()
223 bh 6
224     def ShapeType(self):
225     """Return the type of the shapes in the layer.
226 bh 1535
227     The return value is one of the SHAPETYPE_* constants defined in
228     Thuban.Model.data.
229 bh 6 """
230 bh 1535 return self.store.ShapeType()
231 bh 6
232     def Shape(self, index):
233     """Return the shape with index index"""
234 bh 1535 return self.store.Shape(index)
235 jonathan 364
236 bh 1587 def ShapesInRegion(self, bbox):
237 bh 1593 """Return an iterable over the shapes that overlap the bounding box.
238 bh 143
239 bh 1593 The bbox parameter should be the bounding box as a tuple in the
240     form (minx, miny, maxx, maxy) in unprojected coordinates.
241 bh 143 """
242 jonathan 794 if self.projection is not None:
243 bh 1587 left, bottom, right, top = bbox
244     xs = []; ys = []
245     for x, y in [(left, bottom), (left, top), (right, top),
246     (right, bottom)]:
247     x, y = self.projection.Forward(x, y)
248     xs.append(x)
249     ys.append(y)
250     bbox = (min(xs), min(ys), max(xs), max(ys))
251 jonathan 794
252 bh 1587 return self.store.ShapesInRegion(bbox)
253 bh 143
254 bh 1452 def GetClassificationColumn(self):
255     return self.classification_column
256 jonathan 1427
257 bh 1452 def SetClassificationColumn(self, column):
258     """Set the column to classifiy on, or None. If column is not None
259     and the column does not exist in the table, raise a ValueError.
260 jonathan 1427 """
261 bh 1452 if column:
262     columnType = self.GetFieldType(column)
263     if columnType is None:
264 jonathan 1427 raise ValueError()
265 bh 1452 changed = self.classification_column != column
266     self.classification_column = column
267     if changed:
268     self.changed(LAYER_CHANGED, self)
269 jonathan 1427
270 jonathan 929 def HasClassification(self):
271     return True
272 jonathan 725
273 jonathan 412 def GetClassification(self):
274     return self.__classification
275    
276     def SetClassification(self, clazz):
277 jonathan 1338 """Set the classification used by this layer to 'clazz'
278 jonathan 481
279 jonathan 1338 If 'clazz' is None a default classification is created.
280    
281 jonathan 1427 This issues a LAYER_CHANGED event.
282 jonathan 492 """
283 jonathan 481
284 jonathan 1427 if self.__classification is not None:
285 bh 1452 self.__classification.Unsubscribe(CLASS_CHANGED,
286     self._classification_changed)
287 jonathan 529
288 jonathan 1338 if clazz is None:
289     clazz = classification.Classification()
290 jonathan 529
291 jonathan 1427 self.__classification = clazz
292 bh 1452 self.__classification.Subscribe(CLASS_CHANGED,
293     self._classification_changed)
294 jonathan 1338
295 bh 1452 self._classification_changed()
296 jonathan 492
297 bh 1452 def _classification_changed(self):
298 jonathan 492 """Called from the classification object when it has changed."""
299 jonathan 558 self.changed(LAYER_CHANGED, self)
300 jonathan 492
301 bh 217 def TreeInfo(self):
302     items = []
303    
304 jonathan 1338 items.append(_("Filename: %s") % self.ShapeStore().FileName())
305 jan 1012
306 bh 217 if self.Visible():
307 jan 374 items.append(_("Shown"))
308 bh 217 else:
309 jan 374 items.append(_("Hidden"))
310     items.append(_("Shapes: %d") % self.NumShapes())
311 bh 217
312     bbox = self.LatLongBoundingBox()
313     if bbox is not None:
314 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
315 bh 217 else:
316 jan 374 items.append(_("Extent (lat-lon):"))
317     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
318 bh 217
319 jonathan 736 if self.projection and len(self.projection.params) > 0:
320     items.append((_("Projection"),
321     [str(param) for param in self.projection.params]))
322    
323 jonathan 412 items.append(self.__classification)
324 bh 217
325 jan 374 return (_("Layer '%s'") % self.Title(), items)
326 jonathan 382
327 jonathan 736
328 jonathan 1158 if resource.has_gdal_support():
329     import gdal
330     from gdalconst import GA_ReadOnly
331    
332 jonathan 929 class RasterLayer(BaseLayer):
333    
334     def __init__(self, title, filename, projection = None, visible = True):
335     """Initialize the Raster Layer.
336    
337     title -- title for the layer.
338    
339     filename -- file name of the source image.
340    
341     projection -- Projection object describing the projection which
342     the source image is in.
343    
344     visible -- True is the layer should initially be visible.
345 jonathan 961
346     Throws IOError if the filename is invalid or points to a file that
347     is not in a format GDAL can use.
348 jonathan 929 """
349    
350     BaseLayer.__init__(self, title, visible = visible)
351    
352     self.projection = projection
353 bh 1599 self.filename = os.path.abspath(filename)
354 jonathan 929
355     self.bbox = -1
356    
357 jonathan 1158 if resource.has_gdal_support():
358     #
359     # temporarily open the file so that GDAL can test if it's valid.
360     #
361     dataset = gdal.Open(self.filename, GA_ReadOnly)
362 jonathan 961
363 jonathan 1158 if dataset is None:
364     raise IOError()
365 jonathan 961
366 jonathan 929 self.UnsetModified()
367    
368     def BoundingBox(self):
369     """Return the layer's bounding box in the intrinsic coordinate system.
370    
371 jonathan 1338 If the there is no support for images, or the file cannot
372     be read, or there is no geographics information available, return None.
373 jonathan 929 """
374 jonathan 1158 if not resource.has_gdal_support():
375     return None
376    
377 jonathan 929 if self.bbox == -1:
378     dataset = gdal.Open(self.filename, GA_ReadOnly)
379     if dataset is None:
380     self.bbox = None
381     else:
382 jonathan 961 geotransform = dataset.GetGeoTransform()
383     if geotransform is None:
384     return None
385    
386     x = 0
387     y = dataset.RasterYSize
388     left = geotransform[0] + \
389     geotransform[1] * x + \
390     geotransform[2] * y
391    
392     bottom = geotransform[3] + \
393     geotransform[4] * x + \
394     geotransform[5] * y
395    
396     x = dataset.RasterXSize
397     y = 0
398     right = geotransform[0] + \
399     geotransform[1] * x + \
400     geotransform[2] * y
401    
402     top = geotransform[3] + \
403     geotransform[4] * x + \
404     geotransform[5] * y
405    
406 jonathan 929 self.bbox = (left, bottom, right, top)
407    
408     return self.bbox
409    
410     def LatLongBoundingBox(self):
411     bbox = self.BoundingBox()
412 jonathan 961 if bbox is None:
413 jonathan 929 return None
414    
415 jonathan 961 llx, lly, urx, ury = bbox
416     if self.projection is not None:
417     llx, lly = self.projection.Inverse(llx, lly)
418     urx, ury = self.projection.Inverse(urx, ury)
419    
420     return llx, lly, urx, ury
421    
422 jonathan 929 def GetImageFilename(self):
423     return self.filename
424    
425     def TreeInfo(self):
426     items = []
427    
428 jonathan 1338 items.append(_("Filename: %s") % self.GetImageFilename())
429    
430 jonathan 929 if self.Visible():
431     items.append(_("Shown"))
432     else:
433     items.append(_("Hidden"))
434    
435     bbox = self.LatLongBoundingBox()
436     if bbox is not None:
437     items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
438     else:
439     items.append(_("Extent (lat-lon):"))
440    
441     if self.projection and len(self.projection.params) > 0:
442     items.append((_("Projection"),
443     [str(param) for param in self.projection.params]))
444    
445     return (_("Layer '%s'") % self.Title(), items)
446    

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26