1## Copyright (C) 2013 Alexander Barth
2##
3## This program is free software; you can redistribute it and/or modify
4## it under the terms of the GNU General Public License as published by
5## the Free Software Foundation; either version 2 of the License, or
6## (at your option) any later version.
7##
8## This program is distributed in the hope that it will be useful,
9## but WITHOUT ANY WARRANTY; without even the implied warranty of
10## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11## GNU General Public License for more details.
12##
13## You should have received a copy of the GNU General Public License
14## along with this program; If not, see <http://www.gnu.org/licenses/>.
15
16## -*- texinfo -*-
17## @deftypefn  {Function File} {@var{info} =} ncinfo (@var{filename})
18## @deftypefnx  {Function File} {@var{info} =} ncinfo (@var{filename}, @var{varname})
19## @deftypefnx  {Function File} {@var{info} =} ncinfo (@var{filename}, @var{groupname})
20## return information about an entire NetCDF file @var{filename} (i.e. the root
21## group "/"), about the variable called @var{varname} or the group called
22## @var{groupname}.
23##
24## The structure @var{info} has always the following fields:
25## @itemize
26## @item @var{Filename}: the name of the NetCDF file
27## @item @var{Format}: one of the strings "CLASSIC", "64BIT", "NETCDF4"
28## or "NETCDF4_CLASSIC"
29## @end itemize
30##
31## The structure @var{info} has additional fields depending on wether a
32## group of variable is queried.
33##
34## Groups
35##
36## Groups are returned as an array structure with the following fields:
37##
38## @itemize
39## @item @var{Name}: the group name. The root group is named "/".
40## @item @var{Dimensions}: a array structure with the dimensions.
41## @item @var{Variables}: a array structure with the variables.
42## @item @var{Attributes}: a array structure with global attributes.
43## @item @var{Groups}: a array structure (one for each group) with the
44## same fields as this structure.
45## @end itemize
46##
47## Dimensions
48##
49## Dimensions are returned as an array structure with the following fields:
50## @itemize
51##   @item @var{Name}: the name of the dimension
52##   @item @var{Length}: the length of the dimension
53##   @item @var{Unlimited}: true of the dimension has no fixed limited, false
54## @end itemize
55##
56## Variables
57##
58## Variables are returned as an array structure with the following fields:
59## @itemize
60##   @item @var{Name}: the name of the dimension
61##   @item @var{Dimensions}: array structure of all dimensions of this variable
62## with the same structure as above.
63##   @item @var{Size}: array with the size of the variable
64##   @item @var{Datatype}: string with the corresponding octave data-type
65## (see below)
66##   @item @var{Attributes}: a array structure of attributes
67##   @item @var{FillValue}: the NetCDF fill value of the variable. If the fill
68## value is not defined, then this attribute is an empty array ([]).
69##   @item @var{DeflateLevel}: the NetCDF deflate level between 0 (no
70##   compression) and 9 (maximum compression).
71##   @item @var{Shuffle}: is true if the shuffle filter is activated to improve
72##   compression, otherwise false.
73##   @item @var{CheckSum}: is set to "fletcher32", if check-sums are used,
74#    otherwise this field is not defined.
75## @end itemize
76##
77## Attributes
78##
79## Attributes are returned as an array structure with the following fields:
80## @itemize
81##   @item @var{Name}: the name of the attribute
82##   @item @var{Value}: the value of the attribute (with the corresponding type)
83##   @item @var{Unlimited}: true of the dimension has no fixed limited, false
84## @end itemize
85##
86## Data-types
87##
88## The following the the correspondence between the Octave and NetCDF
89## data-types:
90##
91## @multitable @columnfractions .5 .5
92## @headitem Octave type @tab NetCDF type
93## @item @code{int8}    @tab @code{NC_BYTE}
94## @item @code{uint8}   @tab @code{NC_UBYTE}
95## @item @code{int16}   @tab @code{NC_SHORT}
96## @item @code{uint16}  @tab @code{NC_USHORT}
97## @item @code{int32}   @tab @code{NC_INT}
98## @item @code{uint32}  @tab @code{NC_UINT}
99## @item @code{int64}   @tab @code{NC_INT64}
100## @item @code{uint64}  @tab @code{NC_UINT64}
101## @item @code{single}  @tab @code{NC_FLOAT}
102## @item @code{double}  @tab @code{NC_DOUBLE}
103## @item @code{char}    @tab @code{NC_CHAR}
104## @end multitable
105##
106## The output of @code{ncinfo} can be used to create a NetCDF file with the same
107## meta-data using @code{ncwriteschema}.
108##
109## Note: If there are no attributes (or variable or groups), the corresponding
110## field is an empty matrix and not an empty struct array for compability
111## with matlab.
112##
113## @seealso{ncread,nccreate,ncwriteschema,ncdisp}
114##
115## @end deftypefn
116
117function info = ncinfo(filename,name)
118
119ncid = netcdf_open(filename,"NC_NOWRITE");
120info.Filename = filename;
121
122if nargin == 1
123  name = "/";
124endif
125
126[gid,varid] = ncloc(ncid,name);
127
128if isempty(varid)
129  info = ncinfo_group(info,gid);
130else
131  unlimdimIDs = netcdf_inqUnlimDims(gid);
132  info = ncinfo_var(info,gid,varid,unlimdimIDs);
133endif
134
135# NetCDF format
136ncformat = netcdf_inqFormat(ncid);
137info.Format = lower(strrep(ncformat,'FORMAT_',''));
138
139netcdf_close(ncid);
140endfunction
141
142function dims = ncinfo_dim(ncid,dimids,unlimdimIDs)
143
144dims = [];
145for i=1:length(dimids)
146  tmp = struct();
147
148  [tmp.Name, tmp.Length] = netcdf_inqDim(ncid,dimids(i));
149  tmp.Unlimited = any(unlimdimIDs == dimids(i));
150
151  if isempty(dims)
152    dims = [tmp];
153  else
154    dims(i) = tmp;
155  endif
156endfor
157endfunction
158
159
160function vinfo = ncinfo_var(vinfo,ncid,varid,unlimdimIDs)
161
162[vinfo.Name,xtype,dimids,natts] = netcdf_inqVar(ncid,varid);
163
164% Information about dimension
165
166vinfo.Dimensions = ncinfo_dim(ncid,dimids,unlimdimIDs);
167if isempty(vinfo.Dimensions)
168  vinfo.Size = [];
169else
170  vinfo.Size = cat(2,vinfo.Dimensions.Length);
171end
172
173% Data type
174
175vinfo.Datatype = nc2octtype(xtype);
176
177% Attributes
178
179vinfo.Attributes = [];
180
181for i = 0:natts-1
182    tmp = struct();
183    tmp.Name = netcdf_inqAttName(ncid,varid,i);
184    tmp.Value = netcdf_getAtt(ncid,varid,tmp.Name);
185
186    if isempty(vinfo.Attributes)
187      vinfo.Attributes = [tmp];
188    else
189      vinfo.Attributes(i+1) = tmp;
190    endif
191endfor
192
193% chunking, fillvalue, compression
194
195[storage,vinfo.ChunkSize] = netcdf_inqVarChunking(ncid,varid);
196
197[nofill,vinfo.FillValue] = netcdf_inqVarFill(ncid,varid);
198if nofill
199  vinfo.FillValue = [];
200endif
201
202[shuffle,deflate,vinfo.DeflateLevel] = ...
203    netcdf_inqVarDeflate(ncid,varid);
204
205if ~deflate
206  vinfo.DeflateLevel = [];
207endif
208vinfo.Shuffle = shuffle;
209
210# add checksum information if defined (unlike matlab)
211checksum = netcdf_inqVarFletcher32(ncid,varid);
212if ~strcmp(checksum,'nochecksum');
213  vinfo.Checksum = checksum;
214endif
215
216endfunction
217
218
219function info = ncinfo_group(info,ncid)
220
221info.Name = netcdf_inqGrpName(ncid);
222unlimdimIDs = netcdf_inqUnlimDims(ncid);
223
224[ndims,nvars,ngatts] = netcdf_inq(ncid);
225
226% dimensions
227
228dimids = netcdf_inqDimIDs(ncid);
229info.Dimensions = ncinfo_dim(ncid,dimids,unlimdimIDs);
230
231% variables
232for i=1:nvars
233  info.Variables(i) = ncinfo_var(struct(),ncid,i-1,unlimdimIDs);
234endfor
235
236% global attributes
237info.Attributes = [];
238gid = netcdf_getConstant('NC_GLOBAL');
239for i = 0:ngatts-1
240  tmp = struct();
241  tmp.Name = netcdf_inqAttName(ncid,gid,i);
242  tmp.Value = netcdf_getAtt(ncid,gid,tmp.Name);
243
244  if isempty(info.Attributes)
245    info.Attributes = [tmp];
246  else
247    info.Attributes(i+1) = tmp;
248  endif
249endfor
250
251info.Groups = [];
252gids = netcdf_inqGrps(ncid);
253for i = 1:length(gids)
254  tmp = ncinfo_group(struct(),gids(i));
255
256  if isempty(info.Groups)
257    info.Groups = [tmp];
258  else
259    info.Groups(i) = tmp;
260  endif
261endfor
262
263endfunction
264