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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 723 - (hide annotations)
Thu Apr 24 15:31:53 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: 9343 byte(s)
First step towards table management. Introduce a simple data
abstraction so that we replace the data a layer uses more easily
in the next step.

* Thuban/Model/data.py: New file with a simple data abstraction
that bundles shapefile and dbffile into one object.

* Thuban/Model/session.py (Session.OpenShapefile): New method to
open shapefiles and return a shape store object

* Thuban/Model/layer.py (Layer.__init__): Pass the data as a store
object instead of a shapefile filename. This introduces a new
instance variable store holding the datastore. For intermediate
backwards compatibility keep the old instance variables.
(open_shapefile): Removed. No longer needed with the shape store.
(Layer.SetShapeStore, Layer.ShapeStore): New methods to set and
get the shape store used by a layer.
(Layer.Destroy): No need to explicitly destroy the shapefile or
table anymore.

* Thuban/UI/mainwindow.py (MainWindow.AddLayer)
(MainWindow.AddLayer): Use the session's OpenShapefile method to
open shapefiles

* Thuban/Model/load.py (ProcessSession.start_layer): Use the
session's OpenShapefile method to open shapefiles

* test/test_classification.py
(TestClassification.test_classification): Use the session's
OpenShapefile method to open shapefiles and build the filename in
a more platform independed way

* test/test_layer.py (TestLayer.setUp, TestLayer.tearDown):
Implement to have a session to use in the tests
(TestLayer.test_arc_layer, TestLayer.test_polygon_layer)
(TestLayer.test_point_layer, TestLayer.test_empty_layer): Use the
session's OpenShapefile method to open shapefiles
(TestLayerLegend.setUp): Instantiate a session so that we can use
it to open shapefiles.
(TestLayerLegend.tearDown): Make sure that all references to
layers and session are removed otherwise we may get a resource
leak

* test/test_map.py (TestMapAddLayer.test_add_layer)
(TestMapWithContents.setUp): Instantiate a session so that we can
use it to open shapefiles.
(TestMapWithContents.tearDown): Make sure that all references to
layers, maps and sessions are removed otherwise we may get a
resource leak
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_save.py (SaveSessionTest.testSingleLayer): Use the
session's OpenShapefile method to open shapefiles
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_selection.py (TestSelection.tearDown): Make sure that
all references to the session and the selection are removed
otherwise we may get a resource leak
(TestSelection.get_layer): Instantiate a session so that we can
use it to open shapefiles.
("__main__"): use support.run_tests() so that more info about
uncollected garbage is printed

* test/test_session.py (TestSessionBase.tearDown)
(TestSessionWithContent.tearDown): Make sure that all references
to the session and layers are removed otherwise we may get a
resource leak
(TestSessionWithContent.setUp): Use the session's OpenShapefile
method to open shapefiles

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    
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 bh 276
88    
89 bh 6 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 bh 723 def __init__(self, title, data, projection = None,
106 jonathan 610 fill = Color.Transparent,
107 jonathan 412 stroke = Color.Black,
108 jonathan 464 lineWidth = 1,
109 jonathan 412 visible = 1):
110 bh 6 """Initialize the layer.
111    
112     title -- the title
113 bh 723 data -- datastore object for the shape data shown by the layer
114 bh 6 projection -- the projection object. Its Inverse method is
115     assumed to map the layer's coordinates to lat/long
116     coordinates
117 jonathan 610 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 bh 6 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 bh 276
127 bh 6 self.projection = projection
128    
129 jonathan 481 #
130     # this is really important so that when the classification class
131     # tries to set its parent layer the variable will exist
132     #
133 bh 723 self.__classification = None
134 jonathan 529 self.__setClassLock = False
135 jonathan 481
136 bh 723 self.SetShapeStore(data)
137 jonathan 492
138     self.SetClassification(None)
139    
140 jonathan 464 self.__classification.SetDefaultLineColor(stroke)
141     self.__classification.SetDefaultLineWidth(lineWidth)
142 jonathan 412 self.__classification.SetDefaultFill(fill)
143 jonathan 492 self.__classification.SetLayer(self)
144 jonathan 364
145 jonathan 389 self.UnsetModified()
146    
147 bh 6
148 bh 723 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 bh 179
155 bh 723 numshapes, shapetype, mins, maxs = self.shapefile.info()
156     self.numshapes = numshapes
157     self.shapetype = shapelib_shapetypes[shapetype]
158 bh 171
159 bh 723 # 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 bh 171
166 bh 723 # 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 bh 258 def Destroy(self):
187 bh 260 BaseLayer.Destroy(self)
188 jonathan 529 self.SetClassification(None)
189 bh 258
190 bh 6 def BoundingBox(self):
191 bh 179 """Return the layer's bounding box in the intrinsic coordinate system.
192    
193     If the layer has no shapes, return None.
194     """
195 bh 6 return self.bbox
196    
197     def LatLongBoundingBox(self):
198 bh 179 """Return the layer's bounding box in lat/long coordinates.
199 bh 6
200 bh 179 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 jonathan 464 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 bh 6 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 jonathan 364
233 bh 6 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 bh 82 points.append((x, y))
241 jonathan 364
242 bh 6 return Shape(points)
243    
244 bh 143 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 bh 147 return self.shapetree.find_shapes((left, bottom), (right, top))
252 bh 143
253 bh 6 def SetProjection(self, projection):
254     """Set the layer's projection"""
255     self.projection = projection
256     self.changed(LAYER_PROJECTION_CHANGED, self)
257    
258 jonathan 412 def GetClassification(self):
259     return self.__classification
260    
261     def SetClassification(self, clazz):
262 jonathan 492 """Set the classification to 'clazz'
263 jonathan 481
264 jonathan 492 If 'clazz' is None a default classification is created
265     """
266 jonathan 481
267 jonathan 529 # prevent infinite recursion when calling SetLayer()
268     if self.__setClassLock: return
269    
270     self.__setClassLock = True
271    
272 jonathan 492 if clazz is None:
273 jonathan 529 if self.__classification is not None:
274     self.__classification.SetLayer(None)
275 jonathan 492 self.__classification = classification.Classification()
276     else:
277     self.__classification = clazz
278 jonathan 529 try:
279     self.__classification.SetLayer(self)
280     except ValueError:
281     self.__setClassLock = False
282     raise ValueError
283 jonathan 492
284 jonathan 545 self.changed(LAYER_CHANGED, self)
285 jonathan 412
286 jonathan 529 self.__setClassLock = False
287    
288 jonathan 492 def ClassChanged(self):
289     """Called from the classification object when it has changed."""
290 jonathan 558 self.changed(LAYER_CHANGED, self)
291 jonathan 492
292 bh 217 def TreeInfo(self):
293     items = []
294    
295     if self.Visible():
296 jan 374 items.append(_("Shown"))
297 bh 217 else:
298 jan 374 items.append(_("Hidden"))
299     items.append(_("Shapes: %d") % self.NumShapes())
300 bh 217
301     bbox = self.LatLongBoundingBox()
302     if bbox is not None:
303 jan 374 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
304 bh 217 else:
305 jan 374 items.append(_("Extent (lat-lon):"))
306     items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
307 bh 217
308 jonathan 412 items.append(self.__classification)
309 bh 217
310 jan 374 return (_("Layer '%s'") % self.Title(), items)
311 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