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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2734 - (hide annotations)
Thu Mar 1 12:42:59 2007 UTC (18 years ago) by bramz
File MIME type: text/x-python
File size: 12274 byte(s)
made a copy
1 jan 2605 # Copyright (C) 2003, 2005 by Intevation GmbH
2 bh 723 # Authors:
3     # Bernhard Herzog <[email protected]>
4     #
5     # This program is free software under the GPL (>=v2)
6     # Read the file COPYING coming with the software for details.
7    
8     """Data source abstractions"""
9    
10 bh 1593 from __future__ import generators
11    
12 bh 723 __version__ = "$Revision$"
13     # $Source$
14     # $Id$
15    
16     import os
17 bh 984 import weakref
18 bh 1535 from math import ceil, log
19 bh 723
20     import shapelib
21 bh 1535 import shptree
22 bh 723 import table
23 bh 765 import transientdb
24 bh 723
25 jonathan 1261 from Thuban import _
26 bh 765
27 bh 1535 # Shape type constants
28     SHAPETYPE_POLYGON = "polygon"
29     SHAPETYPE_ARC = "arc"
30     SHAPETYPE_POINT = "point"
31 jonathan 1261
32 bh 1535 # 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 bh 1559 #
38     # Raw shape data formats
39     #
40 bh 1535
41 bh 1559 # Raw data is the same as that returned by the points method.
42     RAW_PYTHON = "RAW_PYTHON"
43 bh 1535
44 bh 1559 # Raw data is a shapefile. The Shape object will use the shapeid as the
45     # raw data.
46     RAW_SHAPEFILE = "RAW_SHAPEFILE"
47 bh 1535
48 bh 1605 # Raw data in well-known text format
49     RAW_WKT = "RAW_WKT"
50 bh 1535
51 bh 1605
52 bh 1559 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 bh 1535 def compute_bbox(self):
61 bh 1559 """
62     Return the bounding box of the shape as a tuple (minx,miny,maxx,maxy)
63     """
64 bh 1535 xs = []
65     ys = []
66 bh 1559 for part in self.Points():
67 bh 1551 for x, y in part:
68     xs.append(x)
69     ys.append(y)
70 bh 1559 return (min(xs), min(ys), max(xs), max(ys))
71 bh 1535
72 bh 1593 def ShapeID(self):
73     return self.shapeid
74    
75 bh 1559 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 bh 1535
83 bh 1559 def RawData(self):
84     """Return the shape id to use with the shapefile"""
85     return self.shapeid
86 bh 1535
87 bh 1559 def Shapefile(self):
88     """Return the shapefile object"""
89     return self.shapefile
90 bh 1535
91    
92 bh 984 class ShapeTable(transientdb.AutoTransientTable):
93    
94     """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 jan 2605 # 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 bh 984
127 jan 2605 class FileShapeStore:
128 bh 723
129 jan 2605 """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 bh 723 """Combine a shapefile and the corresponding DBF file into one object"""
197    
198     def __init__(self, session, filename):
199 jan 2605 FileShapeStore.__init__(self, filename)
200 bh 723
201 bh 765 self.dbftable = table.DBFTable(filename)
202 jan 2605 self._table = ShapeTable(self, session.TransientDB(), self.dbftable)
203     self._bbox = None
204 bh 1972 self._open_shapefile()
205 bh 723
206 bh 1972 def _open_shapefile(self):
207 jan 2605 self.shapefile = shapelib.ShapeFile(self.FileName())
208 bh 1535 self.numshapes, shapetype, mins, maxs = self.shapefile.info()
209     if self.numshapes:
210 jan 2605 self._bbox = mins[:2] + maxs[:2]
211 bh 1535 else:
212 jan 2605 self._bbox = None
213 bh 1535 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 bh 723 def Table(self):
227 jan 2605 """Return the table containing the attribute data."""
228     return self._table
229 bh 723
230     def Shapefile(self):
231 bh 984 """Return the shapefile object"""
232 bh 723 return self.shapefile
233 bh 765
234 bh 984 def FileType(self):
235     """Return the filetype. This is always the string 'shapefile'"""
236     return "shapefile"
237    
238 bh 1535 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 bh 1559 def RawShapeFormat(self):
246     """Return the raw data format of the shape data, i.e. RAW_SHAPEFILE"""
247     return RAW_SHAPEFILE
248    
249 bh 1535 def NumShapes(self):
250 jan 2605 """Return the number of shapes in the shapefile"""
251 bh 1535 return self.numshapes
252    
253 bh 984 def Dependencies(self):
254     """Return the empty tuple.
255    
256     The ShapefileStore doesn't depend on anything else.
257     """
258     return ()
259    
260 bh 1074 def OrigShapeStore(self):
261     """Return None.
262 bh 984
263 bh 1074 The ShapefileStore was not derived from another shapestore.
264     """
265     return None
266    
267 bh 1535 def BoundingBox(self):
268 jan 2605 """Return the bounding box of the shapes in the shapefile.
269 bh 1074
270 bh 1535 The coordinate system used is whatever was used in the shapefile.
271 jan 2605 If the shapefileis empty, return None.
272 bh 1535 """
273 jan 2605 return self._bbox
274 bh 1535
275 bh 1593 def ShapesInRegion(self, bbox):
276     """Return an iterable over the shapes that overlap the bounding box.
277 bh 1535
278 bh 1593 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 jan 2605 shapefile.
281 bh 1535 """
282 bh 1921 # Bind a few globals to locals to make it a bit faster
283     cls = ShapefileShape
284     shapefile = self.shapefile
285    
286 bh 1593 left, bottom, right, top = bbox
287     for i in self.shapetree.find_shapes((left, bottom), (right, top)):
288 bh 1921 yield cls(shapefile, i)
289 bh 1535
290 bh 1593 def AllShapes(self):
291 jan 2605 """Return an iterable over the shapes in the shapefile."""
292 bh 1593 for i in xrange(self.NumShapes()):
293     yield ShapefileShape(self.shapefile, i)
294    
295 bh 1535 def Shape(self, index):
296     """Return the shape with index index"""
297 bh 1559 return ShapefileShape(self.shapefile, index)
298 bh 1535
299    
300 bh 984 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 jonathan 1261
310     Raises ValueError if the number of shapes in the shapestore
311     is different from the number of rows in the table.
312 bh 984 """
313 jonathan 1261
314     numShapes = shapestore.Shapefile().info()[0]
315     if numShapes != table.NumRows():
316     raise ValueError(_("Table not compatible with shapestore."))
317    
318 bh 984 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 bh 1074 """Return the shapefile of the underlying shapestore"""
327 bh 984 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 bh 1074 def OrigShapeStore(self):
334     """
335     Return the original shapestore the derived store was instantiated with
336     """
337     return self.shapestore
338 bh 1535
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 bh 1593 def AllShapes(self):
352 jan 2605 """Return an iterable over the shapes in the shapefile.
353 bh 1593
354     This method is simply delegated to the shapestore the
355     DerivedShapeStore was instantiated with.
356     """
357     return self.shapestore.AllShapes()
358    
359 bh 1535 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 bh 1564 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 bh 1535 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()

Properties

Name Value
svn:eol-style native
svn:keywords Author Date Id Revision

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26