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,7 +230,7 @@ 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,7 +246,7 @@ def __init__(self, filename, mode='r', mmap=None, version=1):
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,11 +359,13 @@ def createVariable(self, name, type, dimensions):
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,7 +458,7 @@ def _write_var_metadata(self, name):
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,7 +518,7 @@ def _write_var_data(self, name):
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,11 +534,7 @@ def _write_values(self, values):
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,7 +572,8 @@ def _read_numrecs(self):
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,7 +588,8 @@ def _read_gatt_array(self):
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,7 +600,8 @@ def _read_att_array(self):
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,7 +659,7 @@ def _read_var_array(self):
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,11 +703,7 @@ def _read_var(self):
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,7 +718,7 @@ def _read_values(self):
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,6 +782,8 @@ class netcdf_variable(object):
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,9 +807,10 @@ class netcdf_variable(object):
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,10 +828,23 @@ def __setattr__(self, attr, value):
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,6 +892,18 @@ def typecode(self):
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
0 commit comments