/[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 481 - (show annotations)
Thu Mar 6 16:46:36 2003 UTC (22 years ago) by jonathan
File MIME type: text/x-python
File size: 9278 byte(s)
(SetClassification): prevent recursion between this method and
Classification.SetLayer().

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
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.None,
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.None if the shapes are not filled
122 stroke -- the stroke color or Color.None if the shapes 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
151 self.SetClassification(classification.Classification(self))
152 self.__classification.SetDefaultLineColor(stroke)
153 self.__classification.SetDefaultLineWidth(lineWidth)
154 self.__classification.SetDefaultFill(fill)
155
156 self.UnsetModified()
157
158 def open_shapefile(self):
159 if self.shapefile is None:
160 self.shapefile = shapelib.ShapeFile(self.filename)
161 numshapes, shapetype, mins, maxs = self.shapefile.info()
162 self.numshapes = numshapes
163 self.shapetype = shapelib_shapetypes[shapetype]
164
165 # if there are shapes, set the bbox accordinly. Otherwise
166 # set it to None.
167 if self.numshapes:
168 self.bbox = mins[:2] + maxs[:2]
169 else:
170 self.bbox = None
171
172 # estimate a good depth for the quad tree. Each depth
173 # multiplies the number of nodes by four, therefore we
174 # basically take the base 4 logarithm of the number of
175 # shapes.
176 if self.numshapes < 4:
177 maxdepth = 1
178 else:
179 maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
180
181 self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
182 maxdepth)
183
184 def Destroy(self):
185 BaseLayer.Destroy(self)
186 if self.shapefile is not None:
187 self.shapefile.close()
188 self.shapefile = None
189 self.shapetree = None
190 self.table.Destroy()
191
192 def BoundingBox(self):
193 """Return the layer's bounding box in the intrinsic coordinate system.
194
195 If the layer has no shapes, return None.
196 """
197 # The bbox will be set by open_shapefile just as we need it
198 # here.
199 self.open_shapefile()
200 return self.bbox
201
202 def LatLongBoundingBox(self):
203 """Return the layer's bounding box in lat/long coordinates.
204
205 Return None, if the layer doesn't contain any shapes.
206 """
207 bbox = self.BoundingBox()
208 if bbox is not None:
209 llx, lly, urx, ury = bbox
210 if self.projection is not None:
211 llx, lly = self.projection.Inverse(llx, lly)
212 urx, ury = self.projection.Inverse(urx, ury)
213 return llx, lly, urx, ury
214 else:
215 return None
216
217 def GetFieldType(self, fieldName):
218 self.open_shapefile()
219 info = self.table.field_info_by_name(fieldName)
220 if info is not None:
221 return info[0]
222 else:
223 return None
224
225 def NumShapes(self):
226 """Return the number of shapes in the layer"""
227 self.open_shapefile()
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 self.open_shapefile()
235 return self.shapetype
236
237 def Shape(self, index):
238 """Return the shape with index index"""
239 self.open_shapefile()
240 shape = self.shapefile.read_object(index)
241
242 if self.shapetype == SHAPETYPE_POINT:
243 points = shape.vertices()
244 else:
245 #for poly in shape.vertices():
246 poly = shape.vertices()[0]
247 points = []
248 for x, y in poly:
249 points.append((x, y))
250
251 return Shape(points)
252
253 def ShapesInRegion(self, box):
254 """Return the ids of the shapes that overlap the box.
255
256 Box is a tuple (left, bottom, right, top) in the coordinate
257 system used by the layer's shapefile.
258 """
259 left, bottom, right, top = box
260 return self.shapetree.find_shapes((left, bottom), (right, top))
261
262 def SetProjection(self, projection):
263 """Set the layer's projection"""
264 self.projection = projection
265 self.changed(LAYER_PROJECTION_CHANGED, self)
266
267 def GetClassification(self):
268 return self.__classification
269
270 def SetClassification(self, clazz):
271
272 # prevent infinite recursion when calling SetLayer()
273 if clazz == self.__classification:
274 return
275
276 self.__classification = clazz
277 self.__classification.SetLayer(self)
278 self.changed(LAYER_LEGEND_CHANGED, self)
279
280 def TreeInfo(self):
281 items = []
282
283 if self.Visible():
284 items.append(_("Shown"))
285 else:
286 items.append(_("Hidden"))
287 items.append(_("Shapes: %d") % self.NumShapes())
288
289 bbox = self.LatLongBoundingBox()
290 if bbox is not None:
291 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
292 else:
293 items.append(_("Extent (lat-lon):"))
294 items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
295
296 items.append(self.__classification)
297
298 return (_("Layer '%s'") % self.Title(), items)
299

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26