/[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 143 by bh, Tue May 7 14:17:20 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    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)  
   
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.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      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              #print "building tree for", self.filename, "..."          else:
164              self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2, 0)              self.bbox = None
165              #print "done"  
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 180  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):      def ShapesInRegion(self, box):
246          """Return the ids of the shapes that overlap the box.          """Return the ids of the shapes that overlap the box.
247    
248          Box is a tuple (left, bottom, right, top) in the coordinate          Box is a tuple (left, bottom, right, top) in unprojected coordinates.
         system used by the layer's shapefile.  
249          """          """
250          left, bottom, right, top = box          left, bottom, right, top = box
251          import time  
252          start = time.time()          if self.projection is not None:
253          ids = self.shapetree.find_shapes((left, bottom), (right, top))              left,  bottom = self.projection.Forward(left, bottom)
254          print "ShapesInRegion", time.time() - start              right, top    = self.projection.Forward(right, top)
255          return ids  
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.143  
changed lines
  Added in v.794

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26