SAVENC Interface to low level functions to write data to a NetCDF file. Syntax: SAVENC(VAR_DATA, VAR_META, GLOBAL_META) SAVENC(VAR_DATA, VAR_META, GLOBAL_META, FILENAME) Description: SAVENC(VAR_DATA, VAR_META, GLOBAL_META) creates a NetCDF file according to global properties given in struct GLOBAL_META with the variables defined by the structs VAR_META and VAR_DATA. GLOBAL_META is struct with the following fields: DIMENSIONS: struct array describing the dimensions with fields: NAME: string with the name of the dimension. LENGTH: number with the length of the dimension, or 0 to indicate a record dimension. ATTRIBUTES: struct array with global attributes with fields: NAME: string with the name of the attribute. VALUE: arbitrary typed value with the value of the attribute. NAME: string with the name of the NetCDF file to be written. For every field in struct VAR_DATA a variable is created with the values in the field value. VAR_META should have a field with the same name containing the metadata for that variable in a struct with fields: DIMENSIONS: (mandatory) string cell array with the name of the dimensions of the variable. ATTRIBUTES: (optional) struct array with fields 'NAME' and 'VALUE' specifying the attributes of the variable. DATATYPE: (optional) string with the NetCDF data type of the variable. Allowed types are 'double', 'float', 'int', 'short', 'byte', or 'char'. If this field is missing, the type is derived from the class of the data, and if it is not valid the default data type 'double' is used. NAME: (optional) string with the variable name as it should appear in the NetCDF file. If this field is missing the variable is named after the field name. This is useful when the desired variable name can not be used as field name. SAVENC(VAR_DATA, VAR_META, GLOBAL_META, FILENAME) will create a NetCDF file named FILENAME, overriding the 'NAME' field in GLOBAL_META. Notes: Fill value and scale conversions are always performed. Only variables present in both VAR_DATA and VAR_META are written in to the NetCDF file. Any field in VAR_DATA not present in VAR_META and viceversa is silently omitted. It would be more convenient to specify attributes and dimensions in structs with attribute and dimension names as field names, and attribute values and dimension lengths as field values. But due to a MATLAB limitation, it will cause trouble with attributes like '_FillValue' (because it is not a valid field name). Examples: global_meta = struct() global_meta.name = 'random.nc' global_meta.dimensions = ... struct('name', {'dim1' 'dim2' 'dim3'}, 'length', {0 5 10}) var_meta = struct() var_meta.rand_num = struct('dimensions', {{}}) var_meta.rand_vec = struct('dimensions', {{'dim1'}}) var_meta.rand_mat = struct('dimensions', {{'dim2' 'dim3'}}, ... 'datatype', {'int'}) var_data = struct() var_data.rand_num = int8(round(12 * rand([1 1]))) var_data.rand_vec = randn([25 1]) var_data.rand_mat = round(12 * rand([5 10])) var_data.rand_mat(var_data.rand_mat == 1) = nan savenc(var_data, var_meta, global_meta) filename = 'random_with_atts.nc' var_meta.rand_num.attributes = ... struct('name', {'comment'}, ... 'value', {'This is a random signed 8 bit integer'}) var_meta.rand_vec.attributes = ... struct('name', {'comment', 'add_offset', 'scale_factor'}, ... 'value', {'This is a random vector', 10, 2.5}) var_meta.rand_mat.attributes = ... struct('name', {'comment', '_FillValue'}, ... 'value', {'This is a random matrix', intmax()}) global_meta.attributes = ... struct('name', {'creation_date'}, 'value', {datestr(now)}) savenc(var_data, var_meta, global_meta, filename) See also: LOADNC Authors: Joan Pau Beltran <joanpau.beltran@socib.cat>
0001 function savenc(var_data, var_meta, global_meta, filename) 0002 %SAVENC Interface to low level functions to write data to a NetCDF file. 0003 % 0004 % Syntax: 0005 % SAVENC(VAR_DATA, VAR_META, GLOBAL_META) 0006 % SAVENC(VAR_DATA, VAR_META, GLOBAL_META, FILENAME) 0007 % 0008 % Description: 0009 % SAVENC(VAR_DATA, VAR_META, GLOBAL_META) creates a NetCDF file according to 0010 % global properties given in struct GLOBAL_META with the variables defined by 0011 % the structs VAR_META and VAR_DATA. 0012 % GLOBAL_META is struct with the following fields: 0013 % DIMENSIONS: struct array describing the dimensions with fields: 0014 % NAME: string with the name of the dimension. 0015 % LENGTH: number with the length of the dimension, or 0 to indicate 0016 % a record dimension. 0017 % ATTRIBUTES: struct array with global attributes with fields: 0018 % NAME: string with the name of the attribute. 0019 % VALUE: arbitrary typed value with the value of the attribute. 0020 % NAME: string with the name of the NetCDF file to be written. 0021 % For every field in struct VAR_DATA a variable is created with the values in 0022 % the field value. VAR_META should have a field with the same name containing 0023 % the metadata for that variable in a struct with fields: 0024 % DIMENSIONS: (mandatory) string cell array with the name of the dimensions 0025 % of the variable. 0026 % ATTRIBUTES: (optional) struct array with fields 'NAME' and 'VALUE' 0027 % specifying the attributes of the variable. 0028 % DATATYPE: (optional) string with the NetCDF data type of the variable. 0029 % Allowed types are 'double', 'float', 'int', 'short', 'byte', or 'char'. 0030 % If this field is missing, the type is derived from the class of the 0031 % data, and if it is not valid the default data type 'double' is used. 0032 % NAME: (optional) string with the variable name as it should appear in the 0033 % NetCDF file. If this field is missing the variable is named after the 0034 % field name. This is useful when the desired variable name can not be 0035 % used as field name. 0036 % 0037 % SAVENC(VAR_DATA, VAR_META, GLOBAL_META, FILENAME) will create a NetCDF file 0038 % named FILENAME, overriding the 'NAME' field in GLOBAL_META. 0039 % 0040 % Notes: 0041 % Fill value and scale conversions are always performed. 0042 % 0043 % Only variables present in both VAR_DATA and VAR_META are written in to the 0044 % NetCDF file. Any field in VAR_DATA not present in VAR_META and viceversa is 0045 % silently omitted. 0046 % 0047 % It would be more convenient to specify attributes and dimensions in structs 0048 % with attribute and dimension names as field names, and attribute values and 0049 % dimension lengths as field values. But due to a MATLAB limitation, it will 0050 % cause trouble with attributes like '_FillValue' (because it is not a valid 0051 % field name). 0052 % 0053 % Examples: 0054 % global_meta = struct() 0055 % global_meta.name = 'random.nc' 0056 % global_meta.dimensions = ... 0057 % struct('name', {'dim1' 'dim2' 'dim3'}, 'length', {0 5 10}) 0058 % var_meta = struct() 0059 % var_meta.rand_num = struct('dimensions', {{}}) 0060 % var_meta.rand_vec = struct('dimensions', {{'dim1'}}) 0061 % var_meta.rand_mat = struct('dimensions', {{'dim2' 'dim3'}}, ... 0062 % 'datatype', {'int'}) 0063 % var_data = struct() 0064 % var_data.rand_num = int8(round(12 * rand([1 1]))) 0065 % var_data.rand_vec = randn([25 1]) 0066 % var_data.rand_mat = round(12 * rand([5 10])) 0067 % var_data.rand_mat(var_data.rand_mat == 1) = nan 0068 % savenc(var_data, var_meta, global_meta) 0069 % filename = 'random_with_atts.nc' 0070 % var_meta.rand_num.attributes = ... 0071 % struct('name', {'comment'}, ... 0072 % 'value', {'This is a random signed 8 bit integer'}) 0073 % var_meta.rand_vec.attributes = ... 0074 % struct('name', {'comment', 'add_offset', 'scale_factor'}, ... 0075 % 'value', {'This is a random vector', 10, 2.5}) 0076 % var_meta.rand_mat.attributes = ... 0077 % struct('name', {'comment', '_FillValue'}, ... 0078 % 'value', {'This is a random matrix', intmax()}) 0079 % global_meta.attributes = ... 0080 % struct('name', {'creation_date'}, 'value', {datestr(now)}) 0081 % savenc(var_data, var_meta, global_meta, filename) 0082 % 0083 % See also: 0084 % LOADNC 0085 % 0086 % Authors: 0087 % Joan Pau Beltran <joanpau.beltran@socib.cat> 0088 0089 % Copyright (C) 2013-2016 0090 % ICTS SOCIB - Servei d'observacio i prediccio costaner de les Illes Balears 0091 % <http://www.socib.es> 0092 % 0093 % This program is free software: you can redistribute it and/or modify 0094 % it under the terms of the GNU General Public License as published by 0095 % the Free Software Foundation, either version 3 of the License, or 0096 % (at your option) any later version. 0097 % 0098 % This program is distributed in the hope that it will be useful, 0099 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0100 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0101 % GNU General Public License for more details. 0102 % 0103 % You should have received a copy of the GNU General Public License 0104 % along with this program. If not, see <http://www.gnu.org/licenses/>. 0105 0106 % Consider make this variable persistent. 0107 ISOCTAVE = exist('OCTAVE_VERSION','builtin'); 0108 NETCDF_TYPES = {'double' 'float' 'int' 'short' 'byte' 'char'}; 0109 NATIVE_TYPES = {'double' 'single' 'int32' 'int16' 'int8' 'char'}; 0110 0111 error(nargchk(3, 4, nargin, 'struct')); 0112 0113 if nargin < 4 0114 filename = global_meta.name; 0115 end 0116 0117 if ISOCTAVE 0118 % Create the resource. 0119 nc = netcdf(filename, 'c'); 0120 try 0121 % Set global attributes. 0122 if isfield(global_meta, 'attributes') 0123 for global_att = global_meta.attributes(:)' 0124 nc.(global_att.name) = global_att.value; 0125 end 0126 end 0127 % Set dimensions. 0128 if isfield(global_meta, 'dimensions') 0129 for global_dim = global_meta.dimensions(:)' 0130 if isfield(global_dim, 'unlimited') && (global_dim.unlimited) 0131 nc(global_dim.name) = 0; 0132 else 0133 nc(global_dim.name) = global_dim.length; 0134 end 0135 end 0136 end 0137 % Set variable dimensions and attributes, and variable data. 0138 field_name_list = intersect(fieldnames(var_data), fieldnames(var_meta)); 0139 for var_idx = 1:numel(field_name_list) 0140 field_name = field_name_list{var_idx}; 0141 if isfield(var_meta.(field_name), 'name') 0142 var_name = var_meta.(field_name).name; 0143 else 0144 var_name = field_name; 0145 end 0146 if isfield(var_meta.(field_name), 'datatype') 0147 var_type = var_meta.(field_name).datatype; 0148 else 0149 data_type = class(var_data.(field_name)); 0150 data_type_select = strcmp(data_type, NATIVE_TYPES); 0151 if any(data_type_select) 0152 var_type = NETCDF_TYPES{data_type_select}; 0153 else 0154 var_type = 'double'; 0155 end 0156 end 0157 nc_var_type_func = str2func(['nc' var_type]); 0158 nc{var_name} = nc_var_type_func(var_meta.(field_name).dimensions{:}); 0159 if isfield(var_meta.(var_name), 'attributes') 0160 for var_att = var_meta.(var_name).attributes(:)' 0161 nc{var_name}.(var_att.name) = var_att.value; 0162 end 0163 end 0164 end 0165 for var_idx = 1:numel(field_name_list) 0166 field_name = field_name_list{var_idx}; 0167 if isfield(var_meta.(field_name), 'name') 0168 var_name = var_meta.(field_name).name; 0169 else 0170 var_name = field_name; 0171 end 0172 % Set the variable data with fill value and scale handling enabled. 0173 % Give the range for record dimensions. 0174 nc_var = nc{var_name}; 0175 nc_var = ncautonan(nc_var, 1); 0176 nc_var = ncautoscale(nc_var, 1); 0177 nc_var_ranges = arrayfun(@(s)(1:s), size(var_data.(field_name)), ... 0178 'UniformOutput', false); 0179 nc_var(nc_var_ranges{:}) = var_data.(field_name); 0180 end 0181 catch 0182 close(nc); 0183 delete(filename); 0184 rethrow(lasterror()); 0185 end 0186 % Close the resource. 0187 close(nc); 0188 else 0189 % Create empty NetCDF file. 0190 nc_create_empty(filename); 0191 try 0192 % Set global attributes. 0193 if isfield(global_meta, 'attributes') 0194 for global_att = global_meta.attributes(:)' 0195 nc_attput(filename, nc_global, global_att.name, global_att.value); 0196 end 0197 end 0198 % Set dimensions. 0199 if isfield(global_meta, 'dimensions') 0200 for global_dim = global_meta.dimensions(:)' 0201 if isfield(global_dim, 'unlimited') && (global_dim.unlimited) 0202 nc_adddim(filename, global_dim.name, 0); 0203 else 0204 nc_adddim(filename, global_dim.name, global_dim.length); 0205 end 0206 end 0207 end 0208 % Set variable dimensions and attributes, and variable data. 0209 field_name_list = intersect(fieldnames(var_data), fieldnames(var_meta)); 0210 for var_idx = 1:numel(field_name_list) 0211 field_name = field_name_list{var_idx}; 0212 if isfield(var_meta.(field_name), 'name') 0213 var_name = var_meta.(field_name).name; 0214 else 0215 var_name = field_name; 0216 end 0217 if isfield(var_meta.(field_name), 'datatype') 0218 var_type = var_meta.(field_name).datatype; 0219 else 0220 data_type = class(var_data.(field_name)); 0221 data_type_select = strcmp(data_type, NATIVE_TYPES); 0222 if any(data_type_select) 0223 var_type = NETCDF_TYPES{data_type_select}; 0224 else 0225 var_type = 'double'; 0226 end 0227 end 0228 nc_var = struct('Name', {var_name}, ... 0229 'Dimension', {var_meta.(field_name).dimensions}, ... 0230 'Datatype', var_type); 0231 if isfield(var_meta.(field_name), 'attributes') 0232 % Rename fields as required by low level library. 0233 nc_var.Attribute = ... 0234 struct('Name', {var_meta.(field_name).attributes.name}, ... 0235 'Value', {var_meta.(field_name).attributes.value}); 0236 end 0237 nc_addvar(filename, nc_var); 0238 end 0239 for var_idx = 1:numel(field_name_list) 0240 field_name = field_name_list{var_idx}; 0241 if isfield(var_meta.(field_name), 'name') 0242 var_name = var_meta.(field_name).name; 0243 else 0244 var_name = field_name; 0245 end 0246 nc_varput(filename, var_name, var_data.(field_name)) 0247 end 0248 catch exception 0249 delete(filename); 0250 rethrow(exception); 0251 end 0252 end 0253 0254 end