/[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 492 - (hide annotations)
Mon Mar 10 10:44:57 2003 UTC (21 years, 11 months ago) by jonathan
File MIME type: text/x-python
File size: 9698 byte(s)
 Added ClassChanged() so that the
        classification can tell the layer when its data has changed.
        (Layer.SetClassification): Accepts None as an arguement to
        remove the current classification and correctly handles
        adding a new classification.

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     LAYER_VISIBILITY_CHANGED
20    
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    
151 jonathan 492
152     self.SetClassification(None)
153    
154 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
155     self.__classification.SetDefaultLineWidth(lineWidth)
156 jonathan 412 self.__classification.SetDefaultFill(fill)
157 jonathan 492 self.__classification.SetLayer(self)
158 jonathan 364
159 jonathan 389 self.UnsetModified()
160    
161 bh 6 def open_shapefile(self):
162     if self.shapefile is None:
163     self.shapefile = shapelib.ShapeFile(self.filename)
164     numshapes, shapetype, mins, maxs = self.shapefile.info()
165     self.numshapes = numshapes
166     self.shapetype = shapelib_shapetypes[shapetype]
167    
168 bh 179 # if there are shapes, set the bbox accordinly. Otherwise
169     # set it to None.
170     if self.numshapes:
171     self.bbox = mins[:2] + maxs[:2]
172     else:
173     self.bbox = None
174    
175 bh 171 # estimate a good depth for the quad tree. Each depth
176     # multiplies the number of nodes by four, therefore we
177     # basically take the base 4 logarithm of the number of
178     # shapes.
179     if self.numshapes < 4:
180     maxdepth = 1
181     else:
182     maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
183    
184     self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
185     maxdepth)
186    
187 bh 258 def Destroy(self):
188 bh 260 BaseLayer.Destroy(self)
189 bh 258 if self.shapefile is not None:
190     self.shapefile.close()
191     self.shapefile = None
192     self.shapetree = None
193     self.table.Destroy()
194    
195 bh 6 def BoundingBox(self):
196 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
197    
198     If the layer has no shapes, return None.
199     """
200     # The bbox will be set by open_shapefile just as we need it
201     # here.
202 bh 6 self.open_shapefile()
203     return self.bbox
204    
205     def LatLongBoundingBox(self):
206 bh 179 """Return the layer's bounding box in lat/long coordinates.
207 bh 6
208 bh 179 Return None, if the layer doesn't contain any shapes.
209     """
210     bbox = self.BoundingBox()
211     if bbox is not None:
212     llx, lly, urx, ury = bbox
213     if self.projection is not None:
214     llx, lly = self.projection.Inverse(llx, lly)
215     urx, ury = self.projection.Inverse(urx, ury)
216     return llx, lly, urx, ury
217     else:
218     return None
219    
220 jonathan 464 def GetFieldType(self, fieldName):
221     self.open_shapefile()
222     info = self.table.field_info_by_name(fieldName)
223     if info is not None:
224     return info[0]
225     else:
226     return None
227    
228 bh 6 def NumShapes(self):
229     """Return the number of shapes in the layer"""
230     self.open_shapefile()
231     return self.numshapes
232    
233     def ShapeType(self):
234     """Return the type of the shapes in the layer.
235     This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
236     """
237     self.open_shapefile()
238     return self.shapetype
239    
240     def Shape(self, index):
241     """Return the shape with index index"""
242     self.open_shapefile()
243     shape = self.shapefile.read_object(index)
244 jonathan 364
245 bh 6 if self.shapetype == SHAPETYPE_POINT:
246     points = shape.vertices()
247     else:
248     #for poly in shape.vertices():
249     poly = shape.vertices()[0]
250     points = []
251     for x, y in poly:
252 bh 82 points.append((x, y))
253 jonathan 364
254 bh 6 return Shape(points)
255    
256 bh 143 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 the coordinate
260     system used by the layer's shapefile.
261     """
262     left, bottom, right, top = box
263 bh 147 return self.shapetree.find_shapes((left, bottom), (right, top))
264 bh 143
265 bh 6 def SetProjection(self, projection):
266     """Set the layer's projection"""
267     self.projection = projection
268     self.changed(LAYER_PROJECTION_CHANGED, self)
269    
270 jonathan 412 def GetClassification(self):
271     return self.__classification
272    
273     def SetClassification(self, clazz):
274 jonathan 492 """Set the classification to 'clazz'
275 jonathan 481
276 jonathan 492 If 'clazz' is None a default classification is created
277     """
278 jonathan 481
279 jonathan 492 if clazz is None:
280     self.__classification = classification.Classification()
281     else:
282    
283     # prevent infinite recursion when calling SetLayer()
284     if clazz == self.__classification:
285     return
286    
287     self.__classification = clazz
288     self.__classification.SetLayer(self)
289    
290 jonathan 412 self.changed(LAYER_LEGEND_CHANGED, self)
291    
292 jonathan 492 def ClassChanged(self):
293     """Called from the classification object when it has changed."""
294     self.changed(LAYER_LEGEND_CHANGED, self)
295    
296 bh 217 def TreeInfo(self):
297     items = []
298    
299     if self.Visible():
300 jan 374 items.append(_("Shown"))
301 bh 217 else:
302 jan 374 items.append(_("Hidden"))
303     items.append(_("Shapes: %d") % self.NumShapes())
304 bh 217
305     bbox = self.LatLongBoundingBox()
306     if bbox is not None:
307 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
308 bh 217 else:
309 jan 374 items.append(_("Extent (lat-lon):"))
310     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
311 bh 217
312 jonathan 412 items.append(self.__classification)
313 bh 217
314 jan 374 return (_("Layer '%s'") % self.Title(), items)
315 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