/[thuban]/trunk/thuban/Thuban/Model/layer.py
ViewVC logotype

Diff of /trunk/thuban/Thuban/Model/layer.py

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 82 by bh, Fri Feb 15 17:11:04 2002 UTC revision 794 by jonathan, Wed Apr 30 17:00:52 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4    # Jonathan Coles <[email protected]>
5  #  #
6  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
7  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
8    
9  __version__ = "$Revision$"  __version__ = "$Revision$"
10    
11  import shapelib  from math import log, ceil
12    
13  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  from Thuban import _
      LAYER_VISIBILITY_CHANGED  
14    
15  from color import Color  import shapelib, shptree
16  # Some predefined colors for internal use  
17  _black = Color(0, 0, 0)  from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \
18         LAYER_CHANGED
19    
20    from color import Color
21    
22  from table import Table  import classification
23    
24  from base import TitledObject, Modifiable  from base import TitledObject, Modifiable
25    
26    
27  class Shape:  class Shape:
28    
29      """Represent one shape"""      """Represent one shape"""
# Line 63  class BaseLayer(TitledObject, Modifiable Line 66  class BaseLayer(TitledObject, Modifiable
66    
67      """Base class for the layers."""      """Base class for the layers."""
68    
69      def __init__(self, title, visible = 1):      def __init__(self, title, visible = True):
70          """Initialize the layer.          """Initialize the layer.
71    
72          title -- the title          title -- the title
# Line 81  class BaseLayer(TitledObject, Modifiable Line 84  class BaseLayer(TitledObject, Modifiable
84          """Set the layer's visibility."""          """Set the layer's visibility."""
85          self.visible = visible          self.visible = visible
86          self.issue(LAYER_VISIBILITY_CHANGED, self)          self.issue(LAYER_VISIBILITY_CHANGED, self)
87            
88            
89  class Layer(BaseLayer):  class Layer(BaseLayer):
90    
91      """Represent the information of one geodata file (currently a shapefile)      """Represent the information of one geodata file (currently a shapefile)
# Line 97  class Layer(BaseLayer): Line 100  class Layer(BaseLayer):
100    
101          TITLE_CHANGED -- The title has changed.          TITLE_CHANGED -- The title has changed.
102          LAYER_PROJECTION_CHANGED -- the projection has changed.          LAYER_PROJECTION_CHANGED -- the projection has changed.
         LAYER_LEGEND_CHANGED -- the fill or stroke attributes have changed  
   
103      """      """
104    
105      def __init__(self, title, filename, projection = None,      def __init__(self, title, data, projection = None,
106                   fill = None, stroke = _black, stroke_width = 1, visible = 1):                   fill = Color.Transparent,
107                     stroke = Color.Black,
108                     lineWidth = 1,
109                     visible = True):
110          """Initialize the layer.          """Initialize the layer.
111    
112          title -- the title          title -- the title
113          filename -- the name of the shapefile          data -- datastore object for the shape data shown by the layer
114          projection -- the projection object. Its Inverse method is          projection -- the projection object. Its Inverse method is
115                 assumed to map the layer's coordinates to lat/long                 assumed to map the layer's coordinates to lat/long
116                 coordinates                 coordinates
117          fill -- the fill color or None if the shapes are not filled          fill -- the fill color or Color.Transparent if the shapes are
118          stroke -- the stroke color or None if the shapes are not stroked                  not filled
119            stroke -- the stroke color or Color.Transparent if the shapes
120                    are not stroked
121          visible -- boolean. If true the layer is visible.          visible -- boolean. If true the layer is visible.
122    
123          colors are expected to be instances of Color class          colors are expected to be instances of Color class
124          """          """
125          BaseLayer.__init__(self, title, visible = visible)          BaseLayer.__init__(self, title, visible = visible)
126          self.filename = filename  
127          self.projection = projection          self.projection = projection
128          self.fill = fill  
129          self.stroke = stroke          #
130          self.stroke_width = stroke_width          # this is really important so that when the classification class
131          self.shapefile = None          # tries to set its parent layer the variable will exist
132          self.open_shapefile()          #
133          # shapetable is the table associated with the shapefile, while          self.__classification = None
134          # table is the default table used to look up attributes for          self.__setClassLock = False
135          # display  
136          self.shapetable = Table(filename)          self.SetShapeStore(data)
137    
138            self.SetClassification(None)
139    
140            self.__classification.SetDefaultLineColor(stroke)
141            self.__classification.SetDefaultLineWidth(lineWidth)
142            self.__classification.SetDefaultFill(fill)
143            self.__classification.SetLayer(self)
144    
145            self.UnsetModified()
146    
147    
148        def SetShapeStore(self, store):
149            self.store = store
150            self.shapefile = self.store.Shapefile()
151            self.shapetable = self.store.Table()
152            self.filename = self.store.filename
153          self.table = self.shapetable          self.table = self.shapetable
154    
155      def open_shapefile(self):          numshapes, shapetype, mins, maxs = self.shapefile.info()
156          if self.shapefile is None:          self.numshapes = numshapes
157              self.shapefile = shapelib.ShapeFile(self.filename)          self.shapetype = shapelib_shapetypes[shapetype]
158              numshapes, shapetype, mins, maxs = self.shapefile.info()  
159              self.numshapes = numshapes          # if there are shapes, set the bbox accordingly. Otherwise
160              self.shapetype = shapelib_shapetypes[shapetype]          # set it to None.
161            if self.numshapes:
162              self.bbox = mins[:2] + maxs[:2]              self.bbox = mins[:2] + maxs[:2]
163            else:
164                self.bbox = None
165    
166            # estimate a good depth for the quad tree. Each depth
167            # multiplies the number of nodes by four, therefore we
168            # basically take the base 4 logarithm of the number of
169            # shapes.
170            if self.numshapes < 4:
171                maxdepth = 1
172            else:
173                maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
174    
175            self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
176                                             maxdepth)
177            if self.__classification is not None:
178                fieldname = self.__classification.GetField()
179                if fieldname is not None and \
180                   not self.store.Table().field_info_by_name(fieldname):
181                    self.SetClassification(None)
182            self.changed(LAYER_CHANGED, self)
183    
184        def ShapeStore(self):
185            return self.store
186    
187        def Destroy(self):
188            BaseLayer.Destroy(self)
189            self.SetClassification(None)
190    
191      def BoundingBox(self):      def BoundingBox(self):
192          """Return the bounding box of the layer's shapes in their default          """Return the layer's bounding box in the intrinsic coordinate system.
193          coordinate system"""  
194          self.open_shapefile()          If the layer has no shapes, return None.
195            """
196          return self.bbox          return self.bbox
197    
198      def LatLongBoundingBox(self):      def LatLongBoundingBox(self):
199          """Return the layer's bounding box in lat/long coordinates"""          """Return the layer's bounding box in lat/long coordinates.
200          llx, lly, urx, ury = self.BoundingBox()  
201          if self.projection is not None:          Return None, if the layer doesn't contain any shapes.
202              llx, lly = self.projection.Inverse(llx, lly)          """
203              urx, ury = self.projection.Inverse(urx, ury)          bbox = self.BoundingBox()
204          return llx, lly, urx, ury          if bbox is not None:
205                llx, lly, urx, ury = bbox
206                if self.projection is not None:
207                    llx, lly = self.projection.Inverse(llx, lly)
208                    urx, ury = self.projection.Inverse(urx, ury)
209                return llx, lly, urx, ury
210            else:
211                return None
212    
213        def GetFieldType(self, fieldName):
214            info = self.table.field_info_by_name(fieldName)
215            if info is not None:
216                return info[0]
217            else:
218                return None
219    
220      def NumShapes(self):      def NumShapes(self):
221          """Return the number of shapes in the layer"""          """Return the number of shapes in the layer"""
         self.open_shapefile()  
222          return self.numshapes          return self.numshapes
223    
224      def ShapeType(self):      def ShapeType(self):
225          """Return the type of the shapes in the layer.          """Return the type of the shapes in the layer.
226          This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.          This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
227          """          """
         self.open_shapefile()  
228          return self.shapetype          return self.shapetype
229    
230      def Shape(self, index):      def Shape(self, index):
231          """Return the shape with index index"""          """Return the shape with index index"""
         self.open_shapefile()  
232          shape = self.shapefile.read_object(index)          shape = self.shapefile.read_object(index)
233    
234          if self.shapetype == SHAPETYPE_POINT:          if self.shapetype == SHAPETYPE_POINT:
235              points = shape.vertices()              points = shape.vertices()
236          else:          else:
# Line 176  class Layer(BaseLayer): Line 239  class Layer(BaseLayer):
239              points = []              points = []
240              for x, y in poly:              for x, y in poly:
241                  points.append((x, y))                  points.append((x, y))
242    
243          return Shape(points)          return Shape(points)
244    
245        def ShapesInRegion(self, box):
246            """Return the ids of the shapes that overlap the box.
247    
248            Box is a tuple (left, bottom, right, top) in unprojected coordinates.
249            """
250            left, bottom, right, top = box
251    
252            if self.projection is not None:
253                left,  bottom = self.projection.Forward(left, bottom)
254                right, top    = self.projection.Forward(right, top)
255    
256            return self.shapetree.find_shapes((left, bottom), (right, top))
257    
258        def GetProjection(self):
259            return self.projection
260    
261      def SetProjection(self, projection):      def SetProjection(self, projection):
262          """Set the layer's projection"""          """Set the layer's projection"""
263          self.projection = projection          self.projection = projection
264          self.changed(LAYER_PROJECTION_CHANGED, self)          self.changed(LAYER_PROJECTION_CHANGED, self)
265    
266      def SetFill(self, fill):      def GetClassification(self):
267          """Set the layer's fill color. None means the shapes are not filled"""          return self.__classification
268          self.fill = fill  
269          self.changed(LAYER_LEGEND_CHANGED, self)      def SetClassification(self, clazz):
270            """Set the classification to 'clazz'
271      def SetStroke(self, stroke):  
272          """Set the layer's stroke color. None means the shapes are not          If 'clazz' is None a default classification is created
273          stroked."""          """
274          self.stroke = stroke  
275          self.changed(LAYER_LEGEND_CHANGED, self)          # prevent infinite recursion when calling SetLayer()
276            if self.__setClassLock: return
277      def SetStrokeWidth(self, width):  
278          """Set the layer's stroke width."""          self.__setClassLock = True
279          self.stroke_width = width  
280          self.changed(LAYER_LEGEND_CHANGED, self)          if clazz is None:
281                if self.__classification is not None:
282                    self.__classification.SetLayer(None)
283                self.__classification = classification.Classification()
284            else:
285                self.__classification = clazz
286                try:
287                    self.__classification.SetLayer(self)
288                except ValueError:
289                    self.__setClassLock = False
290                    raise ValueError
291    
292            self.changed(LAYER_CHANGED, self)
293    
294            self.__setClassLock = False
295    
296        def ClassChanged(self):
297            """Called from the classification object when it has changed."""
298            self.changed(LAYER_CHANGED, self)
299    
300        def TreeInfo(self):
301            items = []
302    
303            if self.Visible():
304                items.append(_("Shown"))
305            else:
306                items.append(_("Hidden"))
307            items.append(_("Shapes: %d") % self.NumShapes())
308    
309            bbox = self.LatLongBoundingBox()
310            if bbox is not None:
311                items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
312            else:
313                items.append(_("Extent (lat-lon):"))
314            items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
315    
316            if self.projection and len(self.projection.params) > 0:
317                items.append((_("Projection"),
318                            [str(param) for param in self.projection.params]))
319    
320            items.append(self.__classification)
321    
322            return (_("Layer '%s'") % self.Title(), items)
323    
324    

Legend:
Removed from v.82  
changed lines
  Added in v.794

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26