/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/layer.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/Model/layer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 839 - (show annotations)
Tue May 6 15:54:18 2003 UTC (21 years, 10 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/layer.py
File MIME type: text/x-python
File size: 10770 byte(s)
Convert all table users to use the new table interface. This only
covers Thuban itself, not GREAT-ER or other applications built on
Thuban yet, so the compatibility interface stays in place for the
time being but it now issues DeprecationWarnings.

Finally, the new Table interface has a new method, HasColumn.

* Thuban/Model/table.py (OldTableInterfaceMixin): All methods
issue deprecation warnings when they're. The warnings refer to the
caller of the method.
(OldTableInterfaceMixin.__deprecation_warning): New. Helper method
for the deprecation warnings

* test/test_table.py: Ignore the deprecation warnings for the old
table in the tests in this module. The purpose of the tests is to
test the old interface, after all.

* test/test_transientdb.py
(TestTransientTable.run_iceland_political_tests): Use the
constants for the types. Add a test for HasColumn
(TestTransientTable.test_transient_joined_table): Adapt to new
table interface. Add a test for HasColumn
(TestTransientTable.test_transient_table_read_twice): Adapt to new
table interface

* Thuban/Model/transientdb.py (TransientTableBase.HasColumn)
(AutoTransientTable.HasColumn): Implement the new table interface
method
(AutoTransientTable.ReadRowAsDict, AutoTransientTable.ValueRange)
(AutoTransientTable.UniqueValues): Adapt to new table interface

* Thuban/Model/layer.py (Layer.SetShapeStore, Layer.GetFieldType):
Adapt to new table interface

* test/test_layer.py (TestLayer.open_shapefile): Helper method to
simplify opening shapefiles a bit easier.
(TestLayer.test_arc_layer, TestLayer.test_polygon_layer)
(TestLayer.test_point_layer): Use the new helper method
(TestLayer.test_get_field_type): New. Test for the GetFieldType
method

* test/test_dbf_table.py (TestDBFTable.test_has_column): Test for
the new table method

* test/test_memory_table.py (TestMemoryTable.test_has_column):
Test for the new table method HasColumn

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 self.bbox = None
35
36 def compute_bbox(self):
37 if self.bbox is not None:
38 return self.bbox
39
40 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 self.bbox = (self.llx, self.lly, self.urx, self.ury)
51
52 return self.bbox
53
54 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 def __init__(self, title, visible = True):
78 """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
96
97 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 def __init__(self, title, data, projection = None,
114 fill = Color.Transparent,
115 stroke = Color.Black,
116 lineWidth = 1,
117 visible = True):
118 """Initialize the layer.
119
120 title -- the title
121 data -- datastore object for the shape data shown by the layer
122 projection -- the projection object. Its Inverse method is
123 assumed to map the layer's coordinates to lat/long
124 coordinates
125 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 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
135 self.projection = projection
136
137 #
138 # this is really important so that when the classification class
139 # tries to set its parent layer the variable will exist
140 #
141 self.__classification = None
142 self.__setClassLock = False
143
144 self.SetShapeStore(data)
145
146 self.SetClassification(None)
147
148 self.__classification.SetDefaultLineColor(stroke)
149 self.__classification.SetDefaultLineWidth(lineWidth)
150 self.__classification.SetDefaultFill(fill)
151 self.__classification.SetLayer(self)
152
153 self.UnsetModified()
154
155
156 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
163 numshapes, shapetype, mins, maxs = self.shapefile.info()
164 self.numshapes = numshapes
165 self.shapetype = shapelib_shapetypes[shapetype]
166
167 # 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
174 # 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 if fieldname is not None and \
188 not self.store.Table().HasColumn(fieldname):
189 self.SetClassification(None)
190 self.changed(LAYER_CHANGED, self)
191
192 def ShapeStore(self):
193 return self.store
194
195 def Destroy(self):
196 BaseLayer.Destroy(self)
197 self.SetClassification(None)
198
199 def BoundingBox(self):
200 """Return the layer's bounding box in the intrinsic coordinate system.
201
202 If the layer has no shapes, return None.
203 """
204 return self.bbox
205
206 def LatLongBoundingBox(self):
207 """Return the layer's bounding box in lat/long coordinates.
208
209 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 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 def GetFieldType(self, fieldName):
254 if self.table.HasColumn(fieldName):
255 return self.table.Column(fieldName).type
256 return None
257
258 def NumShapes(self):
259 """Return the number of shapes in the layer"""
260 return self.numshapes
261
262 def ShapeType(self):
263 """Return the type of the shapes in the layer.
264 This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
265 """
266 return self.shapetype
267
268 def Shape(self, index):
269 """Return the shape with index index"""
270 shape = self.shapefile.read_object(index)
271
272 if self.shapetype == SHAPETYPE_POINT:
273 points = shape.vertices()
274 else:
275 #for poly in shape.vertices():
276 poly = shape.vertices()[0]
277 points = []
278 for x, y in poly:
279 points.append((x, y))
280
281 return Shape(points)
282
283 def ShapesInRegion(self, box):
284 """Return the ids of the shapes that overlap the box.
285
286 Box is a tuple (left, bottom, right, top) in unprojected coordinates.
287 """
288 left, bottom, right, top = box
289
290 if self.projection is not None:
291 left, bottom = self.projection.Forward(left, bottom)
292 right, top = self.projection.Forward(right, top)
293
294 return self.shapetree.find_shapes((left, bottom), (right, top))
295
296 def GetProjection(self):
297 return self.projection
298
299 def SetProjection(self, projection):
300 """Set the layer's projection"""
301 self.projection = projection
302 self.changed(LAYER_PROJECTION_CHANGED, self)
303
304 def GetClassification(self):
305 return self.__classification
306
307 def SetClassification(self, clazz):
308 """Set the classification to 'clazz'
309
310 If 'clazz' is None a default classification is created
311 """
312
313 # prevent infinite recursion when calling SetLayer()
314 if self.__setClassLock: return
315
316 self.__setClassLock = True
317
318 if clazz is None:
319 if self.__classification is not None:
320 self.__classification.SetLayer(None)
321 self.__classification = classification.Classification()
322 else:
323 self.__classification = clazz
324 try:
325 self.__classification.SetLayer(self)
326 except ValueError:
327 self.__setClassLock = False
328 raise ValueError
329
330 self.changed(LAYER_CHANGED, self)
331
332 self.__setClassLock = False
333
334 def ClassChanged(self):
335 """Called from the classification object when it has changed."""
336 self.changed(LAYER_CHANGED, self)
337
338 def TreeInfo(self):
339 items = []
340
341 if self.Visible():
342 items.append(_("Shown"))
343 else:
344 items.append(_("Hidden"))
345 items.append(_("Shapes: %d") % self.NumShapes())
346
347 bbox = self.LatLongBoundingBox()
348 if bbox is not None:
349 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
350 else:
351 items.append(_("Extent (lat-lon):"))
352 items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
353
354 if self.projection and len(self.projection.params) > 0:
355 items.append((_("Projection"),
356 [str(param) for param in self.projection.params]))
357
358 items.append(self.__classification)
359
360 return (_("Layer '%s'") % self.Title(), items)
361
362

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26