~ubuntu-branches/ubuntu/wily/nibabel/wily-proposed

« back to all changes in this revision

Viewing changes to nibabel/externals/netcdf.py

  • Committer: Package Import Robot
  • Author(s): Yaroslav Halchenko
  • Date: 2012-05-06 12:58:22 UTC
  • mfrom: (1.1.3)
  • Revision ID: package-import@ubuntu.com-20120506125822-3jiwjkmdqcxkrior
Tags: 1.2.0-1
* New upstream release: bugfixes, support for new formats
  (Freesurfer, ECAT, etc), nib-ls tool.
* debian/copyright
  - corrected reference to the format specs
  - points to github's repository as the origin of sources
  - removed lightunit license/copyright -- removed upstream
  - added netcdf module license/copyright terms
* debian/control
  - added python-fuse to Recommends and Build-Depends for dicomfs
  - boosted policy compliance to 3.9.3 (no further changes)
* debian/watch
  - adjusted to download numbered tag

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
modules, allowing these modules to be used interchangeably when working
48
48
with NetCDF files. The major advantage of this module over other
49
49
modules is that it doesn't require the code to be linked to the NetCDF
50
 
libraries as the other modules do.
 
50
C libraries.
51
51
 
52
52
The code is based on the `NetCDF file format specification
53
53
<http://www.unidata.ucar.edu/software/netcdf/docs/netcdf.html>`_. A
91
91
 
92
92
Examples
93
93
--------
94
 
To create a NetCDF file::
 
94
To create a NetCDF file:
95
95
 
96
96
Make a temporary file for testing:
97
97
 
133
133
    >>> del f, time # needed for windows unlink
134
134
    >>> os.unlink(fname)
135
135
    >>> os.rmdir(tmp_pth)
136
 
 
137
 
TODO:
138
 
 * properly implement ``_FillValue``.
139
 
 * implement Jeff Whitaker's patch for masked variables.
140
 
 * fix character variables.
141
 
 * implement PAGESIZE for Python 2.6?
142
136
"""
143
137
 
 
138
 
 
139
 
 
140
#TODO:
 
141
# * properly implement ``_FillValue``.
 
142
# * implement Jeff Whitaker's patch for masked variables.
 
143
# * fix character variables.
 
144
# * implement PAGESIZE for Python 2.6?
 
145
 
144
146
#The Scientific.IO.NetCDF API allows attributes to be added directly to
145
147
#instances of ``netcdf_file`` and ``netcdf_variable``. To differentiate
146
148
#between user-set attributes and instance attributes, user-set attributes
149
151
#``obj.__dict__['key'] = value``, instead of simply ``obj.key = value``;
150
152
#otherwise the key would be inserted into userspace attributes.
151
153
 
152
 
__all__ = ['netcdf_file', 'netcdf_variable']
 
154
 
 
155
__all__ = ['netcdf_file']
153
156
 
154
157
 
155
158
from operator import mul
181
184
            NC_FLOAT:  ('f', 4),
182
185
            NC_DOUBLE: ('d', 8) }
183
186
 
184
 
REVERSE = { 'b': NC_BYTE,
185
 
            'c': NC_CHAR,
186
 
            'h': NC_SHORT,
187
 
            'i': NC_INT,
188
 
            'f': NC_FLOAT,
189
 
            'd': NC_DOUBLE,
 
187
REVERSE = { ('b', 1): NC_BYTE,
 
188
            ('B', 1): NC_CHAR,
 
189
            ('c', 1): NC_CHAR,
 
190
            ('h', 2): NC_SHORT,
 
191
            ('i', 4): NC_INT,
 
192
            ('f', 4): NC_FLOAT,
 
193
            ('d', 8): NC_DOUBLE,
190
194
 
191
195
            # these come from asarray(1).dtype.char and asarray('foo').dtype.char,
192
196
            # used when getting the types from generic attributes.
193
 
            'l': NC_INT,
194
 
            'S': NC_CHAR }
 
197
            ('l', 4): NC_INT,
 
198
            ('S', 1): NC_CHAR }
195
199
 
196
200
 
197
201
class netcdf_file(object):
226
230
 
227
231
    """
228
232
    def __init__(self, filename, mode='r', mmap=None, version=1):
229
 
        ''' Initialize netcdf_file from fileobj (string or file-like)
 
233
        """Initialize netcdf_file from fileobj (str or file-like).
230
234
 
231
235
        Parameters
232
236
        ----------
242
246
           version of netcdf to read / write, where 1 means *Classic
243
247
           format* and 2 means *64-bit offset format*.  Default is 1.  See
244
248
           http://www.unidata.ucar.edu/software/netcdf/docs/netcdf/Which-Format.html#Which-Format
245
 
        '''
 
249
        """
246
250
        if hasattr(filename, 'seek'): # file-like
247
251
            self.fp = filename
248
252
            self.filename = 'None'
355
359
 
356
360
        if isinstance(type, basestring): type = dtype(type)
357
361
        typecode, size = type.char, type.itemsize
 
362
        if (typecode, size) not in REVERSE:
 
363
            raise ValueError("NetCDF 3 does not support type %s" % type)
358
364
        dtype_ = '>%s' % typecode
359
365
        if size > 1: dtype_ += str(size)
360
366
 
361
367
        data = empty(shape_, dtype=dtype_)
362
 
        self.variables[name] = netcdf_variable(data, typecode, shape, dimensions)
 
368
        self.variables[name] = netcdf_variable(data, typecode, size, shape, dimensions)
363
369
        return self.variables[name]
364
370
 
365
371
    def flush(self):
452
458
 
453
459
        self._write_att_array(var._attributes)
454
460
 
455
 
        nc_type = REVERSE[var.typecode()]
 
461
        nc_type = REVERSE[var.typecode(), var.itemsize()]
456
462
        self.fp.write(asbytes(nc_type))
457
463
 
458
464
        if not var.isrec:
512
518
 
513
519
    def _write_values(self, values):
514
520
        if hasattr(values, 'dtype'):
515
 
            nc_type = REVERSE[values.dtype.char]
 
521
            nc_type = REVERSE[values.dtype.char, values.dtype.itemsize]
516
522
        else:
517
523
            types = [
518
524
                    (int, NC_INT),
528
534
                if isinstance(sample, class_): break
529
535
 
530
536
        typecode, size = TYPEMAP[nc_type]
531
 
        if typecode is 'c':
532
 
            dtype_ = '>c'
533
 
        else:
534
 
            dtype_ = '>%s' % typecode
535
 
            if size > 1: dtype_ += str(size)
 
537
        dtype_ = '>%s' % typecode
536
538
 
537
539
        values = asarray(values, dtype=dtype_)
538
540
 
570
572
 
571
573
    def _read_dim_array(self):
572
574
        header = self.fp.read(4)
573
 
        assert header in [ZERO, NC_DIMENSION]
 
575
        if not header in [ZERO, NC_DIMENSION]:
 
576
            raise ValueError("Unexpected header.")
574
577
        count = self._unpack_int()
575
578
 
576
579
        for dim in range(count):
585
588
 
586
589
    def _read_att_array(self):
587
590
        header = self.fp.read(4)
588
 
        assert header in [ZERO, NC_ATTRIBUTE]
 
591
        if not header in [ZERO, NC_ATTRIBUTE]:
 
592
            raise ValueError("Unexpected header.")
589
593
        count = self._unpack_int()
590
594
 
591
595
        attributes = {}
596
600
 
597
601
    def _read_var_array(self):
598
602
        header = self.fp.read(4)
599
 
        assert header in [ZERO, NC_VARIABLE]
 
603
        if not header in [ZERO, NC_VARIABLE]:
 
604
            raise ValueError("Unexpected header.")
600
605
 
601
606
        begin = 0
602
607
        dtypes = {'names': [], 'formats': []}
654
659
 
655
660
            # Add variable.
656
661
            self.variables[name] = netcdf_variable(
657
 
                    data, typecode, shape, dimensions, attributes)
 
662
                    data, typecode, size, shape, dimensions, attributes)
658
663
 
659
664
        if rec_vars:
660
665
            # Remove padding when only one record variable.
698
703
        begin = [self._unpack_int, self._unpack_int64][self.version_byte-1]()
699
704
 
700
705
        typecode, size = TYPEMAP[nc_type]
701
 
        if typecode is 'c':
702
 
            dtype_ = '>c'
703
 
        else:
704
 
            dtype_ = '>%s' % typecode
705
 
            if size > 1: dtype_ += str(size)
 
706
        dtype_ = '>%s' % typecode
706
707
 
707
708
        return name, dimensions, shape, attributes, typecode, size, dtype_, begin, vsize
708
709
 
717
718
        self.fp.read(-count % 4)  # read padding
718
719
 
719
720
        if typecode is not 'c':
720
 
            values = fromstring(values, dtype='>%s%d' % (typecode, size))
 
721
            values = fromstring(values, dtype='>%s' % typecode)
721
722
            if values.shape == (1,): values = values[0]
722
723
        else:
723
724
            values = values.rstrip(asbytes('\x00'))
781
782
        Typically, this is initialized as empty, but with the proper shape.
782
783
    typecode : dtype character code
783
784
        Desired data-type for the data array.
 
785
    size : int
 
786
        Desired element size for the data array.
784
787
    shape : sequence of ints
785
788
        The shape of the array.  This should match the lengths of the
786
789
        variable's dimensions.
804
807
    isrec, shape
805
808
 
806
809
    """
807
 
    def __init__(self, data, typecode, shape, dimensions, attributes=None):
 
810
    def __init__(self, data, typecode, size, shape, dimensions, attributes=None):
808
811
        self.data = data
809
812
        self._typecode = typecode
 
813
        self._size = size
810
814
        self._shape = shape
811
815
        self.dimensions = dimensions
812
816
 
824
828
        self.__dict__[attr] = value
825
829
 
826
830
    def isrec(self):
 
831
        """Returns whether the variable has a record dimension or not.
 
832
 
 
833
        A record dimension is a dimension along which additional data could be
 
834
        easily appended in the netcdf data structure without much rewriting of
 
835
        the data file. This attribute is a read-only property of the
 
836
        `netcdf_variable`.
 
837
 
 
838
        """
827
839
        return self.data.shape and not self._shape[0]
828
840
    isrec = property(isrec)
829
841
 
830
842
    def shape(self):
 
843
        """Returns the shape tuple of the data variable.
 
844
 
 
845
        This is a read-only attribute and can not be modified in the
 
846
        same manner of other numpy arrays.
 
847
        """
831
848
        return self.data.shape
832
849
    shape = property(shape)
833
850
 
875
892
        """
876
893
        return self._typecode
877
894
 
 
895
    def itemsize(self):
 
896
        """
 
897
        Return the itemsize of the variable.
 
898
 
 
899
        Returns
 
900
        -------
 
901
        itemsize : int
 
902
            The element size of the variable (eg, 8 for float64).
 
903
 
 
904
        """
 
905
        return self._size
 
906
 
878
907
    def __getitem__(self, index):
879
908
        return self.data[index]
880
909