/[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 723 by bh, Thu Apr 24 15:31:53 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
17    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
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):
93    
94  class ShapefileStore:      """A Table that depends on a ShapefileStore
95    
96        Intended use is by the ShapefileStore for the table associated with
97        the shapefiles.
98        """
99    
100        def __init__(self, store, db, table):
101            """Initialize the ShapeTable.
102    
103            Parameters:
104                store -- the ShapefileStore the table is to depend on
105                db -- The transient database to use
106                table -- the table
107            """
108            transientdb.AutoTransientTable.__init__(self, db, table)
109            self.store = weakref.ref(store)
110    
111        def Dependencies(self):
112            """Return a tuple containing the shapestore"""
113            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(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)
200          # interpreted relative to that anyway, but when saving a  
201          # session we need to compare absolute paths and it's usually          self.dbftable = table.DBFTable(filename)
202          # safer to always work with absolute paths.          self._table = ShapeTable(self, session.TransientDB(), self.dbftable)
203          self.filename = os.path.abspath(filename)          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          self.shapefile = shapelib.ShapeFile(self.filename)          # estimate a good depth for the quad tree. Each depth multiplies
216          self.table = table.Table(filename)          # 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 self.table          """Return the table containing the attribute data."""
228            return self._table
229    
230      def Shapefile(self):      def Shapefile(self):
231            """Return the shapefile object"""
232          return self.shapefile          return self.shapefile
233    
234        def FileType(self):
235            """Return the filetype. This is always the string 'shapefile'"""
236            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):
254            """Return the empty tuple.
255    
256            The ShapefileStore doesn't depend on anything else.
257            """
258            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:
301    
302        """A ShapeStore derived from other shapestores or tables"""
303    
304        def __init__(self, shapestore, table):
305            """Initialize the derived shapestore.
306    
307            The arguments are a shapestore for the shapedata and a table for
308            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
319            self.table = table
320    
321        def Table(self):
322            """Return the table"""
323            return self.table
324    
325        def Shapefile(self):
326            """Return the shapefile of the underlying shapestore"""
327            return self.shapestore.Shapefile()
328    
329        def Dependencies(self):
330            """Return a tuple containing the shapestore and the table"""
331            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        def Shape(self, index):
340            """Return the shape with index index"""
341            return self.shapestore.Shape(index)
342    
343        def ShapesInRegion(self, bbox):
344            """Return the ids of the shapes that overlap the box.
345    
346            This method is simply delegated to the shapestore the
347            DerivedShapeStore was instantiated with.
348            """
349            return self.shapestore.ShapesInRegion(bbox)
350    
351        def AllShapes(self):
352            """Return an iterable over the shapes in the shapefile.
353    
354            This method is simply delegated to the shapestore the
355            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.723  
changed lines
  Added in v.2605

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26