/[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 364 - (show annotations)
Mon Jan 27 11:47:12 2003 UTC (22 years, 1 month ago) by jonathan
File MIME type: text/x-python
File size: 9201 byte(s)
added classification initializations

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26