/[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 701 - (show annotations)
Thu Apr 17 16:18:48 2003 UTC (21 years, 10 months ago) by bh
File MIME type: text/x-python
File size: 9769 byte(s)
Don't import LAYER_LEGEND_CHANGED.
(Layer): Update doc-string since LAYER_LEGEND_CHANGED is not used
anymore.
(Layer.BoundingBox, Layer.GetFieldType, Layer.NumShapes)
(Layer.ShapeType, Layer.Shape): No need to call
self.open_shapefile since it's always called in __init__

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26