/[thuban]/trunk/thuban/Thuban/Model/layer.py
ViewVC logotype

Contents of /trunk/thuban/Thuban/Model/layer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 791 - (show annotations)
Wed Apr 30 12:26:33 2003 UTC (21 years, 10 months ago) by jan
File MIME type: text/x-python
File size: 9637 byte(s)
(Layer.SetShapeStore): Fixed a bug: don't set the Classification to
None if the classfication field is None (ie only a DEFAULT).

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26