/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/layer.py
ViewVC logotype

Contents of /branches/WIP-pyshapelib-bramz/Thuban/Model/layer.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 276 - (show annotations)
Fri Aug 23 15:25:07 2002 UTC (22 years, 6 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/layer.py
File MIME type: text/x-python
File size: 8999 byte(s)
(Layer.__init__): Make sure we have an
absolute filename.

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