/[thuban]/branches/WIP-pyshapelib-bramz/Thuban/Model/data.py
ViewVC logotype

Diff of /branches/WIP-pyshapelib-bramz/Thuban/Model/data.py

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

revision 984 by bh, Thu May 22 16:37:48 2003 UTC revision 2605 by jan, Wed Apr 27 11:04:56 2005 UTC
# Line 1  Line 1 
1  # Copyright (C) 2003 by Intevation GmbH  # Copyright (C) 2003, 2005 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # Bernhard Herzog <[email protected]>
4  #  #
# Line 7  Line 7 
7    
8  """Data source abstractions"""  """Data source abstractions"""
9    
10    from __future__ import generators
11    
12  __version__ = "$Revision$"  __version__ = "$Revision$"
13  # $Source$  # $Source$
14  # $Id$  # $Id$
15    
16  import os  import os
 import warnings  
17  import weakref  import weakref
18    from math import ceil, log
19    
20  import shapelib  import shapelib
21    import shptree
22  import table  import table
23  import transientdb  import transientdb
24    
25    from Thuban import _
26    
27    # Shape type constants
28    SHAPETYPE_POLYGON = "polygon"
29    SHAPETYPE_ARC = "arc"
30    SHAPETYPE_POINT = "point"
31    
32    # mapping from shapelib shapetype constants to our constants
33    shapelib_shapetypes = {shapelib.SHPT_POLYGON: SHAPETYPE_POLYGON,
34                           shapelib.SHPT_ARC: SHAPETYPE_ARC,
35                           shapelib.SHPT_POINT: SHAPETYPE_POINT}
36    
37    #
38    # Raw shape data formats
39    #
40    
41    # Raw data is the same as that returned by the points method.
42    RAW_PYTHON = "RAW_PYTHON"
43    
44    # Raw data is a shapefile. The Shape object will use the shapeid as the
45    # raw data.
46    RAW_SHAPEFILE = "RAW_SHAPEFILE"
47    
48    # Raw data in well-known text format
49    RAW_WKT = "RAW_WKT"
50    
51    
52    class ShapefileShape:
53    
54        """Represent one shape of a shapefile"""
55    
56        def __init__(self, shapefile, shapeid):
57            self.shapefile = shapefile
58            self.shapeid = shapeid
59    
60        def compute_bbox(self):
61            """
62            Return the bounding box of the shape as a tuple (minx,miny,maxx,maxy)
63            """
64            xs = []
65            ys = []
66            for part in self.Points():
67                for x, y in part:
68                    xs.append(x)
69                    ys.append(y)
70            return (min(xs), min(ys), max(xs), max(ys))
71    
72        def ShapeID(self):
73            return self.shapeid
74    
75        def Points(self):
76            """Return the coordinates of the shape as a list of lists of pairs"""
77            shape = self.shapefile.read_object(self.shapeid)
78            points = shape.vertices()
79            if self.shapefile.info()[1] == shapelib.SHPT_POINT:
80                points = [points]
81            return points
82    
83        def RawData(self):
84            """Return the shape id to use with the shapefile"""
85            return self.shapeid
86    
87        def Shapefile(self):
88            """Return the shapefile object"""
89            return self.shapefile
90    
91    
92  class ShapeTable(transientdb.AutoTransientTable):  class ShapeTable(transientdb.AutoTransientTable):
93    
# Line 43  class ShapeTable(transientdb.AutoTransie Line 112  class ShapeTable(transientdb.AutoTransie
112          """Return a tuple containing the shapestore"""          """Return a tuple containing the shapestore"""
113          return (self.store(),)          return (self.store(),)
114    
115    # XXX: (this statement should be kept in mind when re-engeneering)
116    #
117    # From a desing POV it was wrong to distinguish between table and
118    # shapestore.  In hindsight the only reason for doing so was that the
119    # shapelib has different objects for the shapefile(s) and the dbf file,
120    # which of course matches the way the data is organized into different
121    # files.  So the distinction between shapestore and table is an artifact
122    # of the shapefile format.  When we added the postgis support we should
123    # have adopted the table interface for the entire shape store, making the
124    # geometry data an additional column for those shape stores that don't
125    # store the geometries in columns in the first place.
126    
127    class FileShapeStore:
128    
129        """The base class to derive any file-based ShapeStore from.
130    
131        This class contains all information that is needed by a
132        loader routine to actually load the shapestore.
133        This essentially means that the class contains all required information
134        to save the shapestore specification (i.e. in a .thuban file).
135        """
136    
137        def __init__(self, filename, sublayer_name = None):
138            """Initialize the base class with main parameters.
139    
140            filename  -- the source filename.
141                         This filename will be converted to an absolute filename.
142                         The filename will be interpreted relative to the .thuban file anyway,
143                         but when saving a session we need to compare absolute paths
144                         and it's usually safer to always work with absolute paths.
145            sublayer_name -- a string representing a layer within the file shape store.
146                         Some file formats support to contain several layers, or
147                         at least the ogr library says so.
148                         For those filetypes who don't, the sublayer_name can be ignored
149                         and by default it is None.
150            """
151            self._filename = os.path.abspath(filename)
152            self._sublayer_name = sublayer_name
153    
154        def FileName(self):
155            """Return the filename used to open the shapestore.
156    
157            The filename can only be set via __init__ method.
158            """
159            return self._filename
160    
161        def FileType(self):
162            """Return the filetype.
163    
164            The filetype has to be set in all derived classes.
165            It must be string.
166            Known and used types are: "shapefile"
167            """
168            raise NotImplementedError
169    
170        def SublayerName(self):
171            """Return the sublayer_name.
172    
173            This could be None if the shapestore type only supports a single
174            layer.
175            """
176            return self._sublayer_name
177    
178        # Design/Implementation note:
179        # It is not a good idea to have a implementation for a
180        # "setBoundingBox" or BoundingBox in this base class.
181        # In future this data might change during
182        # a Thuban session and thus can not be kept statically here.
183        # It is expected that for many derived classes the bbox must
184        # be retrieved each time anew.
185    
186        def BoundingBox(self):
187            """Return the bounding box of the shapes in the shape store.
188    
189            The coordinate system used is whatever was used in the shape store.
190            If the shape store is empty, return None.
191            """
192            raise NotImplementedError
193    
194  class ShapefileStore:  class ShapefileStore(FileShapeStore):
195    
196      """Combine a shapefile and the corresponding DBF file into one object"""      """Combine a shapefile and the corresponding DBF file into one object"""
197    
198      def __init__(self, session, filename):      def __init__(self, session, filename):
199          # Make the filename absolute. The filename will be          FileShapeStore.__init__(self, filename)
         # 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)  
200    
         self.shapefile = shapelib.ShapeFile(self.filename)  
201          self.dbftable = table.DBFTable(filename)          self.dbftable = table.DBFTable(filename)
202          self.table = ShapeTable(self, session.TransientDB(), self.dbftable)          self._table = ShapeTable(self, session.TransientDB(), self.dbftable)
203            self._bbox = None
204            self._open_shapefile()
205    
206        def _open_shapefile(self):
207            self.shapefile = shapelib.ShapeFile(self.FileName())
208            self.numshapes, shapetype, mins, maxs = self.shapefile.info()
209            if self.numshapes:
210                self._bbox = mins[:2] + maxs[:2]
211            else:
212                self._bbox = None
213            self.shapetype = shapelib_shapetypes[shapetype]
214    
215            # estimate a good depth for the quad tree. Each depth multiplies
216            # the number of nodes by four, therefore we basically take the
217            # base 4 logarithm of the number of shapes.
218            if self.numshapes < 4:
219                maxdepth = 1
220            else:
221                maxdepth = int(ceil(log(self.numshapes / 4.0) / log(4)))
222    
223            self.shapetree = shptree.SHPTree(self.shapefile.cobject(), 2,
224                                             maxdepth)
225    
226      def Table(self):      def Table(self):
227          """Return the table containing the attribute data"""          """Return the table containing the attribute data."""
228          return self.table          return self._table
229    
230      def Shapefile(self):      def Shapefile(self):
231          """Return the shapefile object"""          """Return the shapefile object"""
232          return self.shapefile          return self.shapefile
233    
     def FileName(self):  
         """Return the filename used to open the shapefile"""  
         return self.filename  
   
234      def FileType(self):      def FileType(self):
235          """Return the filetype. This is always the string 'shapefile'"""          """Return the filetype. This is always the string 'shapefile'"""
236          return "shapefile"          return "shapefile"
237    
238        def ShapeType(self):
239            """Return the type of the shapes in the shapestore.
240    
241            This is either SHAPETYPE_POINT, SHAPETYPE_ARC or SHAPETYPE_POLYGON.
242            """
243            return self.shapetype
244    
245        def RawShapeFormat(self):
246            """Return the raw data format of the shape data, i.e. RAW_SHAPEFILE"""
247            return RAW_SHAPEFILE
248    
249        def NumShapes(self):
250            """Return the number of shapes in the shapefile"""
251            return self.numshapes
252    
253      def Dependencies(self):      def Dependencies(self):
254          """Return the empty tuple.          """Return the empty tuple.
255    
# Line 82  class ShapefileStore: Line 257  class ShapefileStore:
257          """          """
258          return ()          return ()
259    
260        def OrigShapeStore(self):
261            """Return None.
262    
263            The ShapefileStore was not derived from another shapestore.
264            """
265            return None
266    
267        def BoundingBox(self):
268            """Return the bounding box of the shapes in the shapefile.
269    
270            The coordinate system used is whatever was used in the shapefile.
271            If the shapefileis empty, return None.
272            """
273            return self._bbox
274    
275        def ShapesInRegion(self, bbox):
276            """Return an iterable over the shapes that overlap the bounding box.
277    
278            The bbox parameter should be the bounding box as a tuple in the
279            form (minx, miny, maxx, maxy) in the coordinate system of the
280            shapefile.
281            """
282            # Bind a few globals to locals to make it a bit faster
283            cls = ShapefileShape
284            shapefile = self.shapefile
285    
286            left, bottom, right, top = bbox
287            for i in self.shapetree.find_shapes((left, bottom), (right, top)):
288                yield cls(shapefile, i)
289    
290        def AllShapes(self):
291            """Return an iterable over the shapes in the shapefile."""
292            for i in xrange(self.NumShapes()):
293                yield ShapefileShape(self.shapefile, i)
294    
295        def Shape(self, index):
296            """Return the shape with index index"""
297            return ShapefileShape(self.shapefile, index)
298    
299    
300  class DerivedShapeStore:  class DerivedShapeStore:
301    
# Line 92  class DerivedShapeStore: Line 306  class DerivedShapeStore:
306    
307          The arguments are a shapestore for the shapedata and a table for          The arguments are a shapestore for the shapedata and a table for
308          the tabular data.          the tabular data.
309    
310            Raises ValueError if the number of shapes in the shapestore
311            is different from the number of rows in the table.
312          """          """
313    
314            numShapes = shapestore.Shapefile().info()[0]
315            if numShapes != table.NumRows():
316                raise ValueError(_("Table not compatible with shapestore."))
317    
318          self.shapestore = shapestore          self.shapestore = shapestore
319          self.table = table          self.table = table
320    
# Line 101  class DerivedShapeStore: Line 323  class DerivedShapeStore:
323          return self.table          return self.table
324    
325      def Shapefile(self):      def Shapefile(self):
326          """Return the shapefile of he underlying shapestore"""          """Return the shapefile of the underlying shapestore"""
327          return self.shapestore.Shapefile()          return self.shapestore.Shapefile()
328    
329      def Dependencies(self):      def Dependencies(self):
330          """Return a tuple containing the shapestore and the table"""          """Return a tuple containing the shapestore and the table"""
331          return (self.shapestore, self.table)          return (self.shapestore, self.table)
332    
333        def OrigShapeStore(self):
334            """
335            Return the original shapestore the derived store was instantiated with
336            """
337            return self.shapestore
338    
339  class SimpleStore:      def Shape(self, index):
340            """Return the shape with index index"""
341            return self.shapestore.Shape(index)
342    
343      """Combine a shapefile and a table object"""      def ShapesInRegion(self, bbox):
344            """Return the ids of the shapes that overlap the box.
345    
346      def __init__(self, shapefile, table):          This method is simply delegated to the shapestore the
347          warnings.warn("The SimpleStore is deprecated."          DerivedShapeStore was instantiated with.
348                        " Use DerivedShapeStore instead",          """
349                        DeprecationWarning, stacklevel = 2)          return self.shapestore.ShapesInRegion(bbox)
         self.shapefile = shapefile  
         self.table = table  
         self.filename = None  
350    
351      def Table(self):      def AllShapes(self):
352          return self.table          """Return an iterable over the shapes in the shapefile.
353    
354      def Shapefile(self):          This method is simply delegated to the shapestore the
355          return self.shapefile          DerivedShapeStore was instantiated with.
356            """
357            return self.shapestore.AllShapes()
358    
359        def ShapeType(self):
360            """Return the type of the shapes in the layer.
361    
362            This method is simply delegated to the shapestore the
363            DerivedShapeStore was instantiated with.
364            """
365            return self.shapestore.ShapeType()
366    
367        def RawShapeFormat(self):
368            """Return the raw data format of the shapes.
369    
370            This method is simply delegated to the shapestore the
371            DerivedShapeStore was instantiated with.
372            """
373            return self.shapestore.RawShapeFormat()
374    
375        def NumShapes(self):
376            """Return the number of shapes in the shapestore."""
377            return self.shapestore.NumShapes()
378    
379        def BoundingBox(self):
380            """Return the bounding box of the shapes in the shapestore.
381    
382            This method is simply delegated to the shapestore the
383            DerivedShapeStore was instantiated with.
384            """
385            return self.shapestore.BoundingBox()

Legend:
Removed from v.984  
changed lines
  Added in v.2605

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26