GENERATEOUTPUTNETCDF Generate NetCDF output for glider deployment data. Syntax: NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS) NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS, OPTIONS) NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS, OPT1, VAL1, ...) Description: NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS) calls SAVENC to generate a NetCDF file named FILENAME from deployment data in struct DATA, according to the template defined by variable metadata in VARS, dimension definitions in struct DIMS and global attributes in struct array ATTS, and returns the absolute name of the generated file in string NC. DATA and VARS should be structs with one field per variable with the variable data and the variable metadata respectively, as needed by SAVENC. To allow runtime defined variable metadata, META might be a struct with variable names as field names and runtime defined variable attributes as values. Each field should be a struct with the vattribute names as field names and the attribute values as field values. If the value of a variable attribute in a field of VARS is left undefined (empty) and its name matches a field name of the corresponding variable field in META, the value is overwritten. DIMS should be a struct as needed by SAVENC. To allow runtime defined dimensions and predefined dimensions (useful for the case of string variables), variables may specifye dimensions in VARS which are not defined in DIMS or with undefined length (empty LENGTH field value), and they are inferred from the size of the data values. ATTS should be a struct array as needed by SAVENC, too. To allow runtime defined global attributes, attributes in ATTS whose name matches a field name in struct DEPLOYMENT are overwritten with the field value. In addition, if the following global attributes are present in struct ATTS, they are updated with values computed from data (see options below): DATE_MODIFIED: modification time given by POSIXTIME ('yyyy-mm-ddTHH:MM:SS+00:00'). GEOSPATIAL_LAT_MAX: maximum latitude value inferred from data. GEOSPATIAL_LAT_MIN: minimum latitude value inferred from data. GEOSPATIAL_LAT_UNITS: latitude units given by variable attributes. GEOSPATIAL_LON_MAX: maximum longitude value inferred from data. GEOSPATIAL_LON_MIN: minimum longitude value inferred from data. GEOSPATIAL_LON_UNITS: longitude units given by variable attributes. GEOSPATIAL_VERTICAL_MAX: maximum vertical value inferred from data. GEOSPATIAL_VERTICAL_MIN: minimum vertical value inferred from data. GEOSPATIAL_VERTICAL_UNITS: vertical units given by variable attributes. GEOSPATIAL_VERTICAL_POSITIVE: vertical positive direction given by variable attributes. TIME_COVERAGE_END: maximum time value inferred from data. TIME_COVERAGE_START: minimum time value inferred from data. TIME_COVERAGE_UNITS: time units given by value variable attributes. NC = GENERATEOUTPUTNETCDF(..., OPTIONS) and NC = GENERATEOUTPUTNETCDF(..., OPT1, VAL1, ...) accept the following options given in key-value pairs OPT1, VAL1... or in a struct OPTIONS with field names as option keys and field values as option values, to control the generation of coverage metadata: MODIFIED: modification time stamp. String with the timestamp for the 'date_modified' attribute. Default value: datestr(posixtime2utc(posixtime()), 'yyyy-mm-ddTHH:MM:SS+00:00'); TIME: variable choices for time coverage information. Char array or string cell array with the names of the variables from which to extract the time coverage information, in order of preference. Default value: 'time' TIME_CONVERSION: conversion to POSIX time for time coverage. String cell array of function names or cell array of function handles with the functions to convert time variables in option TIME to POSIX time format (seconds since 1970-01-01 00:00:00 UTC). If a single value is provided, the same conversion is used for all variable choices. If empty, no conversion is applied. TIME_FORMAT: format for time coverage timestamps. String cell array of function names or cell array of function handles with the functions to convert each time variable choice in option TIME to the desired timestamp format (instead of using the numeric values). If a single value is provided, the same format is used for all choices. If empty, no format is applied and the numeric values are used. Default value: @(t)(datestr(posixtime2utc(t), 'yyyy-mm-ddTHH:MM:SS+00:00')) POSITION: variable choice for position coverage information. Two column string cell array with the names of the variables from which to extract the position coverage information (latitude and longitude). Columns correspond to the x and y coordinates respectively. Default value: {'longitude' 'latitude'} POSITION_CONVERSION: conversion to decimal degrees for position coverage. String cell array of function names or cell array of function handles with the functions to convert each choice of position variables in option POSITION to longitude and latitude in decimal degrees (in that order). If a single value is provided, the same conversion is used for all variable choices. If empty, no conversion is applied. Default value: [] (no conversion applied) VERTICAL: variable choice for latitude coverage information. Char array or string cell array with the names of the variables from which to extract the vertical coverage information, in order of preference. Default value: 'depth' VERTICAL_CONVERSION: conversion to meters for vertical coverage. String cell array of function names or cell array of function handles with the functions to convert each choice of vertical coordinate variable in option VERTICAL to meters. If a single value is provided, the same conversion is used for all variable choices. If empty, no conversion is applied. Default value: [] (no conversion applied) VERTICAL_POSITIVE: vertical positive direction. Char array or string cell array with the positive direction of each choice of vertical coordinate variable in option VERTICAL ('up' or 'down'). If a single string is provided, the same direction is assumed for all vertical coordinate variables. Default value: 'down' Notes: Usually input data is the output of LOADSLOCUMDATA or LOADSEAGLIDERDATA, PROCESSGLIDERDATA or GRIDGLIDERDATA. Be aware that only variables present in both structs DATA and VARS are added to the NetCDF file. Any field in DATA not present in VARS is omited. Examples: nc = generateOutputNetCDF(filename, data, deployment, vars, dims, atts) nc = ... generateOutputNetCDF( ... filename, data, deployment, vars, dims, atts, ... 'time', {'m_present_time' 'sci_m_present_time'}, ... 'vertical', {'m_depth' 'sci_water_pressure' 'm_pressure'}, ... 'vertical_positive', 'down', ... 'vertical_conversion', {false, @(z)(z*10), @(z)(z*10)}, ... 'position', {'m_gps_lon' 'm_gps_lat'; 'm_lon' 'm_lat'}, ... 'position_conversion', ... @(x,y)(subsref({nmea2deg(x) nmea2deg(y)}, substruct('{}', {':'})))) See also: SAVENC POSIXTIME2UTC POSIXTIME NMEA2DEG LOADSLOCUMDATA LOADSEAGLIDERDATA PREPROCESSGLIDERDATA PROCESSGLIDERDATA GRIDGLIDERDATA Authors: Joan Pau Beltran <joanpau.beltran@socib.cat>
0001 function nc = generateOutputNetCDF(filename, data, meta, deployment, vars, dims, atts, varargin) 0002 %GENERATEOUTPUTNETCDF Generate NetCDF output for glider deployment data. 0003 % 0004 % Syntax: 0005 % NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS) 0006 % NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS, OPTIONS) 0007 % NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS, OPT1, VAL1, ...) 0008 % 0009 % Description: 0010 % NC = GENERATEOUTPUTNETCDF(FILENAME, DATA, META, DEPLOYMENT, VARS, DIMS, ATTS) 0011 % calls SAVENC to generate a NetCDF file named FILENAME from deployment data 0012 % in struct DATA, according to the template defined by variable metadata in 0013 % VARS, dimension definitions in struct DIMS and global attributes in struct 0014 % array ATTS, and returns the absolute name of the generated file in string 0015 % NC. 0016 % 0017 % DATA and VARS should be structs with one field per variable with the 0018 % variable data and the variable metadata respectively, as needed by SAVENC. 0019 % To allow runtime defined variable metadata, META might be a struct with 0020 % variable names as field names and runtime defined variable attributes as 0021 % values. Each field should be a struct with the vattribute names as field 0022 % names and the attribute values as field values. If the value of a variable 0023 % attribute in a field of VARS is left undefined (empty) and its name matches 0024 % a field name of the corresponding variable field in META, the value is 0025 % overwritten. 0026 % 0027 % DIMS should be a struct as needed by SAVENC. To allow runtime defined 0028 % dimensions and predefined dimensions (useful for the case of string 0029 % variables), variables may specifye dimensions in VARS which are not defined 0030 % in DIMS or with undefined length (empty LENGTH field value), and they are 0031 % inferred from the size of the data values. 0032 % 0033 % ATTS should be a struct array as needed by SAVENC, too. To allow runtime 0034 % defined global attributes, attributes in ATTS whose name matches a field 0035 % name in struct DEPLOYMENT are overwritten with the field value. 0036 % In addition, if the following global attributes are present in struct ATTS, 0037 % they are updated with values computed from data (see options below): 0038 % DATE_MODIFIED: modification time given by POSIXTIME ('yyyy-mm-ddTHH:MM:SS+00:00'). 0039 % GEOSPATIAL_LAT_MAX: maximum latitude value inferred from data. 0040 % GEOSPATIAL_LAT_MIN: minimum latitude value inferred from data. 0041 % GEOSPATIAL_LAT_UNITS: latitude units given by variable attributes. 0042 % GEOSPATIAL_LON_MAX: maximum longitude value inferred from data. 0043 % GEOSPATIAL_LON_MIN: minimum longitude value inferred from data. 0044 % GEOSPATIAL_LON_UNITS: longitude units given by variable attributes. 0045 % GEOSPATIAL_VERTICAL_MAX: maximum vertical value inferred from data. 0046 % GEOSPATIAL_VERTICAL_MIN: minimum vertical value inferred from data. 0047 % GEOSPATIAL_VERTICAL_UNITS: vertical units given by variable attributes. 0048 % GEOSPATIAL_VERTICAL_POSITIVE: vertical positive direction given by variable attributes. 0049 % TIME_COVERAGE_END: maximum time value inferred from data. 0050 % TIME_COVERAGE_START: minimum time value inferred from data. 0051 % TIME_COVERAGE_UNITS: time units given by value variable attributes. 0052 % 0053 % NC = GENERATEOUTPUTNETCDF(..., OPTIONS) and 0054 % NC = GENERATEOUTPUTNETCDF(..., OPT1, VAL1, ...) accept the following 0055 % options given in key-value pairs OPT1, VAL1... or in a struct OPTIONS 0056 % with field names as option keys and field values as option values, 0057 % to control the generation of coverage metadata: 0058 % MODIFIED: modification time stamp. 0059 % String with the timestamp for the 'date_modified' attribute. 0060 % Default value: 0061 % datestr(posixtime2utc(posixtime()), 'yyyy-mm-ddTHH:MM:SS+00:00'); 0062 % TIME: variable choices for time coverage information. 0063 % Char array or string cell array with the names of the variables from 0064 % which to extract the time coverage information, in order of preference. 0065 % Default value: 'time' 0066 % TIME_CONVERSION: conversion to POSIX time for time coverage. 0067 % String cell array of function names or cell array of function handles 0068 % with the functions to convert time variables in option TIME to POSIX 0069 % time format (seconds since 1970-01-01 00:00:00 UTC). If a single value 0070 % is provided, the same conversion is used for all variable choices. 0071 % If empty, no conversion is applied. 0072 % TIME_FORMAT: format for time coverage timestamps. 0073 % String cell array of function names or cell array of function handles 0074 % with the functions to convert each time variable choice in option TIME 0075 % to the desired timestamp format (instead of using the numeric values). 0076 % If a single value is provided, the same format is used for all choices. 0077 % If empty, no format is applied and the numeric values are used. 0078 % Default value: @(t)(datestr(posixtime2utc(t), 'yyyy-mm-ddTHH:MM:SS+00:00')) 0079 % POSITION: variable choice for position coverage information. 0080 % Two column string cell array with the names of the variables from which 0081 % to extract the position coverage information (latitude and longitude). 0082 % Columns correspond to the x and y coordinates respectively. 0083 % Default value: {'longitude' 'latitude'} 0084 % POSITION_CONVERSION: conversion to decimal degrees for position coverage. 0085 % String cell array of function names or cell array of function handles 0086 % with the functions to convert each choice of position variables in 0087 % option POSITION to longitude and latitude in decimal degrees 0088 % (in that order). If a single value is provided, the same conversion 0089 % is used for all variable choices. If empty, no conversion is applied. 0090 % Default value: [] (no conversion applied) 0091 % VERTICAL: variable choice for latitude coverage information. 0092 % Char array or string cell array with the names of the variables from 0093 % which to extract the vertical coverage information, in order of 0094 % preference. 0095 % Default value: 'depth' 0096 % VERTICAL_CONVERSION: conversion to meters for vertical coverage. 0097 % String cell array of function names or cell array of function handles 0098 % with the functions to convert each choice of vertical coordinate 0099 % variable in option VERTICAL to meters. If a single value is provided, 0100 % the same conversion is used for all variable choices. If empty, 0101 % no conversion is applied. 0102 % Default value: [] (no conversion applied) 0103 % VERTICAL_POSITIVE: vertical positive direction. 0104 % Char array or string cell array with the positive direction of each 0105 % choice of vertical coordinate variable in option VERTICAL ('up' or 0106 % 'down'). If a single string is provided, the same direction is assumed 0107 % for all vertical coordinate variables. 0108 % Default value: 'down' 0109 % 0110 % Notes: 0111 % Usually input data is the output of LOADSLOCUMDATA or LOADSEAGLIDERDATA, 0112 % PROCESSGLIDERDATA or GRIDGLIDERDATA. 0113 % 0114 % Be aware that only variables present in both structs DATA and VARS are 0115 % added to the NetCDF file. Any field in DATA not present in VARS is omited. 0116 % 0117 % Examples: 0118 % nc = generateOutputNetCDF(filename, data, deployment, vars, dims, atts) 0119 % nc = ... 0120 % generateOutputNetCDF( ... 0121 % filename, data, deployment, vars, dims, atts, ... 0122 % 'time', {'m_present_time' 'sci_m_present_time'}, ... 0123 % 'vertical', {'m_depth' 'sci_water_pressure' 'm_pressure'}, ... 0124 % 'vertical_positive', 'down', ... 0125 % 'vertical_conversion', {false, @(z)(z*10), @(z)(z*10)}, ... 0126 % 'position', {'m_gps_lon' 'm_gps_lat'; 'm_lon' 'm_lat'}, ... 0127 % 'position_conversion', ... 0128 % @(x,y)(subsref({nmea2deg(x) nmea2deg(y)}, substruct('{}', {':'})))) 0129 % 0130 % See also: 0131 % SAVENC 0132 % POSIXTIME2UTC 0133 % POSIXTIME 0134 % NMEA2DEG 0135 % LOADSLOCUMDATA 0136 % LOADSEAGLIDERDATA 0137 % PREPROCESSGLIDERDATA 0138 % PROCESSGLIDERDATA 0139 % GRIDGLIDERDATA 0140 % 0141 % Authors: 0142 % Joan Pau Beltran <joanpau.beltran@socib.cat> 0143 0144 % Copyright (C) 2013-2016 0145 % ICTS SOCIB - Servei d'observacio i prediccio costaner de les Illes Balears 0146 % <http://www.socib.es> 0147 % 0148 % This program is free software: you can redistribute it and/or modify 0149 % it under the terms of the GNU General Public License as published by 0150 % the Free Software Foundation, either version 3 of the License, or 0151 % (at your option) any later version. 0152 % 0153 % This program is distributed in the hope that it will be useful, 0154 % but WITHOUT ANY WARRANTY; without even the implied warranty of 0155 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0156 % GNU General Public License for more details. 0157 % 0158 % You should have received a copy of the GNU General Public License 0159 % along with this program. If not, see <http://www.gnu.org/licenses/>. 0160 0161 error(nargchk(6, 26, nargin, 'struct')); 0162 0163 %% Set options and default values. 0164 options.modified = ... 0165 datestr(posixtime2utc(posixtime()), 'yyyy-mm-ddTHH:MM:SS+00:00'); 0166 options.time = 'time'; 0167 options.time_conversion = []; 0168 options.time_format = ... 0169 @(t)(datestr(posixtime2utc(t), 'yyyy-mm-ddTHH:MM:SS+00:00')); 0170 options.position = {'longitude' 'latitude'}; 0171 options.position_conversion = []; 0172 options.vertical = 'depth'; 0173 options.vertical_conversion = []; 0174 options.vertical_positive = 'down'; 0175 0176 0177 %% Parse optional arguments. 0178 % Get option key-value pairs in any accepted call signature. 0179 argopts = varargin; 0180 if isscalar(argopts) && isstruct(argopts{1}) 0181 % Options passed as a single option struct argument: 0182 % field names are option keys and field values are option values. 0183 opt_key_list = fieldnames(argopts{1}); 0184 opt_val_list = struct2cell(argopts{1}); 0185 elseif mod(numel(argopts), 2) == 0 0186 % Options passed as key-value argument pairs. 0187 opt_key_list = argopts(1:2:end); 0188 opt_val_list = argopts(2:2:end); 0189 else 0190 error('glider_toolbox:generateOutputNetCDF:InvalidOptions', ... 0191 'Invalid optional arguments (neither key-value pairs nor struct).'); 0192 end 0193 % Overwrite default options with values given in extra arguments. 0194 for opt_idx = 1:numel(opt_key_list) 0195 opt = lower(opt_key_list{opt_idx}); 0196 val = opt_val_list{opt_idx}; 0197 if isfield(options, opt) 0198 options.(opt) = val; 0199 else 0200 error('glider_toolbox:generateOutputNetCDF:InvalidOption', ... 0201 'Invalid option: %s.', opt); 0202 end 0203 end 0204 0205 0206 %% Get dynamic global attribute values. 0207 dyn_atts = struct(); 0208 % Modification date: 0209 dyn_atts.date_modified = options.modified; 0210 % Time coverage: 0211 time_field_list = cellstr(options.time); 0212 time_field_present = ... 0213 isfield(data, time_field_list) & isfield(vars, time_field_list); 0214 if any(time_field_present) 0215 time_field_index = find(time_field_present, 1); 0216 time_field = time_field_list{time_field_index}; 0217 time_units = []; 0218 if iscell(options.time_conversion) 0219 time_func = options.time_conversion{time_field_index}; 0220 else 0221 time_func = options.time_conversion; 0222 end 0223 if isempty(time_func) 0224 time_data = data.(time_field); 0225 if isfield(vars.(time_field), 'attributes') 0226 time_atts = vars.(time_field).attributes; 0227 time_units_select = strcmp('units', {time_atts.name}); 0228 if any(time_units_select) 0229 time_units = time_atts(time_units_select).value; 0230 end 0231 end 0232 else 0233 if ischar(time_func) 0234 time_func = str2func(time_func); 0235 end 0236 time_data = time_func(data.(time_field)); 0237 end 0238 if numel(options.time_format) > 1 0239 time_format_func = options.time_format{time_field_index}; 0240 else 0241 time_format_func = options.time_format; 0242 end 0243 if isempty(time_format_func) 0244 dyn_atts.time_coverage_start = min(time_data); 0245 dyn_atts.time_coverage_end = max(time_data); 0246 if time_units 0247 dyn_atts.time_coverage_units = time_units; 0248 end 0249 else 0250 dyn_atts.time_coverage_start = time_format_func(min(time_data)); 0251 dyn_atts.time_coverage_end = time_format_func(max(time_data)); 0252 end 0253 end 0254 % Geospatial coverage: 0255 position_field_list = cellstr(options.position); 0256 position_field_present = all(isfield(data, position_field_list) ... 0257 & isfield(vars, position_field_list), 2); 0258 if any(position_field_present) 0259 position_field_index = find(position_field_present, 1); 0260 [position_x_field, position_y_field] = ... 0261 position_field_list{position_field_index, :}; 0262 if iscell(options.position_conversion) 0263 position_func = options.position_conversion{position_field_index}; 0264 else 0265 position_func = options.position_conversion; 0266 end 0267 if isempty(position_func) 0268 longitude_data = data.(position_x_field); 0269 latitude_data = data.(position_y_field); 0270 longitude_units = []; 0271 latitude_units = []; 0272 if isfield(vars.(position_x_field), 'attributes') 0273 longitude_atts = vars.(position_x_field).attributes; 0274 longitude_units_select = strcmp('units', {longitude_atts.name}); 0275 if any(longitude_units_select) 0276 longitude_units = longitude_atts(longitude_units_select).value; 0277 end 0278 end 0279 if isfield(vars.(position_y_field), 'attributes') 0280 latitude_atts = vars.(position_y_field).attributes; 0281 latitude_units_select = strcmp('units', {latitude_atts.name}); 0282 if any(latitude_units_select) 0283 latitude_units = latitude_atts(latitude_units_select).value; 0284 end 0285 end 0286 else 0287 if ischar(position_func) 0288 position_func = str2func(position_func); 0289 end 0290 [longitude_data, latitude_data] = ... 0291 position_func(data.(position_x_field), data.(position_y_field)); 0292 longitude_units = 'degree_east'; 0293 latitude_units = 'degree_north'; 0294 end 0295 dyn_atts.geospatial_lon_min = min(longitude_data); 0296 dyn_atts.geospatial_lon_max = max(longitude_data); 0297 dyn_atts.geospatial_lat_min = min(latitude_data); 0298 dyn_atts.geospatial_lat_max = max(latitude_data); 0299 if longitude_units 0300 dyn_atts.geospatial_lon_units = longitude_units; 0301 end 0302 if latitude_units 0303 dyn_atts.geospatial_lat_units = latitude_units; 0304 end 0305 end 0306 % Vertical coverage: 0307 vertical_field_list = cellstr(options.vertical); 0308 vertical_field_present = ... 0309 isfield(data, vertical_field_list) & isfield(vars, vertical_field_list); 0310 if any(vertical_field_present) 0311 vertical_field_index = find(vertical_field_present, 1); 0312 vertical_field = vertical_field_list{vertical_field_index}; 0313 if iscell(options.vertical_conversion) 0314 vertical_func = options.vertical_conversion{vertical_field_index}; 0315 else 0316 vertical_func = options.vertical_conversion; 0317 end 0318 if isempty(vertical_func) 0319 vertical_data = data.(vertical_field); 0320 vertical_units = []; 0321 vertical_positive = []; 0322 if isfield(vars.(vertical_field), 'attributes') 0323 vertical_atts = vars.(vertical_field).attributes; 0324 vertical_units_select = strcmp('units', {vertical_atts.name}); 0325 vertical_positive_select = strcmp('positive', {vertical_atts.name}); 0326 if any(vertical_units_select) 0327 vertical_units = vertical_atts(vertical_units_select).value; 0328 end 0329 if any(vertical_positive_select) 0330 vertical_positive = vertical_atts(vertical_positive_select).value; 0331 end 0332 end 0333 else 0334 if ischar(vertical_func) 0335 vertical_func = str2func(vertical_func); 0336 end 0337 vertical_data = vertical_func(data.(vertical_field)); 0338 vertical_units = 'meters'; 0339 vertical_positive_list = cellstr(options.vertical_positive); 0340 if numel(vertical_positive_list) > 1 0341 vertical_positive = vertical_positive_list{position_field_index}; 0342 else 0343 vertical_positive = vertical_positive_list{1}; 0344 end 0345 end 0346 dyn_atts.geospatial_vertical_min = min(vertical_data); 0347 dyn_atts.geospatial_vertical_max = max(vertical_data); 0348 if vertical_units 0349 dyn_atts.geospatial_vertical_units = vertical_units; 0350 end 0351 if vertical_positive 0352 dyn_atts.geospatial_vertical_positive = vertical_positive; 0353 end 0354 end 0355 0356 0357 %% Aggregate global metadata (global attributes and dimension definitions). 0358 global_meta = struct(); 0359 % Set global attributes. 0360 global_meta.attributes = atts; 0361 % Overwrite default attributes with deployment fields or dynamic values. 0362 for att_idx = 1:numel(global_meta.attributes) 0363 if isfield(deployment, global_meta.attributes(att_idx).name) 0364 global_meta.attributes(att_idx).value = ... 0365 deployment.(global_meta.attributes(att_idx).name); 0366 elseif isfield(dyn_atts, global_meta.attributes(att_idx).name) 0367 global_meta.attributes(att_idx).value = ... 0368 dyn_atts.(global_meta.attributes(att_idx).name); 0369 end 0370 end 0371 % Set dimension lengths. 0372 global_meta.dimensions = dims; 0373 % Overwrite lengths of dimensions not defined by input arguments. 0374 var_name_list = fieldnames(vars); 0375 for var_idx = 1:numel(var_name_list); 0376 var_name = var_name_list{var_idx}; 0377 if isfield(data, var_name) 0378 dim_name_list = vars.(var_name).dimensions; 0379 dim_size_list = size(data.(var_name)); 0380 for dim_idx = 1:numel(dim_name_list) 0381 dim_name = dim_name_list{dim_idx}; 0382 dim_size = dim_size_list(dim_idx); 0383 dim_comp = strcmp(dim_name, {global_meta.dimensions.name}); 0384 if ~any(dim_comp) 0385 global_meta.dimensions(end+1) = ... 0386 struct('name', {dim_name}, 'length', {dim_size}); 0387 elseif isempty(global_meta.dimensions(dim_comp).length) 0388 global_meta.dimensions(dim_comp).length = dim_size; 0389 end 0390 end 0391 end 0392 end 0393 0394 0395 %% Aggregate variable metadata and overwrite runtime defined metadata. 0396 variable_meta = struct(); 0397 var_name_list = fieldnames(vars); 0398 for var_name_idx = 1:numel(var_name_list) 0399 var_name = var_name_list{var_name_idx}; 0400 if isfield(data, var_name) 0401 variable_meta.(var_name) = vars.(var_name); 0402 % Loop in reverse order to allow for deletion of indexed attributes. 0403 for att_idx = numel(variable_meta.(var_name).attributes):-1:1 0404 att_name = variable_meta.(var_name).attributes(att_idx).name; 0405 att_value = variable_meta.(var_name).attributes(att_idx).value; 0406 if isempty(att_value) 0407 if isfield(meta, var_name) && isfield(meta.(var_name), att_name) 0408 if iscellstr(meta.(var_name).(att_name)) 0409 variable_meta.(var_name).attributes(att_idx).value = ... 0410 strtrim(sprintf('%s ', meta.(var_name).(att_name){:})); 0411 elseif islogical(meta.(var_name).(att_name)) 0412 variable_meta.(var_name).attributes(att_idx).value = ... 0413 uint8(meta.(var_name).(att_name)); 0414 else 0415 variable_meta.(var_name).attributes(att_idx).value = ... 0416 meta.(var_name).(att_name); 0417 end 0418 else 0419 variable_meta.(var_name).attributes(att_idx) = []; 0420 end 0421 end 0422 end 0423 end 0424 end 0425 0426 0427 %% Aggregate required variable data and apply required conversions. 0428 variable_data = struct(); 0429 data_field_list = fieldnames(data); 0430 for data_field_idx = 1:numel(data_field_list) 0431 data_field = data_field_list{data_field_idx}; 0432 if isfield(vars, data_field) 0433 % Convert text data from string cell array to C style strings. 0434 if iscellstr(data.(data_field)) 0435 variable_data.(data_field) = strc(data.(data_field)); 0436 else 0437 variable_data.(data_field) = data.(data_field); 0438 end 0439 end 0440 end 0441 0442 0443 %% Create base directory of target file if needed. 0444 % This seems to be the best way to check if a relative path points to 0445 % an existing directory (EXIST checks for existance in the whole load path). 0446 [file_dir, ~, ~] = fileparts(filename); 0447 [status, attrout] = fileattrib(file_dir); 0448 if ~status 0449 [success, message] = mkdir(file_dir); 0450 if ~success 0451 error('glider_toolbox:generateOutputNetCDF:NetCDFDirectoryError', ... 0452 'Could not create directory %s: %s.', file_dir, message); 0453 end 0454 elseif ~attrout.directory 0455 error('glider_toolbox:generateOutputNetCDF:NetCDFDirectoryError', ... 0456 'Not a directory: %s.', attrout.Name); 0457 end 0458 0459 0460 %% Generate the file. 0461 savenc(variable_data, variable_meta, global_meta, filename); 0462 0463 0464 %% Return the absolute name of the generated file. 0465 [status, attrout, ~] = fileattrib(filename); 0466 if status==0 0467 % We should never get here (if NetCDF creation succeed, file must exist). 0468 error('glider_toolbox:generateOutputNetCDF:NetCDFFileError', ... 0469 'NetCDF generation succeed but problems with output file %s: %s.', ... 0470 filename, attrout); 0471 end 0472 nc = attrout.Name; 0473 0474 end