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

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

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

revision 628 by jonathan, Wed Apr 9 10:09:15 2003 UTC revision 1043 by bh, Mon May 26 19:27:15 2003 UTC
# Line 1  Line 1 
1  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH  # Copyright (c) 2001, 2002, 2003 by Intevation GmbH
2  # Authors:  # Authors:
3  # Bernhard Herzog <[email protected]>  # 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)  # 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.
# Line 11  Classes for handling tables of data. Line 13  Classes for handling tables of data.
13    
14  __version__ = "$Revision$"  __version__ = "$Revision$"
15    
16    import os
17    import inspect
18    import warnings
19    
20    from base import TitledObject
21    
22  import dbflib  import dbflib
23    
24  # the field types supported by a Table instance.  # the field types supported by a Table instance.
# Line 24  dbflib_fieldtypes = {dbflib.FTString: FI Line 32  dbflib_fieldtypes = {dbflib.FTString: FI
32                       dbflib.FTInteger: FIELDTYPE_INT,                       dbflib.FTInteger: FIELDTYPE_INT,
33                       dbflib.FTDouble: FIELDTYPE_DOUBLE}                       dbflib.FTDouble: FIELDTYPE_DOUBLE}
34    
 class Table:  
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      """      """
     Represent a table of data.  
105    
106      Currently this is basically just a wrapper around dbflib.      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:      # Implementation strategy regarding writing to a DBF file:
# Line 48  class Table: Line 133  class Table:
133    
134      def __init__(self, filename):      def __init__(self, filename):
135          self.filename = filename          self.filename = filename
136            title = os.path.basename(self.filename)
137            TitledObject.__init__(self, title)
138          self.dbf = dbflib.DBFFile(filename)          self.dbf = dbflib.DBFFile(filename)
139    
140          # If true, self.dbf is open for writing.          # If true, self.dbf is open for writing.
141          self._writable = 0          self._writable = 0
142    
143      def Destroy(self):          # Create the column information objects
144          self.dbf.close()          self.columns = []
145          self.dbf = None          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 record_count(self):      def NumRows(self):
156          """Return the number of records"""          """Return the number of rows in the table"""
157          return self.dbf.record_count()          return self.dbf.record_count()
158    
159      def field_count(self):      def NumColumns(self):
160          """Return the number of fields in a record"""          """Return the number of columns in the table"""
161          return self.dbf.field_count()          return self.dbf.field_count()
162    
163      def field_info(self, field):      def Columns(self):
164          """Return a tuple (type, name, width, prec) for the field no. field          """Return the table's colum definitions
165    
166          type is the data type of the field, name the name, width the          The return value is a sequence of DBFColumn instances, one for
167          field width in characters and prec the decimal precision.          each column.
168          """          """
169          type, name, width, prec = self.dbf.field_info(field)          return self.columns
         type = dbflib_fieldtypes[type]  
         return type, name, width, prec  
   
     def field_info_by_name(self, fieldName):  
         count = self.field_count()  
   
         for i in range(count):  
             info = self.field_info(i)  
             if info[1] == fieldName:  
                 return info  
170    
171          return None      def Column(self, col):
172            """Return information about the column given by its name or index
173    
174      def field_range(self, fieldName):          The returned object is an instance of DBFColumn
175          """Finds the first occurences of the minimum and maximum values          """
176          in the table for the given field.          return self.column_map[col]
177    
178          This assumes that the standard comparison operators (<, >, etc.)      def HasColumn(self, col):
179          will work for the given data.          """Return whether the table has a column with the given name or index
180            """
181            return self.column_map.has_key(col)
182    
183          Returns a tuple ((min, rec), (max, rec)) where:      def ReadRowAsDict(self, row):
184              min is the minimum value          """Return the entire row as a dictionary with column names as keys"""
185              max is the maximum value          return self.dbf.read_record(row)
             rec is the record number where the value was found. One  
                 should check that the record number of min is not  
                 the same as the record number of max.  
186    
187          Returns None if there are no records      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          count = self.record_count()          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:          if count == 0:
203              return None              return None
204    
205          rec = self.read_record(0)          min = max = self.ReadValue(0, col)
   
         min = rec[fieldName]  
         min_rec = 0  
   
         max = rec[fieldName]  
         max_rec = 0  
   
206          for i in range(1, count):          for i in range(1, count):
207              rec = self.read_record(i)              value = self.ReadValue(i, col)
208              data = rec[fieldName]              if value < min:
209                    min = value
210                elif value > max:
211                    max = value
212    
213              if data < min:          return (min, max)
                 min = data  
                 min_rec = rec  
             elif data > max:  
                 max = data  
                 max_rec = rec  
   
         return ((min, min_rec), (max, max_rec))  
   
     def GetUniqueValues(self, fieldName):  
         """Return a list of all unique entries in the table for the given  
         field name.  
         """  
214    
215        def UniqueValues(self, col):
216            """Return a sorted list of all unique values in the column col"""
217          dict = {}          dict = {}
218    
219          for i in range(0, self.record_count()):          for i in range(self.NumRows()):
220              rec = self.read_record(i)              value = self.ReadValue(i, col)
221              data = rec[fieldName]              dict[value] = 0
222    
223              if not dict.has_key(data):          values = dict.keys()
224                  dict[data] = 0          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          return dict.keys()      def Destroy(self):
238            self.dbf.close()
239      def read_record(self, record):          self.dbf = None
         """Return the record no. record as a dict mapping field names to values  
         """  
         return self.dbf.read_record(record)  
240    
241      def write_record(self, record, values):      def write_record(self, record, values):
242          """Write the values into the record          """Write the values into the record
# Line 172  class Table: Line 259  class Table:
259          self.dbf.write_record(record, values)          self.dbf.write_record(record, values)
260          self.dbf.commit()          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    

Legend:
Removed from v.628  
changed lines
  Added in v.1043

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26