/[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 610 - (show annotations)
Fri Apr 4 13:56:59 2003 UTC (21 years, 11 months ago) by jonathan
File MIME type: text/x-python
File size: 10095 byte(s)
Rename Color.None to Color.Transparent.

1 # Copyright (c) 2001, 2002 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_LEGEND_CHANGED, \
19 LAYER_VISIBILITY_CHANGED, 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 LAYER_LEGEND_CHANGED -- the fill or stroke attributes have changed
106
107 """
108
109 def __init__(self, title, filename, projection = None,
110 fill = Color.Transparent,
111 stroke = Color.Black,
112 lineWidth = 1,
113 visible = 1):
114 """Initialize the layer.
115
116 title -- the title
117 filename -- the name of the shapefile
118 projection -- the projection object. Its Inverse method is
119 assumed to map the layer's coordinates to lat/long
120 coordinates
121 fill -- the fill color or Color.Transparent if the shapes are
122 not filled
123 stroke -- the stroke color or Color.Transparent if the shapes
124 are not stroked
125 visible -- boolean. If true the layer is visible.
126
127 colors are expected to be instances of Color class
128 """
129 BaseLayer.__init__(self, title, visible = visible)
130
131 # Make the filename absolute. The filename will be
132 # interpreted relative to that anyway, but when saving a
133 # session we need to compare absolute paths and it's usually
134 # safer to always work with absolute paths.
135 self.filename = os.path.abspath(filename)
136
137 self.projection = projection
138 self.shapefile = None
139 self.shapetree = None
140 self.open_shapefile()
141 # shapetable is the table associated with the shapefile, while
142 # table is the default table used to look up attributes for
143 # display
144 self.shapetable = Table(filename)
145 self.table = self.shapetable
146
147 #
148 # this is really important so that when the classification class
149 # tries to set its parent layer the variable will exist
150 #
151 self.__classification = None
152 self.__setClassLock = False
153
154
155 self.SetClassification(None)
156
157 self.__classification.SetDefaultLineColor(stroke)
158 self.__classification.SetDefaultLineWidth(lineWidth)
159 self.__classification.SetDefaultFill(fill)
160 self.__classification.SetLayer(self)
161
162 self.UnsetModified()
163
164 def open_shapefile(self):
165 if self.shapefile is None:
166 self.shapefile = shapelib.ShapeFile(self.filename)
167 numshapes, shapetype, mins, maxs = self.shapefile.info()
168 self.numshapes = numshapes
169 self.shapetype = shapelib_shapetypes[shapetype]
170
171 # if there are shapes, set the bbox accordinly. Otherwise
172 # set it to None.
173 if self.numshapes:
174 self.bbox = mins[:2] + maxs[:2]
175 else:
176 self.bbox = None
177
178 # estimate a good depth for the quad tree. Each depth
179 # multiplies the number of nodes by four, therefore we
180 # basically take the base 4 logarithm of the number of
181 # shapes.
182 if self.numshapes < 4:
183 maxdepth = 1
184 else:
185 maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
186
187 self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
188 maxdepth)
189
190 def Destroy(self):
191 BaseLayer.Destroy(self)
192 if self.shapefile is not None:
193 self.shapefile.close()
194 self.shapefile = None
195 self.shapetree = None
196 self.SetClassification(None)
197 self.table.Destroy()
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 # The bbox will be set by open_shapefile just as we need it
205 # here.
206 self.open_shapefile()
207 return self.bbox
208
209 def LatLongBoundingBox(self):
210 """Return the layer's bounding box in lat/long coordinates.
211
212 Return None, if the layer doesn't contain any shapes.
213 """
214 bbox = self.BoundingBox()
215 if bbox is not None:
216 llx, lly, urx, ury = bbox
217 if self.projection is not None:
218 llx, lly = self.projection.Inverse(llx, lly)
219 urx, ury = self.projection.Inverse(urx, ury)
220 return llx, lly, urx, ury
221 else:
222 return None
223
224 def GetFieldType(self, fieldName):
225 self.open_shapefile()
226 info = self.table.field_info_by_name(fieldName)
227 if info is not None:
228 return info[0]
229 else:
230 return None
231
232 def NumShapes(self):
233 """Return the number of shapes in the layer"""
234 self.open_shapefile()
235 return self.numshapes
236
237 def ShapeType(self):
238 """Return the type of the shapes in the layer.
239 This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
240 """
241 self.open_shapefile()
242 return self.shapetype
243
244 def Shape(self, index):
245 """Return the shape with index index"""
246 self.open_shapefile()
247 shape = self.shapefile.read_object(index)
248
249 if self.shapetype == SHAPETYPE_POINT:
250 points = shape.vertices()
251 else:
252 #for poly in shape.vertices():
253 poly = shape.vertices()[0]
254 points = []
255 for x, y in poly:
256 points.append((x, y))
257
258 return Shape(points)
259
260 def ShapesInRegion(self, box):
261 """Return the ids of the shapes that overlap the box.
262
263 Box is a tuple (left, bottom, right, top) in the coordinate
264 system used by the layer's shapefile.
265 """
266 left, bottom, right, top = box
267 return self.shapetree.find_shapes((left, bottom), (right, top))
268
269 def SetProjection(self, projection):
270 """Set the layer's projection"""
271 self.projection = projection
272 self.changed(LAYER_PROJECTION_CHANGED, self)
273
274 def GetClassification(self):
275 return self.__classification
276
277 def SetClassification(self, clazz):
278 """Set the classification to 'clazz'
279
280 If 'clazz' is None a default classification is created
281 """
282
283 # prevent infinite recursion when calling SetLayer()
284 if self.__setClassLock: return
285
286 self.__setClassLock = True
287
288 if clazz is None:
289 if self.__classification is not None:
290 self.__classification.SetLayer(None)
291 self.__classification = classification.Classification()
292 else:
293 self.__classification = clazz
294 try:
295 self.__classification.SetLayer(self)
296 except ValueError:
297 self.__setClassLock = False
298 raise ValueError
299
300 self.changed(LAYER_CHANGED, self)
301
302 self.__setClassLock = False
303
304 def ClassChanged(self):
305 """Called from the classification object when it has changed."""
306 self.changed(LAYER_CHANGED, self)
307
308 def TreeInfo(self):
309 items = []
310
311 if self.Visible():
312 items.append(_("Shown"))
313 else:
314 items.append(_("Hidden"))
315 items.append(_("Shapes: %d") % self.NumShapes())
316
317 bbox = self.LatLongBoundingBox()
318 if bbox is not None:
319 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
320 else:
321 items.append(_("Extent (lat-lon):"))
322 items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
323
324 items.append(self.__classification)
325
326 return (_("Layer '%s'") % self.Title(), items)
327

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26