/[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 610 - (hide annotations)
Fri Apr 4 13:56:59 2003 UTC (21 years, 11 months ago) by jonathan
File MIME type: text/x-python
File size: 10095 byte(s)
Rename Color.None to Color.Transparent.

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 610 fill = Color.Transparent,
111 jonathan 412 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 610 fill -- the fill color or Color.Transparent if the shapes are
122     not filled
123     stroke -- the stroke color or Color.Transparent if the shapes
124     are not stroked
125 bh 6 visible -- boolean. If true the layer is visible.
126    
127     colors are expected to be instances of Color class
128     """
129     BaseLayer.__init__(self, title, visible = visible)
130 bh 276
131     # Make the filename absolute. The filename will be
132     # interpreted relative to that anyway, but when saving a
133     # session we need to compare absolute paths and it's usually
134     # safer to always work with absolute paths.
135     self.filename = os.path.abspath(filename)
136    
137 bh 6 self.projection = projection
138     self.shapefile = None
139 bh 143 self.shapetree = None
140 bh 21 self.open_shapefile()
141 bh 6 # shapetable is the table associated with the shapefile, while
142     # table is the default table used to look up attributes for
143     # display
144     self.shapetable = Table(filename)
145     self.table = self.shapetable
146    
147 jonathan 481 #
148     # this is really important so that when the classification class
149     # tries to set its parent layer the variable will exist
150     #
151     self.__classification = None
152 jonathan 529 self.__setClassLock = False
153 jonathan 481
154 jonathan 492
155     self.SetClassification(None)
156    
157 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
158     self.__classification.SetDefaultLineWidth(lineWidth)
159 jonathan 412 self.__classification.SetDefaultFill(fill)
160 jonathan 492 self.__classification.SetLayer(self)
161 jonathan 364
162 jonathan 389 self.UnsetModified()
163    
164 bh 6 def open_shapefile(self):
165     if self.shapefile is None:
166     self.shapefile = shapelib.ShapeFile(self.filename)
167     numshapes, shapetype, mins, maxs = self.shapefile.info()
168     self.numshapes = numshapes
169     self.shapetype = shapelib_shapetypes[shapetype]
170    
171 bh 179 # if there are shapes, set the bbox accordinly. Otherwise
172     # set it to None.
173     if self.numshapes:
174     self.bbox = mins[:2] + maxs[:2]
175     else:
176     self.bbox = None
177    
178 bh 171 # estimate a good depth for the quad tree. Each depth
179     # multiplies the number of nodes by four, therefore we
180     # basically take the base 4 logarithm of the number of
181     # shapes.
182     if self.numshapes < 4:
183     maxdepth = 1
184     else:
185     maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
186    
187     self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
188     maxdepth)
189    
190 bh 258 def Destroy(self):
191 bh 260 BaseLayer.Destroy(self)
192 bh 258 if self.shapefile is not None:
193     self.shapefile.close()
194     self.shapefile = None
195     self.shapetree = None
196 jonathan 529 self.SetClassification(None)
197 bh 258 self.table.Destroy()
198    
199 bh 6 def BoundingBox(self):
200 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
201    
202     If the layer has no shapes, return None.
203     """
204     # The bbox will be set by open_shapefile just as we need it
205     # here.
206 bh 6 self.open_shapefile()
207     return self.bbox
208    
209     def LatLongBoundingBox(self):
210 bh 179 """Return the layer's bounding box in lat/long coordinates.
211 bh 6
212 bh 179 Return None, if the layer doesn't contain any shapes.
213     """
214     bbox = self.BoundingBox()
215     if bbox is not None:
216     llx, lly, urx, ury = bbox
217     if self.projection is not None:
218     llx, lly = self.projection.Inverse(llx, lly)
219     urx, ury = self.projection.Inverse(urx, ury)
220     return llx, lly, urx, ury
221     else:
222     return None
223    
224 jonathan 464 def GetFieldType(self, fieldName):
225     self.open_shapefile()
226     info = self.table.field_info_by_name(fieldName)
227     if info is not None:
228     return info[0]
229     else:
230     return None
231    
232 bh 6 def NumShapes(self):
233     """Return the number of shapes in the layer"""
234     self.open_shapefile()
235     return self.numshapes
236    
237     def ShapeType(self):
238     """Return the type of the shapes in the layer.
239     This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
240     """
241     self.open_shapefile()
242     return self.shapetype
243    
244     def Shape(self, index):
245     """Return the shape with index index"""
246     self.open_shapefile()
247     shape = self.shapefile.read_object(index)
248 jonathan 364
249 bh 6 if self.shapetype == SHAPETYPE_POINT:
250     points = shape.vertices()
251     else:
252     #for poly in shape.vertices():
253     poly = shape.vertices()[0]
254     points = []
255     for x, y in poly:
256 bh 82 points.append((x, y))
257 jonathan 364
258 bh 6 return Shape(points)
259    
260 bh 143 def ShapesInRegion(self, box):
261     """Return the ids of the shapes that overlap the box.
262    
263     Box is a tuple (left, bottom, right, top) in the coordinate
264     system used by the layer's shapefile.
265     """
266     left, bottom, right, top = box
267 bh 147 return self.shapetree.find_shapes((left, bottom), (right, top))
268 bh 143
269 bh 6 def SetProjection(self, projection):
270     """Set the layer's projection"""
271     self.projection = projection
272     self.changed(LAYER_PROJECTION_CHANGED, self)
273    
274 jonathan 412 def GetClassification(self):
275     return self.__classification
276    
277     def SetClassification(self, clazz):
278 jonathan 492 """Set the classification to 'clazz'
279 jonathan 481
280 jonathan 492 If 'clazz' is None a default classification is created
281     """
282 jonathan 481
283 jonathan 529 # prevent infinite recursion when calling SetLayer()
284     if self.__setClassLock: return
285    
286     self.__setClassLock = True
287    
288 jonathan 492 if clazz is None:
289 jonathan 529 if self.__classification is not None:
290     self.__classification.SetLayer(None)
291 jonathan 492 self.__classification = classification.Classification()
292     else:
293     self.__classification = clazz
294 jonathan 529 try:
295     self.__classification.SetLayer(self)
296     except ValueError:
297     self.__setClassLock = False
298     raise ValueError
299 jonathan 492
300 jonathan 545 self.changed(LAYER_CHANGED, self)
301 jonathan 412
302 jonathan 529 self.__setClassLock = False
303    
304 jonathan 492 def ClassChanged(self):
305     """Called from the classification object when it has changed."""
306 jonathan 558 self.changed(LAYER_CHANGED, self)
307 jonathan 492
308 bh 217 def TreeInfo(self):
309     items = []
310    
311     if self.Visible():
312 jan 374 items.append(_("Shown"))
313 bh 217 else:
314 jan 374 items.append(_("Hidden"))
315     items.append(_("Shapes: %d") % self.NumShapes())
316 bh 217
317     bbox = self.LatLongBoundingBox()
318     if bbox is not None:
319 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
320 bh 217 else:
321 jan 374 items.append(_("Extent (lat-lon):"))
322     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
323 bh 217
324 jonathan 412 items.append(self.__classification)
325 bh 217
326 jan 374 return (_("Layer '%s'") % self.Title(), items)
327 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