/[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 1086 - (hide annotations)
Wed May 28 11:36:05 2003 UTC (21 years, 9 months ago) by jan
File MIME type: text/x-python
File size: 14560 byte(s)
(Layer.TreeInfo): Fixed a bug (a layer does not necessarily have a filename).

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 171 from math import log, ceil
12    
13 jan 374 from Thuban import _
14    
15 bh 143 import shapelib, shptree
16 bh 6
17 jonathan 929 import gdal
18     from gdalconst import GA_ReadOnly
19    
20 bh 701 from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \
21     LAYER_CHANGED
22 bh 6
23     from color import Color
24    
25 jonathan 437 import classification
26 bh 6
27     from base import TitledObject, Modifiable
28    
29 bh 723
30 bh 6 class Shape:
31    
32     """Represent one shape"""
33    
34     def __init__(self, points):
35     self.points = points
36     #self.compute_bbox()
37 jonathan 828 self.bbox = None
38 bh 6
39     def compute_bbox(self):
40 jonathan 828 if self.bbox is not None:
41     return self.bbox
42    
43 bh 6 xs = []
44     ys = []
45     for x, y in self.points:
46     xs.append(x)
47     ys.append(y)
48     self.llx = min(xs)
49     self.lly = min(ys)
50     self.urx = max(xs)
51     self.ury = max(ys)
52    
53 jonathan 828 self.bbox = (self.llx, self.lly, self.urx, self.ury)
54    
55     return self.bbox
56    
57 bh 6 def Points(self):
58     return self.points
59    
60    
61    
62     # Shape type constants
63     SHAPETYPE_POLYGON = "polygon"
64     SHAPETYPE_ARC = "arc"
65     SHAPETYPE_POINT = "point"
66    
67     # mapping from shapelib shapetype constants to our constants
68     shapelib_shapetypes = {shapelib.SHPT_POLYGON: SHAPETYPE_POLYGON,
69     shapelib.SHPT_ARC: SHAPETYPE_ARC,
70     shapelib.SHPT_POINT: SHAPETYPE_POINT}
71    
72     shapetype_names = {SHAPETYPE_POINT: "Point",
73     SHAPETYPE_ARC: "Arc",
74     SHAPETYPE_POLYGON: "Polygon"}
75    
76     class BaseLayer(TitledObject, Modifiable):
77    
78     """Base class for the layers."""
79    
80 jonathan 929 def __init__(self, title, visible = True, projection = None):
81 bh 6 """Initialize the layer.
82    
83     title -- the title
84     visible -- boolean. If true the layer is visible.
85     """
86     TitledObject.__init__(self, title)
87     Modifiable.__init__(self)
88     self.visible = visible
89 jonathan 929 self.projection = projection
90 bh 6
91     def Visible(self):
92     """Return true if layer is visible"""
93     return self.visible
94    
95     def SetVisible(self, visible):
96     """Set the layer's visibility."""
97     self.visible = visible
98     self.issue(LAYER_VISIBILITY_CHANGED, self)
99 bh 276
100 jonathan 929 def HasClassification(self):
101     """Determine if this layer support classifications."""
102     return False
103 bh 276
104 jonathan 929 def GetProjection(self):
105     """Return the layer's projection."""
106     return self.projection
107    
108     def SetProjection(self, projection):
109     """Set the layer's projection"""
110     self.projection = projection
111     self.changed(LAYER_PROJECTION_CHANGED, self)
112    
113 bh 6 class Layer(BaseLayer):
114    
115     """Represent the information of one geodata file (currently a shapefile)
116    
117     All children of the layer have the same type.
118    
119     A layer has fill and stroke colors. Colors should be instances of
120     Color. They can also be None, indicating no fill or no stroke.
121    
122     The layer objects send the following events, all of which have the
123     layer object as parameter:
124    
125     TITLE_CHANGED -- The title has changed.
126     LAYER_PROJECTION_CHANGED -- the projection has changed.
127     """
128    
129 bh 723 def __init__(self, title, data, projection = None,
130 jonathan 610 fill = Color.Transparent,
131 jonathan 412 stroke = Color.Black,
132 jonathan 464 lineWidth = 1,
133 jonathan 771 visible = True):
134 bh 6 """Initialize the layer.
135    
136     title -- the title
137 bh 723 data -- datastore object for the shape data shown by the layer
138 bh 6 projection -- the projection object. Its Inverse method is
139     assumed to map the layer's coordinates to lat/long
140     coordinates
141 jonathan 610 fill -- the fill color or Color.Transparent if the shapes are
142     not filled
143     stroke -- the stroke color or Color.Transparent if the shapes
144     are not stroked
145 bh 6 visible -- boolean. If true the layer is visible.
146    
147     colors are expected to be instances of Color class
148     """
149 jonathan 929 BaseLayer.__init__(self, title,
150     visible = visible,
151     projection = projection)
152 bh 276
153 jonathan 481 #
154     # this is really important so that when the classification class
155     # tries to set its parent layer the variable will exist
156     #
157 bh 723 self.__classification = None
158 jonathan 529 self.__setClassLock = False
159 jonathan 481
160 bh 723 self.SetShapeStore(data)
161 jonathan 492
162     self.SetClassification(None)
163    
164 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
165     self.__classification.SetDefaultLineWidth(lineWidth)
166 jonathan 412 self.__classification.SetDefaultFill(fill)
167 jonathan 492 self.__classification.SetLayer(self)
168 jonathan 364
169 jonathan 389 self.UnsetModified()
170    
171 bh 6
172 bh 723 def SetShapeStore(self, store):
173     self.store = store
174     self.shapefile = self.store.Shapefile()
175     self.shapetable = self.store.Table()
176 bh 996 if hasattr(self.store, "FileName"):
177     self.filename = self.store.FileName()
178 bh 723 self.table = self.shapetable
179 bh 179
180 bh 723 numshapes, shapetype, mins, maxs = self.shapefile.info()
181     self.numshapes = numshapes
182     self.shapetype = shapelib_shapetypes[shapetype]
183 bh 171
184 bh 723 # if there are shapes, set the bbox accordingly. Otherwise
185     # set it to None.
186     if self.numshapes:
187     self.bbox = mins[:2] + maxs[:2]
188     else:
189     self.bbox = None
190 bh 171
191 bh 723 # estimate a good depth for the quad tree. Each depth
192     # multiplies the number of nodes by four, therefore we
193     # basically take the base 4 logarithm of the number of
194     # shapes.
195     if self.numshapes < 4:
196     maxdepth = 1
197     else:
198     maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
199    
200     self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
201     maxdepth)
202     if self.__classification is not None:
203     fieldname = self.__classification.GetField()
204 jan 791 if fieldname is not None and \
205 bh 839 not self.store.Table().HasColumn(fieldname):
206 bh 723 self.SetClassification(None)
207     self.changed(LAYER_CHANGED, self)
208    
209     def ShapeStore(self):
210     return self.store
211    
212 bh 258 def Destroy(self):
213 bh 260 BaseLayer.Destroy(self)
214 jonathan 529 self.SetClassification(None)
215 bh 258
216 bh 6 def BoundingBox(self):
217 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
218    
219     If the layer has no shapes, return None.
220     """
221 bh 6 return self.bbox
222    
223     def LatLongBoundingBox(self):
224 bh 179 """Return the layer's bounding box in lat/long coordinates.
225 bh 6
226 bh 179 Return None, if the layer doesn't contain any shapes.
227     """
228     bbox = self.BoundingBox()
229     if bbox is not None:
230     llx, lly, urx, ury = bbox
231     if self.projection is not None:
232     llx, lly = self.projection.Inverse(llx, lly)
233     urx, ury = self.projection.Inverse(urx, ury)
234     return llx, lly, urx, ury
235     else:
236     return None
237    
238 jonathan 828 def ShapesBoundingBox(self, shapes):
239     """Return a bounding box in lat/long coordinates for the given
240     list of shape ids.
241    
242     If shapes is None or empty, return None.
243     """
244    
245     if shapes is None or len(shapes) == 0: return None
246    
247     llx = []
248     lly = []
249     urx = []
250     ury = []
251    
252     if self.projection is not None:
253     inverse = lambda x, y: self.projection.Inverse(x, y)
254     else:
255     inverse = lambda x, y: (x, y)
256    
257     for id in shapes:
258     left, bottom, right, top = self.Shape(id).compute_bbox()
259    
260     left, bottom = inverse(left, bottom)
261     right, top = inverse(right, top)
262    
263     llx.append(left)
264     lly.append(bottom)
265     urx.append(right)
266     ury.append(top)
267    
268     return (min(llx), min(lly), max(urx), max(ury))
269    
270 jonathan 464 def GetFieldType(self, fieldName):
271 bh 839 if self.table.HasColumn(fieldName):
272     return self.table.Column(fieldName).type
273     return None
274 jonathan 464
275 bh 6 def NumShapes(self):
276     """Return the number of shapes in the layer"""
277     return self.numshapes
278    
279     def ShapeType(self):
280     """Return the type of the shapes in the layer.
281     This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
282     """
283     return self.shapetype
284    
285     def Shape(self, index):
286     """Return the shape with index index"""
287     shape = self.shapefile.read_object(index)
288 jonathan 364
289 bh 6 if self.shapetype == SHAPETYPE_POINT:
290     points = shape.vertices()
291     else:
292     #for poly in shape.vertices():
293     poly = shape.vertices()[0]
294     points = []
295     for x, y in poly:
296 bh 82 points.append((x, y))
297 jonathan 364
298 bh 6 return Shape(points)
299    
300 bh 143 def ShapesInRegion(self, box):
301     """Return the ids of the shapes that overlap the box.
302    
303 jonathan 794 Box is a tuple (left, bottom, right, top) in unprojected coordinates.
304 bh 143 """
305     left, bottom, right, top = box
306 jonathan 794
307     if self.projection is not None:
308     left, bottom = self.projection.Forward(left, bottom)
309     right, top = self.projection.Forward(right, top)
310    
311 bh 147 return self.shapetree.find_shapes((left, bottom), (right, top))
312 bh 143
313 jonathan 929 def HasClassification(self):
314     return True
315 jonathan 725
316 jonathan 412 def GetClassification(self):
317     return self.__classification
318    
319     def SetClassification(self, clazz):
320 jonathan 492 """Set the classification to 'clazz'
321 jonathan 481
322 jonathan 492 If 'clazz' is None a default classification is created
323     """
324 jonathan 481
325 jonathan 529 # prevent infinite recursion when calling SetLayer()
326     if self.__setClassLock: return
327    
328     self.__setClassLock = True
329    
330 jonathan 492 if clazz is None:
331 jonathan 529 if self.__classification is not None:
332     self.__classification.SetLayer(None)
333 jonathan 492 self.__classification = classification.Classification()
334     else:
335     self.__classification = clazz
336 jonathan 529 try:
337     self.__classification.SetLayer(self)
338     except ValueError:
339     self.__setClassLock = False
340     raise ValueError
341 jonathan 492
342 jonathan 545 self.changed(LAYER_CHANGED, self)
343 jonathan 412
344 jonathan 529 self.__setClassLock = False
345    
346 jonathan 492 def ClassChanged(self):
347     """Called from the classification object when it has changed."""
348 jonathan 558 self.changed(LAYER_CHANGED, self)
349 jonathan 492
350 bh 217 def TreeInfo(self):
351     items = []
352    
353 jan 1086 if hasattr(self, 'filename'):
354     items.append(_("Filename: %s") % self.filename)
355 jan 1012
356 bh 217 if self.Visible():
357 jan 374 items.append(_("Shown"))
358 bh 217 else:
359 jan 374 items.append(_("Hidden"))
360     items.append(_("Shapes: %d") % self.NumShapes())
361 bh 217
362     bbox = self.LatLongBoundingBox()
363     if bbox is not None:
364 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
365 bh 217 else:
366 jan 374 items.append(_("Extent (lat-lon):"))
367     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
368 bh 217
369 jonathan 736 if self.projection and len(self.projection.params) > 0:
370     items.append((_("Projection"),
371     [str(param) for param in self.projection.params]))
372    
373 jonathan 412 items.append(self.__classification)
374 bh 217
375 jan 374 return (_("Layer '%s'") % self.Title(), items)
376 jonathan 382
377 jonathan 736
378 jonathan 929 class RasterLayer(BaseLayer):
379    
380     def __init__(self, title, filename, projection = None, visible = True):
381     """Initialize the Raster Layer.
382    
383     title -- title for the layer.
384    
385     filename -- file name of the source image.
386    
387     projection -- Projection object describing the projection which
388     the source image is in.
389    
390     visible -- True is the layer should initially be visible.
391 jonathan 961
392     Throws IOError if the filename is invalid or points to a file that
393     is not in a format GDAL can use.
394 jonathan 929 """
395    
396     BaseLayer.__init__(self, title, visible = visible)
397    
398     self.projection = projection
399     self.filename = filename
400    
401     self.bbox = -1
402    
403 jonathan 961 #
404     # temporarily open the file so that GDAL can test if it's valid.
405     #
406     dataset = gdal.Open(self.filename, GA_ReadOnly)
407    
408     if dataset is None:
409     raise IOError()
410    
411 jonathan 929 self.UnsetModified()
412    
413     def BoundingBox(self):
414     """Return the layer's bounding box in the intrinsic coordinate system.
415    
416     If the layer has no shapes, return None.
417     """
418     if self.bbox == -1:
419     dataset = gdal.Open(self.filename, GA_ReadOnly)
420     if dataset is None:
421     self.bbox = None
422     else:
423 jonathan 961 geotransform = dataset.GetGeoTransform()
424     if geotransform is None:
425     return None
426    
427     x = 0
428     y = dataset.RasterYSize
429     left = geotransform[0] + \
430     geotransform[1] * x + \
431     geotransform[2] * y
432    
433     bottom = geotransform[3] + \
434     geotransform[4] * x + \
435     geotransform[5] * y
436    
437     x = dataset.RasterXSize
438     y = 0
439     right = geotransform[0] + \
440     geotransform[1] * x + \
441     geotransform[2] * y
442    
443     top = geotransform[3] + \
444     geotransform[4] * x + \
445     geotransform[5] * y
446    
447 jonathan 929 self.bbox = (left, bottom, right, top)
448    
449     return self.bbox
450    
451     def LatLongBoundingBox(self):
452     bbox = self.BoundingBox()
453 jonathan 961 if bbox is None:
454 jonathan 929 return None
455    
456 jonathan 961 llx, lly, urx, ury = bbox
457     if self.projection is not None:
458     llx, lly = self.projection.Inverse(llx, lly)
459     urx, ury = self.projection.Inverse(urx, ury)
460    
461     return llx, lly, urx, ury
462    
463 jonathan 929 def GetImageFilename(self):
464     return self.filename
465    
466     def TreeInfo(self):
467     items = []
468    
469     if self.Visible():
470     items.append(_("Shown"))
471     else:
472     items.append(_("Hidden"))
473     items.append(_("Shapes: %d") % self.NumShapes())
474    
475     bbox = self.LatLongBoundingBox()
476     if bbox is not None:
477     items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
478     else:
479     items.append(_("Extent (lat-lon):"))
480    
481     if self.projection and len(self.projection.params) > 0:
482     items.append((_("Projection"),
483     [str(param) for param in self.projection.params]))
484    
485     return (_("Layer '%s'") % self.Title(), items)
486    

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26