/[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 1427 by jonathan, Wed Jul 16 13:22:48 2003 UTC revision 2339 by silke, Fri Aug 20 16:59:21 2004 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003, 2004 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  # Jonathan Coles <[email protected]>  # Jonathan Coles <[email protected]>
5    # Silke Reimer <[email protected]>
6  #  #
7  # This program is free software under the GPL (>=v2)  # This program is free software under the GPL (>=v2)
8  # Read the file COPYING coming with Thuban for details.  # Read the file COPYING coming with Thuban for details.
9    
10  __version__ = "$Revision$"  __version__ = "$Revision$"
11    
12  from math import log, ceil  import os
13  import warnings  import warnings
14    
15  from Thuban import _  from Thuban import _
 import shapelib, shptree  
16    
17  from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \  from messages import LAYER_PROJECTION_CHANGED, LAYER_VISIBILITY_CHANGED, \
18       LAYER_CHANGED, LAYER_SHAPESTORE_REPLACED, CLASS_CHANGED       LAYER_CHANGED, LAYER_SHAPESTORE_REPLACED, CLASS_CHANGED
# Line 21  import classification Line 21  import classification
21    
22  from color import Transparent, Black  from color import Transparent, Black
23  from base import TitledObject, Modifiable  from base import TitledObject, Modifiable
24    from data import SHAPETYPE_POLYGON, SHAPETYPE_ARC, SHAPETYPE_POINT
25    
26  import resource  import resource
27    
28    
 class Shape:  
   
     """Represent one shape"""  
   
     def __init__(self, points):  
         self.points = points  
         #self.compute_bbox()  
         self.bbox = None  
   
     def compute_bbox(self):  
         if self.bbox is not None:  
             return self.bbox  
   
         xs = []  
         ys = []  
         for x, y in self.points:  
             xs.append(x)  
             ys.append(y)  
         self.llx = min(xs)  
         self.lly = min(ys)  
         self.urx = max(xs)  
         self.ury = max(ys)  
   
         self.bbox = (self.llx, self.lly, self.urx, self.ury)  
   
         return self.bbox  
   
     def Points(self):  
         return self.points  
   
   
   
 # Shape type constants  
 SHAPETYPE_POLYGON = "polygon"  
 SHAPETYPE_ARC = "arc"  
 SHAPETYPE_POINT = "point"  
   
 # mapping from shapelib shapetype constants to our constants  
 shapelib_shapetypes = {shapelib.SHPT_POLYGON: SHAPETYPE_POLYGON,  
                        shapelib.SHPT_ARC: SHAPETYPE_ARC,  
                        shapelib.SHPT_POINT: SHAPETYPE_POINT}  
   
29  shapetype_names = {SHAPETYPE_POINT: "Point",  shapetype_names = {SHAPETYPE_POINT: "Point",
30                     SHAPETYPE_ARC: "Arc",                     SHAPETYPE_ARC: "Arc",
31                     SHAPETYPE_POLYGON: "Polygon"}                     SHAPETYPE_POLYGON: "Polygon"}
# Line 157  class Layer(BaseLayer): Line 116  class Layer(BaseLayer):
116    
117          self.SetShapeStore(data)          self.SetShapeStore(data)
118    
119          self.SetClassificationField(None)          self.classification_column = None
120            self.SetClassificationColumn(None)
121          self.SetClassification(None)          self.SetClassification(None)
122    
123          self.__classification.SetDefaultLineColor(stroke)          self.__classification.SetDefaultLineColor(stroke)
# Line 166  class Layer(BaseLayer): Line 126  class Layer(BaseLayer):
126    
127          self.UnsetModified()          self.UnsetModified()
128    
     def __getattr__(self, attr):  
         """Access to some attributes for backwards compatibility  
   
         The attributes implemented here are now held by the shapestore  
         if at all. For backwards compatibility pretend that they are  
         still there but issue a DeprecationWarning when they are  
         accessed.  
         """  
         if attr in ("table", "shapetable"):  
             value = self.store.Table()  
         elif attr == "shapefile":  
             value = self.store.Shapefile()  
         elif attr == "filename":  
             value = self.store.FileName()  
         else:  
             raise AttributeError, attr  
         warnings.warn("The Layer attribute %r is deprecated."  
                       " It's value can be accessed through the shapestore"  
                       % attr, DeprecationWarning, stacklevel = 2)  
         return value  
   
129      def SetShapeStore(self, store):      def SetShapeStore(self, store):
130          # Set the classification to None if there is a classification          # Set the classification to None if there is a classification
131          # and the new shapestore doesn't have a table with a suitable          # and the new shapestore doesn't have a table with a suitable
# Line 194  class Layer(BaseLayer): Line 133  class Layer(BaseLayer):
133          # FIXME: Maybe we should keep it the same if the type is          # FIXME: Maybe we should keep it the same if the type is
134          # compatible enough such as FIELDTYPE_DOUBLE and FIELDTYPE_INT          # compatible enough such as FIELDTYPE_DOUBLE and FIELDTYPE_INT
135          if self.__classification is not None:          if self.__classification is not None:
136              fieldname = self.classificationField              columnname = self.classification_column
137              fieldtype = self.GetFieldType(fieldname)              columntype = self.GetFieldType(columnname)
138              table = store.Table()              table = store.Table()
139              if (fieldname is not None              if (columnname is not None
140                  and (not table.HasColumn(fieldname)                  and (not table.HasColumn(columnname)
141                       or table.Column(fieldname).type != fieldtype)):                       or table.Column(columnname).type != columntype)):
142                  self.SetClassification(None)                  self.SetClassification(None)
143    
144          self.store = store          self.store = store
         shapefile = self.store.Shapefile()  
145    
         numshapes, shapetype, mins, maxs = shapefile.info()  
         self.numshapes = numshapes  
         self.shapetype = shapelib_shapetypes[shapetype]  
   
         # if there are shapes, set the bbox accordingly. Otherwise  
         # set it to None.  
         if self.numshapes:  
             self.bbox = mins[:2] + maxs[:2]  
         else:  
             self.bbox = None  
   
         # estimate a good depth for the quad tree. Each depth  
         # 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)))  
   
         self.shapetree = shptree.SHPTree(shapefile.cobject(), 2,  
                                          maxdepth)  
146          self.changed(LAYER_SHAPESTORE_REPLACED, self)          self.changed(LAYER_SHAPESTORE_REPLACED, self)
147    
148      def ShapeStore(self):      def ShapeStore(self):
# Line 235  class Layer(BaseLayer): Line 151  class Layer(BaseLayer):
151      def Destroy(self):      def Destroy(self):
152          BaseLayer.Destroy(self)          BaseLayer.Destroy(self)
153          if self.__classification is not None:          if self.__classification is not None:
154              self.__classification.Unsubscribe(CLASS_CHANGED, self.ClassChanged)              self.__classification.Unsubscribe(CLASS_CHANGED,
155                                                  self._classification_changed)
156    
157      def BoundingBox(self):      def BoundingBox(self):
158          """Return the layer's bounding box in the intrinsic coordinate system.          """Return the layer's bounding box in the intrinsic coordinate system.
159    
160          If the layer has no shapes, return None.          If the layer has no shapes, return None.
161          """          """
162          return self.bbox          return self.store.BoundingBox()
163    
164      def LatLongBoundingBox(self):      def LatLongBoundingBox(self):
165          """Return the layer's bounding box in lat/long coordinates.          """Return the layer's bounding box in lat/long coordinates.
# Line 250  class Layer(BaseLayer): Line 167  class Layer(BaseLayer):
167          Return None, if the layer doesn't contain any shapes.          Return None, if the layer doesn't contain any shapes.
168          """          """
169          bbox = self.BoundingBox()          bbox = self.BoundingBox()
170          if bbox is not None:          if bbox is not None and self.projection is not None:
171              llx, lly, urx, ury = bbox              bbox = self.projection.InverseBBox(bbox)
172              if self.projection is not None:          return bbox
                 llx, lly = self.projection.Inverse(llx, lly)  
                 urx, ury = self.projection.Inverse(urx, ury)  
             return llx, lly, urx, ury  
         else:  
             return None  
173    
174      def ShapesBoundingBox(self, shapes):      def ShapesBoundingBox(self, shapes):
175          """Return a bounding box in lat/long coordinates for the given          """Return a bounding box in lat/long coordinates for the given
# Line 268  class Layer(BaseLayer): Line 180  class Layer(BaseLayer):
180    
181          if shapes is None or len(shapes) == 0: return None          if shapes is None or len(shapes) == 0: return None
182    
183          llx = []          xs = []
184          lly = []          ys = []
         urx = []  
         ury = []  
   
         if self.projection is not None:  
             inverse = lambda x, y: self.projection.Inverse(x, y)  
         else:  
             inverse = lambda x, y: (x, y)  
185    
186          for id in shapes:          for id in shapes:
187              left, bottom, right, top = self.Shape(id).compute_bbox()              bbox = self.Shape(id).compute_bbox()
188                if self.projection is not None:
189              left, bottom = inverse(left, bottom)                  bbox = self.projection.InverseBBox(bbox)
190              right, top   = inverse(right, top)              left, bottom, right, top = bbox
191                xs.append(left); xs.append(right)
192                ys.append(bottom); ys.append(top)
193    
194              llx.append(left)          return (min(xs), min(ys), max(xs), max(ys))
             lly.append(bottom)  
             urx.append(right)  
             ury.append(top)  
195    
         return (min(llx), min(lly), max(urx), max(ury))  
196    
197      def GetFieldType(self, fieldName):      def GetFieldType(self, fieldName):
198          if self.store:          if self.store:
# Line 303  class Layer(BaseLayer): Line 206  class Layer(BaseLayer):
206    
207      def NumShapes(self):      def NumShapes(self):
208          """Return the number of shapes in the layer"""          """Return the number of shapes in the layer"""
209          return self.numshapes          return self.store.NumShapes()
210    
211      def ShapeType(self):      def ShapeType(self):
212          """Return the type of the shapes in the layer.          """Return the type of the shapes in the layer.
213          This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.  
214            The return value is one of the SHAPETYPE_* constants defined in
215            Thuban.Model.data.
216          """          """
217          return self.shapetype          return self.store.ShapeType()
218    
219      def Shape(self, index):      def Shape(self, index):
220          """Return the shape with index index"""          """Return the shape with index index"""
221          shape = self.store.Shapefile().read_object(index)          return self.store.Shape(index)
222    
223          if self.shapetype == SHAPETYPE_POINT:      def ShapesInRegion(self, bbox):
224              points = shape.vertices()          """Return an iterable over the shapes that overlap the bounding box.
         else:  
             #for poly in shape.vertices():  
             poly = shape.vertices()[0]  
             points = []  
             for x, y in poly:  
                 points.append((x, y))  
225    
226          return Shape(points)          The bbox parameter should be the bounding box as a tuple in the
227            form (minx, miny, maxx, maxy) in unprojected coordinates.
     def ShapesInRegion(self, box):  
         """Return the ids of the shapes that overlap the box.  
   
         Box is a tuple (left, bottom, right, top) in unprojected coordinates.  
228          """          """
         left, bottom, right, top = box  
   
229          if self.projection is not None:          if self.projection is not None:
230              left,  bottom = self.projection.Forward(left, bottom)              # Ensure that region lies within the layer's bounding box
231              right, top    = self.projection.Forward(right, top)              # Otherwise projection of the region would lead to incorrect
232                # values.
233          return self.shapetree.find_shapes((left, bottom), (right, top))              clipbbox = self.ClipBoundingBox(bbox)
234                bbox = self.projection.ForwardBBox(clipbbox)
235      def GetClassificationField(self):          return self.store.ShapesInRegion(bbox)
236          return self.classificationField  
237        def GetClassificationColumn(self):
238      def SetClassificationField(self, field):          return self.classification_column
239          """Set the field to classifiy on, or None. If field is not None  
240          and the field does not exist in the table, raise a ValueError.      def SetClassificationColumn(self, column):
241          """          """Set the column to classifiy on, or None. If column is not None
242          if field:          and the column does not exist in the table, raise a ValueError.
243              fieldType = self.GetFieldType(field)          """
244              if fieldType is None:          if column:
245                columnType = self.GetFieldType(column)
246                if columnType is None:
247                  raise ValueError()                  raise ValueError()
248          self.classificationField = field          changed = self.classification_column != column
249            self.classification_column = column
250            if changed:
251                self.changed(LAYER_CHANGED, self)
252    
253      def HasClassification(self):      def HasClassification(self):
254          return True          return True
# Line 367  class Layer(BaseLayer): Line 265  class Layer(BaseLayer):
265          """          """
266    
267          if self.__classification is not None:          if self.__classification is not None:
268              self.__classification.Unsubscribe(CLASS_CHANGED, self.ClassChanged)              self.__classification.Unsubscribe(CLASS_CHANGED,
269                                                  self._classification_changed)
270    
271          if clazz is None:          if clazz is None:
272              clazz = classification.Classification()              clazz = classification.Classification()
273    
274          self.__classification = clazz          self.__classification = clazz
275          self.__classification.Subscribe(CLASS_CHANGED, self.ClassChanged)          self.__classification.Subscribe(CLASS_CHANGED,
276                                            self._classification_changed)
277    
278          self.ClassChanged()          self._classification_changed()
279    
280      def ClassChanged(self):      def _classification_changed(self):
281          """Called from the classification object when it has changed."""          """Called from the classification object when it has changed."""
282          self.changed(LAYER_CHANGED, self)          self.changed(LAYER_CHANGED, self)
283    
# Line 394  class Layer(BaseLayer): Line 294  class Layer(BaseLayer):
294    
295          bbox = self.LatLongBoundingBox()          bbox = self.LatLongBoundingBox()
296          if bbox is not None:          if bbox is not None:
297              items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % bbox)              items.append(_("Extent (lat-lon): (%g, %g, %g, %g)") % tuple(bbox))
298          else:          else:
299              items.append(_("Extent (lat-lon):"))              items.append(_("Extent (lat-lon):"))
300          items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])          items.append(_("Shapetype: %s") % shapetype_names[self.ShapeType()])
# Line 407  class Layer(BaseLayer): Line 307  class Layer(BaseLayer):
307    
308          return (_("Layer '%s'") % self.Title(), items)          return (_("Layer '%s'") % self.Title(), items)
309    
310        def ClipBoundingBox(self, bbox):
311            """ Clip bbox to layer's bounding box.
312    
313            Returns that part of bbox that lies within the layers bounding box.
314            If bbox is completely outside of the layers bounding box, bbox is
315            returned.  It is assumed that bbox has sensible values, i.e. bminx
316            < bmaxx and bminy < bmaxy.
317            """
318            bminx, bminy, bmaxx, bmaxy = bbox
319            lminx, lminy, lmaxx, lmaxy = self.LatLongBoundingBox()
320            if bminx > lmaxx or bmaxx < lminx:
321                left, right = bminx, bmaxx
322            else:
323                left = max(lminx, bminx)
324                right = min(lmaxx, bmaxx)
325            if bminy > lmaxy or bmaxy < lminy:
326                bottom, top = bminy, bmaxy
327            else:
328                bottom = max(lminy, bminy)
329                top = min(lmaxy, bmaxy)
330            
331            return (left, bottom, right, top)
332    
333    
334  if resource.has_gdal_support():  if resource.has_gdal_support():
335      import gdal      import gdal
# Line 433  class RasterLayer(BaseLayer): Line 356  class RasterLayer(BaseLayer):
356          BaseLayer.__init__(self, title, visible = visible)          BaseLayer.__init__(self, title, visible = visible)
357    
358          self.projection = projection          self.projection = projection
359          self.filename = filename          self.filename = os.path.abspath(filename)
360    
361          self.bbox = -1          self.bbox = -1
362    
# Line 495  class RasterLayer(BaseLayer): Line 418  class RasterLayer(BaseLayer):
418          if bbox is None:          if bbox is None:
419              return None              return None
420    
         llx, lly, urx, ury = bbox  
421          if self.projection is not None:          if self.projection is not None:
422              llx, lly = self.projection.Inverse(llx, lly)              bbox = self.projection.InverseBBox(bbox)
             urx, ury = self.projection.Inverse(urx, ury)  
423    
424          return llx, lly, urx, ury          return bbox
425    
426      def GetImageFilename(self):      def GetImageFilename(self):
427          return self.filename          return self.filename

Legend:
Removed from v.1427  
changed lines
  Added in v.2339

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26