/[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 828 - (hide annotations)
Tue May 6 12:06:12 2003 UTC (21 years, 10 months ago) by jonathan
File MIME type: text/x-python
File size: 10812 byte(s)
(Shape): Since a Shape is immutable only
        calculate the bounding box once (the first time compute_bbox() is
        called).
(Layer.ShapesBoundingBox): New. Given a list of shape ids, return
        the bounding box for the shapes in lat/long coordinates.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26