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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1535 - (show annotations)
Fri Aug 1 14:27:13 2003 UTC (21 years, 7 months ago) by bh
File MIME type: text/x-python
File size: 14678 byte(s)
* Thuban/Model/data.py (SHAPETYPE_POLYGON, SHAPETYPE_ARC)
(SHAPETYPE_POINT, Shape): Move these constants and classes from
layer.py to data.py
(ShapefileStore.__init__): More Initialization for the new methods
and functionality.
(ShapefileStore.ShapeType, ShapefileStore.NumShapes)
(ShapefileStore.BoundingBox, ShapefileStore.ShapesInRegion)
(ShapefileStore.Shape): New methods that were formerly implemented
in the layer.
(DerivedShapeStore.Shape, DerivedShapeStore.ShapesInRegion)
(DerivedShapeStore.ShapeType, DerivedShapeStore.NumShapes)
(DerivedShapeStore.BoundingBox): New. DerivedShapeStore
equivalents of the new shape methods. These versions are simply
delegated to the original shapstore.

* Thuban/Model/layer.py (SHAPETYPE_POLYGON, SHAPETYPE_ARC)
(SHAPETYPE_POINT, Shape): Removed. They're now in data.py
(Layer.SetShapeStore): Removed the initializatin of instance
variables that were needed for the stuff that's now in
ShapefileStore
(Layer.BoundingBox, Layer.NumShapes, Layer.ShapeType)
(Layer.Shape, Layer.ShapesInRegion): Simply delegate to the
shapestore.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26