/[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 382 - (show annotations)
Tue Jan 28 18:37:35 2003 UTC (22 years, 1 month ago) by jonathan
File MIME type: text/x-python
File size: 9411 byte(s)
Added a call to build the tree info for classifications. Commented out
unnecessary lines.

1 # Copyright (c) 2001, 2002 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 #
5 # This program is free software under the GPL (>=v2)
6 # Read the file COPYING coming with Thuban for details.
7
8 __version__ = "$Revision$"
9
10 import os
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_LEGEND_CHANGED, \
18 LAYER_VISIBILITY_CHANGED
19
20 from color import Color
21 # Some predefined colors for internal use
22 _black = Color(0, 0, 0)
23
24 from classification import Classification
25
26 from table import Table
27
28 from base import TitledObject, Modifiable
29
30 class Shape:
31
32 """Represent one shape"""
33
34 def __init__(self, points):
35 self.points = points
36 #self.compute_bbox()
37
38 def compute_bbox(self):
39 xs = []
40 ys = []
41 for x, y in self.points:
42 xs.append(x)
43 ys.append(y)
44 self.llx = min(xs)
45 self.lly = min(ys)
46 self.urx = max(xs)
47 self.ury = max(ys)
48
49 def Points(self):
50 return self.points
51
52
53
54 # Shape type constants
55 SHAPETYPE_POLYGON = "polygon"
56 SHAPETYPE_ARC = "arc"
57 SHAPETYPE_POINT = "point"
58
59 # mapping from shapelib shapetype constants to our constants
60 shapelib_shapetypes = {shapelib.SHPT_POLYGON: SHAPETYPE_POLYGON,
61 shapelib.SHPT_ARC: SHAPETYPE_ARC,
62 shapelib.SHPT_POINT: SHAPETYPE_POINT}
63
64 shapetype_names = {SHAPETYPE_POINT: "Point",
65 SHAPETYPE_ARC: "Arc",
66 SHAPETYPE_POLYGON: "Polygon"}
67
68 class BaseLayer(TitledObject, Modifiable):
69
70 """Base class for the layers."""
71
72 def __init__(self, title, visible = 1):
73 """Initialize the layer.
74
75 title -- the title
76 visible -- boolean. If true the layer is visible.
77 """
78 TitledObject.__init__(self, title)
79 Modifiable.__init__(self)
80 self.visible = visible
81
82 def Visible(self):
83 """Return true if layer is visible"""
84 return self.visible
85
86 def SetVisible(self, visible):
87 """Set the layer's visibility."""
88 self.visible = visible
89 self.issue(LAYER_VISIBILITY_CHANGED, self)
90
91
92 class Layer(BaseLayer):
93
94 """Represent the information of one geodata file (currently a shapefile)
95
96 All children of the layer have the same type.
97
98 A layer has fill and stroke colors. Colors should be instances of
99 Color. They can also be None, indicating no fill or no stroke.
100
101 The layer objects send the following events, all of which have the
102 layer object as parameter:
103
104 TITLE_CHANGED -- The title has changed.
105 LAYER_PROJECTION_CHANGED -- the projection has changed.
106 LAYER_LEGEND_CHANGED -- the fill or stroke attributes have changed
107
108 """
109
110 def __init__(self, title, filename, projection = None,
111 fill = None, stroke = _black, stroke_width = 1, 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 None if the shapes are not filled
120 stroke -- the stroke color or None if the shapes are not stroked
121 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
127 # Make the filename absolute. The filename will be
128 # interpreted relative to that anyway, but when saving a
129 # session we need to compare absolute paths and it's usually
130 # safer to always work with absolute paths.
131 self.filename = os.path.abspath(filename)
132
133 self.projection = projection
134 self.fill = fill
135 self.stroke = stroke
136 self.stroke_width = stroke_width
137 self.shapefile = None
138 self.shapetree = None
139 self.open_shapefile()
140 # shapetable is the table associated with the shapefile, while
141 # table is the default table used to look up attributes for
142 # display
143 self.shapetable = Table(filename)
144 self.table = self.shapetable
145
146 self.classification = Classification()
147 self.classification.setNull(
148 {'stroke':stroke, 'stroke_width':stroke_width, 'fill':fill})
149
150 def open_shapefile(self):
151 if self.shapefile is None:
152 self.shapefile = shapelib.ShapeFile(self.filename)
153 numshapes, shapetype, mins, maxs = self.shapefile.info()
154 self.numshapes = numshapes
155 self.shapetype = shapelib_shapetypes[shapetype]
156
157 # if there are shapes, set the bbox accordinly. Otherwise
158 # set it to None.
159 if self.numshapes:
160 self.bbox = mins[:2] + maxs[:2]
161 else:
162 self.bbox = None
163
164 # estimate a good depth for the quad tree. Each depth
165 # multiplies the number of nodes by four, therefore we
166 # basically take the base 4 logarithm of the number of
167 # shapes.
168 if self.numshapes < 4:
169 maxdepth = 1
170 else:
171 maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
172
173 self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
174 maxdepth)
175
176 def Destroy(self):
177 BaseLayer.Destroy(self)
178 if self.shapefile is not None:
179 self.shapefile.close()
180 self.shapefile = None
181 self.shapetree = None
182 self.table.Destroy()
183
184 def BoundingBox(self):
185 """Return the layer's bounding box in the intrinsic coordinate system.
186
187 If the layer has no shapes, return None.
188 """
189 # The bbox will be set by open_shapefile just as we need it
190 # here.
191 self.open_shapefile()
192 return self.bbox
193
194 def LatLongBoundingBox(self):
195 """Return the layer's bounding box in lat/long coordinates.
196
197 Return None, if the layer doesn't contain any shapes.
198 """
199 bbox = self.BoundingBox()
200 if bbox is not None:
201 llx, lly, urx, ury = bbox
202 if self.projection is not None:
203 llx, lly = self.projection.Inverse(llx, lly)
204 urx, ury = self.projection.Inverse(urx, ury)
205 return llx, lly, urx, ury
206 else:
207 return None
208
209 def NumShapes(self):
210 """Return the number of shapes in the layer"""
211 self.open_shapefile()
212 return self.numshapes
213
214 def ShapeType(self):
215 """Return the type of the shapes in the layer.
216 This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
217 """
218 self.open_shapefile()
219 return self.shapetype
220
221 def Shape(self, index):
222 """Return the shape with index index"""
223 self.open_shapefile()
224 shape = self.shapefile.read_object(index)
225
226 if self.shapetype == SHAPETYPE_POINT:
227 points = shape.vertices()
228 else:
229 #for poly in shape.vertices():
230 poly = shape.vertices()[0]
231 points = []
232 for x, y in poly:
233 points.append((x, y))
234
235 return Shape(points)
236
237 def ShapesInRegion(self, box):
238 """Return the ids of the shapes that overlap the box.
239
240 Box is a tuple (left, bottom, right, top) in the coordinate
241 system used by the layer's shapefile.
242 """
243 left, bottom, right, top = box
244 return self.shapetree.find_shapes((left, bottom), (right, top))
245
246 def SetProjection(self, projection):
247 """Set the layer's projection"""
248 self.projection = projection
249 self.changed(LAYER_PROJECTION_CHANGED, self)
250
251 def SetFill(self, fill):
252 """Set the layer's fill color. None means the shapes are not filled"""
253 self.fill = fill
254 self.changed(LAYER_LEGEND_CHANGED, self)
255
256 def SetStroke(self, stroke):
257 """Set the layer's stroke color. None means the shapes are not
258 stroked."""
259 self.stroke = stroke
260 self.changed(LAYER_LEGEND_CHANGED, self)
261
262 def SetStrokeWidth(self, width):
263 """Set the layer's stroke width."""
264 self.stroke_width = width
265 self.changed(LAYER_LEGEND_CHANGED, self)
266
267 def TreeInfo(self):
268 items = []
269
270 if self.Visible():
271 items.append(_("Shown"))
272 else:
273 items.append(_("Hidden"))
274 items.append(_("Shapes: %d") % self.NumShapes())
275
276 bbox = self.LatLongBoundingBox()
277 if bbox is not None:
278 items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
279 else:
280 items.append(_("Extent (lat-lon):"))
281 items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
282
283 def color_string(color):
284 if color is None:
285 return "None"
286 return "(%.3f, %.3f, %.3f)" % (color.red, color.green, color.blue)
287
288 # layers will always have a classification with at least a NULL data set
289
290 #items.append((_("Fill: %s") % color_string(self.fill), self.fill))
291 #items.append((_("Outline: %s") % color_string(self.stroke), self.stroke))
292
293 items.append(self.classification)
294
295 return (_("Layer '%s'") % self.Title(), items)
296

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26