/[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 364 by jonathan, Mon Jan 27 11:47:12 2003 UTC revision 736 by jonathan, Fri Apr 25 09:11:57 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    
 import os  
11  from math import log, ceil  from math import log, ceil
12    
13    from Thuban import _
14    
15  import shapelib, shptree  import shapelib, shptree
16    
17  from messages import LAYER_PROJECTION_CHANGED, LAYER_LEGEND_CHANGED, \  from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \
18       LAYER_VISIBILITY_CHANGED       LAYER_CHANGED
19    
20  from color import Color  from color import Color
 # Some predefined colors for internal use  
 _black = Color(0, 0, 0)  
   
 from classification import Classification  
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 101  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 = 1):
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    
         # Make the filename absolute. The filename will be  
         # interpreted relative to that anyway, but when saving a  
         # session we need to compare absolute paths and it's usually  
         # safer to always work with absolute paths.  
         self.filename = os.path.abspath(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.shapetree = None          #
133          self.open_shapefile()          self.__classification = None
134          # shapetable is the table associated with the shapefile, while          self.__setClassLock = False
135          # table is the default table used to look up attributes for  
136          # display          self.SetShapeStore(data)
137          self.shapetable = Table(filename)  
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          self.classification = Classification()          numshapes, shapetype, mins, maxs = self.shapefile.info()
156          self.classification.setNull(          self.numshapes = numshapes
157              {'stroke':stroke, 'stroke_width':stroke_width, 'fill':fill})          self.shapetype = shapelib_shapetypes[shapetype]
158    
159      def open_shapefile(self):          # if there are shapes, set the bbox accordingly. Otherwise
160          if self.shapefile is None:          # set it to None.
161              self.shapefile = shapelib.ShapeFile(self.filename)          if self.numshapes:
162              numshapes, shapetype, mins, maxs = self.shapefile.info()              self.bbox = mins[:2] + maxs[:2]
163              self.numshapes = numshapes          else:
164              self.shapetype = shapelib_shapetypes[shapetype]              self.bbox = None
165    
166              # if there are shapes, set the bbox accordinly. Otherwise          # estimate a good depth for the quad tree. Each depth
167              # set it to None.          # multiplies the number of nodes by four, therefore we
168              if self.numshapes:          # basically take the base 4 logarithm of the number of
169                  self.bbox = mins[:2] + maxs[:2]          # shapes.
170              else:          if self.numshapes < 4:
171                  self.bbox = None              maxdepth = 1
172            else:
173              # estimate a good depth for the quad tree. Each depth              maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
             # multiplies the number of nodes by four, therefore we  
             # basically take the base 4 logarithm of the number of  
             # shapes.  
             if self.numshapes < 4:  
                 maxdepth = 1  
             else:  
                 maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))  
174    
175              self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,          self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
176                                               maxdepth)                                           maxdepth)
177            if self.__classification is not None:
178                fieldname = self.__classification.GetField()
179                if not self.store.Table().field_info_by_name(fieldname):
180                    self.SetClassification(None)
181            self.changed(LAYER_CHANGED, self)
182    
183        def ShapeStore(self):
184            return self.store
185    
186      def Destroy(self):      def Destroy(self):
187          BaseLayer.Destroy(self)          BaseLayer.Destroy(self)
188          if self.shapefile is not None:          self.SetClassification(None)
             self.shapefile.close()  
             self.shapefile = None  
             self.shapetree = None  
         self.table.Destroy()  
189    
190      def BoundingBox(self):      def BoundingBox(self):
191          """Return the layer's bounding box in the intrinsic coordinate system.          """Return the layer's bounding box in the intrinsic coordinate system.
192    
193          If the layer has no shapes, return None.          If the layer has no shapes, return None.
194          """          """
         # The bbox will be set by open_shapefile just as we need it  
         # here.  
         self.open_shapefile()  
195          return self.bbox          return self.bbox
196    
197      def LatLongBoundingBox(self):      def LatLongBoundingBox(self):
# Line 204  class Layer(BaseLayer): Line 209  class Layer(BaseLayer):
209          else:          else:
210              return None              return None
211    
212        def GetFieldType(self, fieldName):
213            info = self.table.field_info_by_name(fieldName)
214            if info is not None:
215                return info[0]
216            else:
217                return None
218    
219      def NumShapes(self):      def NumShapes(self):
220          """Return the number of shapes in the layer"""          """Return the number of shapes in the layer"""
         self.open_shapefile()  
221          return self.numshapes          return self.numshapes
222    
223      def ShapeType(self):      def ShapeType(self):
224          """Return the type of the shapes in the layer.          """Return the type of the shapes in the layer.
225          This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.          This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
226          """          """
         self.open_shapefile()  
227          return self.shapetype          return self.shapetype
228    
229      def Shape(self, index):      def Shape(self, index):
230          """Return the shape with index index"""          """Return the shape with index index"""
         self.open_shapefile()  
231          shape = self.shapefile.read_object(index)          shape = self.shapefile.read_object(index)
232    
233          if self.shapetype == SHAPETYPE_POINT:          if self.shapetype == SHAPETYPE_POINT:
# Line 241  class Layer(BaseLayer): Line 250  class Layer(BaseLayer):
250          left, bottom, right, top = box          left, bottom, right, top = box
251          return self.shapetree.find_shapes((left, bottom), (right, top))          return self.shapetree.find_shapes((left, bottom), (right, top))
252    
253        def GetProjection(self):
254            return self.projection
255    
256      def SetProjection(self, projection):      def SetProjection(self, projection):
257          """Set the layer's projection"""          """Set the layer's projection"""
258          self.projection = projection          self.projection = projection
259          self.changed(LAYER_PROJECTION_CHANGED, self)          self.changed(LAYER_PROJECTION_CHANGED, self)
260    
261      def SetFill(self, fill):      def GetClassification(self):
262          """Set the layer's fill color. None means the shapes are not filled"""          return self.__classification
263          self.fill = fill  
264          self.changed(LAYER_LEGEND_CHANGED, self)      def SetClassification(self, clazz):
265            """Set the classification to 'clazz'
266      def SetStroke(self, stroke):  
267          """Set the layer's stroke color. None means the shapes are not          If 'clazz' is None a default classification is created
268          stroked."""          """
269          self.stroke = stroke  
270          self.changed(LAYER_LEGEND_CHANGED, self)          # prevent infinite recursion when calling SetLayer()
271            if self.__setClassLock: return
     def SetStrokeWidth(self, width):  
         """Set the layer's stroke width."""  
         self.stroke_width = width  
         self.changed(LAYER_LEGEND_CHANGED, self)  
272    
273            self.__setClassLock = True
274    
275            if clazz is None:
276                if self.__classification is not None:
277                    self.__classification.SetLayer(None)
278                self.__classification = classification.Classification()
279            else:
280                self.__classification = clazz
281                try:
282                    self.__classification.SetLayer(self)
283                except ValueError:
284                    self.__setClassLock = False
285                    raise ValueError
286    
287            self.changed(LAYER_CHANGED, self)
288    
289            self.__setClassLock = False
290    
291        def ClassChanged(self):
292            """Called from the classification object when it has changed."""
293            self.changed(LAYER_CHANGED, self)
294    
295      def TreeInfo(self):      def TreeInfo(self):
296          items = []          items = []
297    
298          if self.Visible():          if self.Visible():
299              items.append("Shown")              items.append(_("Shown"))
300          else:          else:
301              items.append("Hidden")              items.append(_("Hidden"))
302          items.append("Shapes: %d" % self.NumShapes())          items.append(_("Shapes: %d") % self.NumShapes())
303    
304          bbox = self.LatLongBoundingBox()          bbox = self.LatLongBoundingBox()
305          if bbox is not None:          if bbox is not None:
306              items.append("Extent (lat-lon): (%g, %g, %g, %g)" % bbox)              items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)
307          else:          else:
308              items.append("Extent (lat-lon):")              items.append(_("Extent (lat-lon):"))
309          items.append("Shapetype: %s" % shapetype_names[self.ShapeType()])          items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
310    
311            if self.projection and len(self.projection.params) > 0:
312                items.append((_("Projection"),
313                            [str(param) for param in self.projection.params]))
314    
315            items.append(self.__classification)
316    
317            return (_("Layer '%s'") % self.Title(), items)
318    
         def color_string(color):  
             if color is None:  
                 return "None"  
             return "(%.3f, %.3f, %.3f)" % (color.red, color.green, color.blue)  
         items.append("Fill: " + color_string(self.fill))  
         items.append("Outline: " + color_string(self.stroke))  
319    
         return ("Layer '%s'" % self.Title(), items)  

Legend:
Removed from v.364  
changed lines
  Added in v.736

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26