/[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 389 - (show annotations)
Mon Feb 10 15:25:30 2003 UTC (22 years ago) by jonathan
File MIME type: text/x-python
File size: 8950 byte(s)
* Thuban/Model/layer.py (Layer): Remove the
        Set[Fill|Stroke|StrokeWidth]() methods. Code should call the
        SetDefault*() methods on the layer's classification object.
        (Layer.__init__): Use the new SetDefault*() methods in the
        Classification class.

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

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26