/[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 545 - (hide annotations)
Thu Mar 20 09:44:04 2003 UTC (21 years, 11 months ago) by jonathan
File MIME type: text/x-python
File size: 10047 byte(s)
(Layer.SetClassification): Generate a LAYER_CHANGED event instead of a
LAYER_LEGEND_CHANGED event.

1 bh 73 # Copyright (c) 2001, 2002 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 276 import os
12 bh 171 from math import log, ceil
13    
14 jan 374 from Thuban import _
15    
16 bh 143 import shapelib, shptree
17 bh 6
18     from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \
19 jonathan 545 LAYER_VISIBILITY_CHANGED, LAYER_CHANGED
20 bh 6
21     from color import Color
22    
23 jonathan 437 import classification
24 bh 6
25     from table import Table
26    
27     from base import TitledObject, Modifiable
28    
29     class Shape:
30    
31     """Represent one shape"""
32    
33     def __init__(self, points):
34     self.points = points
35     #self.compute_bbox()
36    
37     def compute_bbox(self):
38     xs = []
39     ys = []
40     for x, y in self.points:
41     xs.append(x)
42     ys.append(y)
43     self.llx = min(xs)
44     self.lly = min(ys)
45     self.urx = max(xs)
46     self.ury = max(ys)
47    
48     def Points(self):
49     return self.points
50    
51    
52    
53     # Shape type constants
54     SHAPETYPE_POLYGON = "polygon"
55     SHAPETYPE_ARC = "arc"
56     SHAPETYPE_POINT = "point"
57    
58     # mapping from shapelib shapetype constants to our constants
59     shapelib_shapetypes = {shapelib.SHPT_POLYGON: SHAPETYPE_POLYGON,
60     shapelib.SHPT_ARC: SHAPETYPE_ARC,
61     shapelib.SHPT_POINT: SHAPETYPE_POINT}
62    
63     shapetype_names = {SHAPETYPE_POINT: "Point",
64     SHAPETYPE_ARC: "Arc",
65     SHAPETYPE_POLYGON: "Polygon"}
66    
67     class BaseLayer(TitledObject, Modifiable):
68    
69     """Base class for the layers."""
70    
71     def __init__(self, title, visible = 1):
72     """Initialize the layer.
73    
74     title -- the title
75     visible -- boolean. If true the layer is visible.
76     """
77     TitledObject.__init__(self, title)
78     Modifiable.__init__(self)
79     self.visible = visible
80    
81     def Visible(self):
82     """Return true if layer is visible"""
83     return self.visible
84    
85     def SetVisible(self, visible):
86     """Set the layer's visibility."""
87     self.visible = visible
88     self.issue(LAYER_VISIBILITY_CHANGED, self)
89 bh 276
90    
91 bh 6 class Layer(BaseLayer):
92    
93     """Represent the information of one geodata file (currently a shapefile)
94    
95     All children of the layer have the same type.
96    
97     A layer has fill and stroke colors. Colors should be instances of
98     Color. They can also be None, indicating no fill or no stroke.
99    
100     The layer objects send the following events, all of which have the
101     layer object as parameter:
102    
103     TITLE_CHANGED -- The title has changed.
104     LAYER_PROJECTION_CHANGED -- the projection has changed.
105     LAYER_LEGEND_CHANGED -- the fill or stroke attributes have changed
106    
107     """
108    
109     def __init__(self, title, filename, projection = None,
110 jonathan 412 fill = Color.None,
111     stroke = Color.Black,
112 jonathan 464 lineWidth = 1,
113 jonathan 412 visible = 1):
114 bh 6 """Initialize the layer.
115    
116     title -- the title
117     filename -- the name of the shapefile
118     projection -- the projection object. Its Inverse method is
119     assumed to map the layer's coordinates to lat/long
120     coordinates
121 jonathan 464 fill -- the fill color or Color.None if the shapes are not filled
122     stroke -- the stroke color or Color.None if the shapes are not stroked
123 bh 6 visible -- boolean. If true the layer is visible.
124    
125     colors are expected to be instances of Color class
126     """
127     BaseLayer.__init__(self, title, visible = visible)
128 bh 276
129     # Make the filename absolute. The filename will be
130     # interpreted relative to that anyway, but when saving a
131     # session we need to compare absolute paths and it's usually
132     # safer to always work with absolute paths.
133     self.filename = os.path.abspath(filename)
134    
135 bh 6 self.projection = projection
136     self.shapefile = None
137 bh 143 self.shapetree = None
138 bh 21 self.open_shapefile()
139 bh 6 # shapetable is the table associated with the shapefile, while
140     # table is the default table used to look up attributes for
141     # display
142     self.shapetable = Table(filename)
143     self.table = self.shapetable
144    
145 jonathan 481 #
146     # this is really important so that when the classification class
147     # tries to set its parent layer the variable will exist
148     #
149     self.__classification = None
150 jonathan 529 self.__setClassLock = False
151 jonathan 481
152 jonathan 492
153     self.SetClassification(None)
154    
155 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
156     self.__classification.SetDefaultLineWidth(lineWidth)
157 jonathan 412 self.__classification.SetDefaultFill(fill)
158 jonathan 492 self.__classification.SetLayer(self)
159 jonathan 364
160 jonathan 389 self.UnsetModified()
161    
162 bh 6 def open_shapefile(self):
163     if self.shapefile is None:
164     self.shapefile = shapelib.ShapeFile(self.filename)
165     numshapes, shapetype, mins, maxs = self.shapefile.info()
166     self.numshapes = numshapes
167     self.shapetype = shapelib_shapetypes[shapetype]
168    
169 bh 179 # if there are shapes, set the bbox accordinly. Otherwise
170     # set it to None.
171     if self.numshapes:
172     self.bbox = mins[:2] + maxs[:2]
173     else:
174     self.bbox = None
175    
176 bh 171 # estimate a good depth for the quad tree. Each depth
177     # multiplies the number of nodes by four, therefore we
178     # basically take the base 4 logarithm of the number of
179     # shapes.
180     if self.numshapes < 4:
181     maxdepth = 1
182     else:
183     maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
184    
185     self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
186     maxdepth)
187    
188 bh 258 def Destroy(self):
189 bh 260 BaseLayer.Destroy(self)
190 bh 258 if self.shapefile is not None:
191     self.shapefile.close()
192     self.shapefile = None
193     self.shapetree = None
194 jonathan 529 self.SetClassification(None)
195 bh 258 self.table.Destroy()
196    
197 bh 6 def BoundingBox(self):
198 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
199    
200     If the layer has no shapes, return None.
201     """
202     # The bbox will be set by open_shapefile just as we need it
203     # here.
204 bh 6 self.open_shapefile()
205     return self.bbox
206    
207     def LatLongBoundingBox(self):
208 bh 179 """Return the layer's bounding box in lat/long coordinates.
209 bh 6
210 bh 179 Return None, if the layer doesn't contain any shapes.
211     """
212     bbox = self.BoundingBox()
213     if bbox is not None:
214     llx, lly, urx, ury = bbox
215     if self.projection is not None:
216     llx, lly = self.projection.Inverse(llx, lly)
217     urx, ury = self.projection.Inverse(urx, ury)
218     return llx, lly, urx, ury
219     else:
220     return None
221    
222 jonathan 464 def GetFieldType(self, fieldName):
223     self.open_shapefile()
224     info = self.table.field_info_by_name(fieldName)
225     if info is not None:
226     return info[0]
227     else:
228     return None
229    
230 bh 6 def NumShapes(self):
231     """Return the number of shapes in the layer"""
232     self.open_shapefile()
233     return self.numshapes
234    
235     def ShapeType(self):
236     """Return the type of the shapes in the layer.
237     This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
238     """
239     self.open_shapefile()
240     return self.shapetype
241    
242     def Shape(self, index):
243     """Return the shape with index index"""
244     self.open_shapefile()
245     shape = self.shapefile.read_object(index)
246 jonathan 364
247 bh 6 if self.shapetype == SHAPETYPE_POINT:
248     points = shape.vertices()
249     else:
250     #for poly in shape.vertices():
251     poly = shape.vertices()[0]
252     points = []
253     for x, y in poly:
254 bh 82 points.append((x, y))
255 jonathan 364
256 bh 6 return Shape(points)
257    
258 bh 143 def ShapesInRegion(self, box):
259     """Return the ids of the shapes that overlap the box.
260    
261     Box is a tuple (left, bottom, right, top) in the coordinate
262     system used by the layer's shapefile.
263     """
264     left, bottom, right, top = box
265 bh 147 return self.shapetree.find_shapes((left, bottom), (right, top))
266 bh 143
267 bh 6 def SetProjection(self, projection):
268     """Set the layer's projection"""
269     self.projection = projection
270     self.changed(LAYER_PROJECTION_CHANGED, self)
271    
272 jonathan 412 def GetClassification(self):
273     return self.__classification
274    
275     def SetClassification(self, clazz):
276 jonathan 492 """Set the classification to 'clazz'
277 jonathan 481
278 jonathan 492 If 'clazz' is None a default classification is created
279     """
280 jonathan 481
281 jonathan 529 # prevent infinite recursion when calling SetLayer()
282     if self.__setClassLock: return
283    
284     self.__setClassLock = True
285    
286 jonathan 492 if clazz is None:
287 jonathan 529 if self.__classification is not None:
288     self.__classification.SetLayer(None)
289 jonathan 492 self.__classification = classification.Classification()
290     else:
291     self.__classification = clazz
292 jonathan 529 try:
293     self.__classification.SetLayer(self)
294     except ValueError:
295     self.__setClassLock = False
296     raise ValueError
297 jonathan 492
298 jonathan 545 self.changed(LAYER_CHANGED, self)
299 jonathan 412
300 jonathan 529 self.__setClassLock = False
301    
302 jonathan 492 def ClassChanged(self):
303     """Called from the classification object when it has changed."""
304     self.changed(LAYER_LEGEND_CHANGED, self)
305    
306 bh 217 def TreeInfo(self):
307     items = []
308    
309     if self.Visible():
310 jan 374 items.append(_("Shown"))
311 bh 217 else:
312 jan 374 items.append(_("Hidden"))
313     items.append(_("Shapes: %d") % self.NumShapes())
314 bh 217
315     bbox = self.LatLongBoundingBox()
316     if bbox is not None:
317 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
318 bh 217 else:
319 jan 374 items.append(_("Extent (lat-lon):"))
320     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
321 bh 217
322 jonathan 412 items.append(self.__classification)
323 bh 217
324 jan 374 return (_("Layer '%s'") % self.Title(), items)
325 jonathan 382

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26