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

Contents of /branches/WIP-pyshapelib-bramz/Thuban/Model/table.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1043 - (show annotations)
Mon May 26 19:27:15 2003 UTC (21 years, 9 months ago) by bh
Original Path: trunk/thuban/Thuban/Model/table.py
File MIME type: text/x-python
File size: 13937 byte(s)
Remove the Precision methods again. They're too DBF specific to be
part of the table interface and they're only used in table_to_dbf
anyway.

* Thuban/Model/transientdb.py (TransientTableBase.Width):Use a
fixed precision of 12 for doubles.
(TransientTableBase.Precision): Removed
(AutoTransientTable.Width): Delegate to self.table.

* Thuban/Model/table.py (DBFTable.Precision)
(MemoryTable.Precision): Removed.
(MemoryTable.Width): Use a fixed precision of 12 for doubles.
(table_to_dbf): Use a fixed precision of 12 for floats unless the
column object specifies something else.

1 # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2 # Authors:
3 # Bernhard Herzog <[email protected]>
4 # Jan-Oliver Wagner <[email protected]>
5 # Frank Koormann <[email protected]>
6 #
7 # This program is free software under the GPL (>=v2)
8 # Read the file COPYING coming with Thuban for details.
9
10 """
11 Classes for handling tables of data.
12 """
13
14 __version__ = "$Revision$"
15
16 import os
17 import inspect
18 import warnings
19
20 from base import TitledObject
21
22 import dbflib
23
24 # the field types supported by a Table instance.
25 FIELDTYPE_INT = "int"
26 FIELDTYPE_STRING = "string"
27 FIELDTYPE_DOUBLE = "double"
28
29
30 # map the dbflib constants for the field types to our constants
31 dbflib_fieldtypes = {dbflib.FTString: FIELDTYPE_STRING,
32 dbflib.FTInteger: FIELDTYPE_INT,
33 dbflib.FTDouble: FIELDTYPE_DOUBLE}
34
35
36 class OldTableInterfaceMixin:
37
38 """Mixin to implement the old table interface using the new one"""
39
40 def __deprecation_warning(self):
41 """Issue a DeprecationWarning for code hat uses the old interface"""
42 callername = inspect.currentframe().f_back.f_code.co_name
43 warnings.warn("The %s method of the old table interface"
44 " is deprecated" % callername,
45 DeprecationWarning, stacklevel = 3)
46
47 def record_count(self):
48 self.__deprecation_warning()
49 return self.NumRows()
50
51 def field_count(self):
52 self.__deprecation_warning()
53 return self.NumColumns()
54
55 def field_info(self, field):
56 """Return a tuple (type, name, width, prec) for the field no. field
57
58 type is the data type of the field, name the name, width the
59 field width in characters and prec the decimal precision. width
60 and prec will be zero if the information returned by the Column
61 method doesn't provide values for them.
62 """
63 self.__deprecation_warning()
64 col = self.Column(field)
65 return (col.type, col.name,
66 getattr(col, "width", 0), getattr(col, "prec", 0))
67
68 def field_info_by_name(self, col):
69 self.__deprecation_warning()
70 try:
71 return self.field_info(col)
72 except KeyError:
73 # FIXME: It may be that field_info raises other exceptions
74 # when the name is not a valid column name.
75 return None
76
77 def field_range(self, fieldName):
78 self.__deprecation_warning()
79 min, max = self.ValueRange(fieldName)
80 return ((min, None), (max, None))
81
82 def GetUniqueValues(self, field):
83 self.__deprecation_warning()
84 return self.UniqueValues(field)
85
86 def read_record(self, r):
87 self.__deprecation_warning()
88 return self.ReadRowAsDict(r)
89
90
91
92 class DBFColumn:
93
94 """Description of a column in a DBFTable
95
96 Instances have the following public attributes:
97
98 name -- Name of the column
99 type -- Type of the column (one of FIELDTYPE_STRING, FIELDTYPE_INT or\
100 FIELDTYPE_DOUBLE)
101 index -- The index of the column
102 width -- the width of the data in the column
103 prec -- The precision of the data (only valid for type == FIELDTYPE_DOUBLE)
104 """
105
106 def __init__(self, name, type, width, prec, index):
107 self.name = name
108 self.type = type
109 self.width = width
110 self.prec = prec
111 self.index = index
112
113
114 class DBFTable(TitledObject, OldTableInterfaceMixin):
115
116 """
117 Table interface for the data in a DBF file
118 """
119
120 # Implementation strategy regarding writing to a DBF file:
121 #
122 # Most of the time Thuban only needs to read from a table and it is
123 # important that Thuban can work with read-only files. Therefore the
124 # DBF file is opened only for reading initially. Only when
125 # write_record is called we try to open the DBF file for writing as
126 # well. If that succeeds the read/write DBF file will be used for
127 # all IO afterwards.
128 #
129 # It's important to use the same DBF file object for both reading
130 # and writing to make sure that reading a records after writing
131 # returns the new values. With two separate objects this wouldn't
132 # work because a DBF file object buffers some data
133
134 def __init__(self, filename):
135 self.filename = filename
136 title = os.path.basename(self.filename)
137 TitledObject.__init__(self, title)
138 self.dbf = dbflib.DBFFile(filename)
139
140 # If true, self.dbf is open for writing.
141 self._writable = 0
142
143 # Create the column information objects
144 self.columns = []
145 self.column_map = {}
146 for i in range(self.NumColumns()):
147 ftype, name, width, prec = self.dbf.field_info(i)
148 ftype = dbflib_fieldtypes[ftype]
149 index = len(self.columns)
150 col = DBFColumn(name, ftype, width, prec, index)
151 self.columns.append(col)
152 self.column_map[name] = col
153 self.column_map[index] = col
154
155 def NumRows(self):
156 """Return the number of rows in the table"""
157 return self.dbf.record_count()
158
159 def NumColumns(self):
160 """Return the number of columns in the table"""
161 return self.dbf.field_count()
162
163 def Columns(self):
164 """Return the table's colum definitions
165
166 The return value is a sequence of DBFColumn instances, one for
167 each column.
168 """
169 return self.columns
170
171 def Column(self, col):
172 """Return information about the column given by its name or index
173
174 The returned object is an instance of DBFColumn
175 """
176 return self.column_map[col]
177
178 def HasColumn(self, col):
179 """Return whether the table has a column with the given name or index
180 """
181 return self.column_map.has_key(col)
182
183 def ReadRowAsDict(self, row):
184 """Return the entire row as a dictionary with column names as keys"""
185 return self.dbf.read_record(row)
186
187 def ReadValue(self, row, col):
188 """Return the value of the specified row and column
189
190 The col parameter may be the index of the column or its name.
191 """
192 return self.dbf.read_record(row)[self.column_map[col].name]
193
194 def ValueRange(self, col):
195 """Return the minimum and maximum values of the values in the column
196
197 The return value is a tuple (min, max) unless the table is empty
198 in which case the return value is None.
199 """
200 count = self.NumRows()
201
202 if count == 0:
203 return None
204
205 min = max = self.ReadValue(0, col)
206 for i in range(1, count):
207 value = self.ReadValue(i, col)
208 if value < min:
209 min = value
210 elif value > max:
211 max = value
212
213 return (min, max)
214
215 def UniqueValues(self, col):
216 """Return a sorted list of all unique values in the column col"""
217 dict = {}
218
219 for i in range(self.NumRows()):
220 value = self.ReadValue(i, col)
221 dict[value] = 0
222
223 values = dict.keys()
224 values.sort()
225 return values
226
227 def Dependencies(self):
228 """Return an empty sequence. The DBFTable doesn't depend on anything"""
229 return ()
230
231 # DBF specific interface parts.
232
233 def Width(self, col):
234 """Return column width"""
235 return self.column_map[col].width
236
237 def Destroy(self):
238 self.dbf.close()
239 self.dbf = None
240
241 def write_record(self, record, values):
242 """Write the values into the record
243
244 The values parameter may either be a dictionary or a sequence.
245
246 If it's a dictionary the keys must be the names of the fields
247 and their value must have a suitable type. Only the fields
248 actually contained in the dictionary are written. Fields for
249 which there's no item in the dict are not modified.
250
251 If it's a sequence, all fields must be present in the right
252 order.
253 """
254 if not self._writable:
255 new_dbf = dbflib.DBFFile(self.filename, "r+b")
256 self.dbf.close()
257 self.dbf = new_dbf
258 self._writable = 1
259 self.dbf.write_record(record, values)
260 self.dbf.commit()
261
262 def FileName(self):
263 """Return the filename the DBFTable was instantiated with"""
264 return self.filename
265
266
267 class MemoryColumn:
268
269 def __init__(self, name, type, index):
270 self.name = name
271 self.type = type
272 self.index = index
273
274 class MemoryTable(TitledObject, OldTableInterfaceMixin):
275
276 """Very simple table implementation that operates on a list of tuples"""
277
278 def __init__(self, fields, data):
279 """Initialize the MemoryTable
280
281 Parameters:
282 fields -- List of (name, field_type) pairs
283 data -- List of tuples, one for each row of data
284 """
285 self.data = data
286 title = 'MemoryTable'
287 TitledObject.__init__(self, title)
288
289 # Create the column information objects
290 self.columns = []
291 self.column_map = {}
292 for name, ftype in fields:
293 index = len(self.columns)
294 col = MemoryColumn(name, ftype, index)
295 self.columns.append(col)
296 self.column_map[name] = col
297 self.column_map[index] = col
298
299 def NumColumns(self):
300 """Return the number of columns in the table"""
301 return len(self.columns)
302
303 def Column(self, col):
304 """Return information about the column given by its name or index
305
306 The returned object is an instance of MemoryColumn.
307 """
308 return self.column_map[col]
309
310 def Columns(self):
311 """Return the table's colum definitions
312
313 The return value is a sequence of MemoryColumn instances, one
314 for each column.
315 """
316 return self.columns
317
318 def HasColumn(self, col):
319 """Return whether the table has a column with the given name or index
320 """
321 return self.column_map.has_key(col)
322
323 def NumRows(self):
324 """Return the number of rows in the table"""
325 return len(self.data)
326
327 def ReadValue(self, row, col):
328 """Return the value of the specified row and column
329
330 The col parameter may be the index of the column or its name.
331 """
332 return self.data[row][self.column_map[col].index]
333
334 def ReadRowAsDict(self, index):
335 """Return the entire row as a dictionary with column names as keys"""
336 return dict([(col.name, self.data[index][col.index])
337 for col in self.columns])
338
339 def ValueRange(self, col):
340 """Return the minimum and maximum values of the values in the column
341
342 The return value is a tuple (min, max) unless the table is empty
343 in which case the return value is None.
344 """
345
346 index = self.column_map[col].index
347 values = [row[index] for row in self.data]
348 if not values:
349 return None
350
351 return min(values), max(values)
352
353 def UniqueValues(self, col):
354 """Return a sorted list of all unique values in the column col
355
356 col can be either column index or name.
357 """
358 dict = {}
359
360 for i in range(self.NumRows()):
361 value = self.ReadValue(i, col)
362 dict[value] = 0
363
364 values = dict.keys()
365 values.sort()
366 return values
367
368 def Width(self, col):
369 """Return the maximum width of values in the column
370
371 The return value is the the maximum length of string
372 representation of the values in the column (represented by index
373 or name).
374 """
375 max = 0
376
377 type = self.column_map[col].type
378 index = self.column_map[col].index
379 values = [row[index] for row in self.data]
380 if not values:
381 return None
382
383 if type == FIELDTYPE_DOUBLE:
384 format = "%.12f"
385 elif type == FIELDTYPE_INT:
386 format = "%d"
387 else:
388 format = "%s"
389 for value in values:
390 l = len(format % value)
391 if l > max:
392 max = l
393
394 return max
395
396 def Dependencies(self):
397 """Return an empty sequence. The MemoryTable doesn't depend on anything
398 """
399 return ()
400
401 def write_record(self, record, values):
402 # TODO: Check for correct lenght and perhaps also
403 # for correct types in case values is a tuple. How to report problems?
404 # TODO: Allow values to be a dictionary and write the single
405 # fields that are specified.
406 self.data[record] = values
407
408
409 def table_to_dbf(table, filename):
410 """Create the dbf file filename from the table"""
411 dbf = dbflib.create(filename)
412
413 dbflib_fieldtypes = {FIELDTYPE_STRING: dbflib.FTString,
414 FIELDTYPE_INT: dbflib.FTInteger,
415 FIELDTYPE_DOUBLE: dbflib.FTDouble}
416
417 # Initialise the header. Distinguish between DBFTable and others.
418 for col in table.Columns():
419 width = table.Width(col.name)
420 if col.type == FIELDTYPE_DOUBLE:
421 prec = getattr(col, "prec", 12)
422 else:
423 prec = 0
424 dbf.add_field(col.name, dbflib_fieldtypes[col.type], width, prec)
425
426 for i in range(table.NumRows()):
427 record = table.ReadRowAsDict(i)
428 dbf.write_record(i, record)
429 dbf.close()
430
431 def table_to_csv(table, filename):
432 """Export table to csv file."""
433
434 file = open(filename,"w")
435 columns = table.Columns()
436 if columns:
437 header = "#%s" % columns[0].name
438 for col in columns[1:]:
439 header = header + ",%s" % col.name
440 header = header + "\n"
441 file.write(header)
442
443 for i in range(table.NumRows()):
444 record = table.ReadRowAsDict(i)
445 if len(record):
446 line = "%s" % record[columns[0].name]
447 for col in columns[1:]:
448 line = line + ",%s" % record[col.name]
449 line = line + "\n"
450 file.write(line)
451 file.close()
452

Properties

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26