/[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 736 - (show annotations)
Fri Apr 25 09:11:57 2003 UTC (21 years, 10 months ago) by jonathan
File MIME type: text/x-python
File size: 9588 byte(s)
(Layer.TreeInfo): Add an item to the tree for projection information.

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 = 1):
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 = 1):
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 not self.store.Table().field_info_by_name(fieldname):
180 self.SetClassification(None)
181 self.changed(LAYER_CHANGED, self)
182
183 def ShapeStore(self):
184 return self.store
185
186 def Destroy(self):
187 BaseLayer.Destroy(self)
188 self.SetClassification(None)
189
190 def BoundingBox(self):
191 """Return the layer's bounding box in the intrinsic coordinate system.
192
193 If the layer has no shapes, return None.
194 """
195 return self.bbox
196
197 def LatLongBoundingBox(self):
198 """Return the layer's bounding box in lat/long coordinates.
199
200 Return None, if the layer doesn't contain any shapes.
201 """
202 bbox = self.BoundingBox()
203 if bbox is not None:
204 llx, lly, urx, ury = bbox
205 if self.projection is not None:
206 llx, lly = self.projection.Inverse(llx, lly)
207 urx, ury = self.projection.Inverse(urx, ury)
208 return llx, lly, urx, ury
209 else:
210 return None
211
212 def GetFieldType(self, fieldName):
213 info = self.table.field_info_by_name(fieldName)
214 if info is not None:
215 return info[0]
216 else:
217 return None
218
219 def NumShapes(self):
220 """Return the number of shapes in the layer"""
221 return self.numshapes
222
223 def ShapeType(self):
224 """Return the type of the shapes in the layer.
225 This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
226 """
227 return self.shapetype
228
229 def Shape(self, index):
230 """Return the shape with index index"""
231 shape = self.shapefile.read_object(index)
232
233 if self.shapetype == SHAPETYPE_POINT:
234 points = shape.vertices()
235 else:
236 #for poly in shape.vertices():
237 poly = shape.vertices()[0]
238 points = []
239 for x, y in poly:
240 points.append((x, y))
241
242 return Shape(points)
243
244 def ShapesInRegion(self, box):
245 """Return the ids of the shapes that overlap the box.
246
247 Box is a tuple (left, bottom, right, top) in the coordinate
248 system used by the layer's shapefile.
249 """
250 left, bottom, right, top = box
251 return self.shapetree.find_shapes((left, bottom), (right, top))
252
253 def GetProjection(self):
254 return self.projection
255
256 def SetProjection(self, projection):
257 """Set the layer's projection"""
258 self.projection = projection
259 self.changed(LAYER_PROJECTION_CHANGED, self)
260
261 def GetClassification(self):
262 return self.__classification
263
264 def SetClassification(self, clazz):
265 """Set the classification to 'clazz'
266
267 If 'clazz' is None a default classification is created
268 """
269
270 # prevent infinite recursion when calling SetLayer()
271 if self.__setClassLock: return
272
273 self.__setClassLock = True
274
275 if clazz is None:
276 if self.__classification is not None:
277 self.__classification.SetLayer(None)
278 self.__classification = classification.Classification()
279 else:
280 self.__classification = clazz
281 try:
282 self.__classification.SetLayer(self)
283 except ValueError:
284 self.__setClassLock = False
285 raise ValueError
286
287 self.changed(LAYER_CHANGED, self)
288
289 self.__setClassLock = False
290
291 def ClassChanged(self):
292 """Called from the classification object when it has changed."""
293 self.changed(LAYER_CHANGED, self)
294
295 def TreeInfo(self):
296 items = []
297
298 if self.Visible():
299 items.append(_("Shown"))
300 else:
301 items.append(_("Hidden"))
302 items.append(_("Shapes: %d") % self.NumShapes())
303
304 bbox = self.LatLongBoundingBox()
305 if bbox is not None:
306 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
307 else:
308 items.append(_("Extent (lat-lon):"))
309 items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
310
311 if self.projection and len(self.projection.params) > 0:
312 items.append((_("Projection"),
313 [str(param) for param in self.projection.params]))
314
315 items.append(self.__classification)
316
317 return (_("Layer '%s'") % self.Title(), items)
318
319

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26