From 75586e9ad85c0b059a55fedfd3478f2febbabfba Mon Sep 17 00:00:00 2001 From: Jacques Grelet <jacques.grelet@ird.fr> Date: Tue, 12 Jan 2010 08:30:48 +0000 Subject: [PATCH] use native NetCDF for Matlab >= R2008b rename @netcdf directory to @netcdf_native (conflict with C Dunham NetCDF toolbox) Remove FillValue_ attribute for <PARAM>_CALCOEFF_CONV, <PARAM>_LINCOEFF_CONV and SSPS_EXT_BOTTLE --- @dynaload/display.m | 2 +- @dynaload/dynaload.m | 18 +- @dynaload/save.m | 4 +- @dynaload/write.m | 10 +- @hashtable/elements.m | 8 +- @hashtable/size.m | 6 + @netcdf_native/display.m | 53 ++ @netcdf_native/file.m | 5 + @netcdf_native/get.m | 28 + @netcdf_native/getConstantNames.m | 17 + @netcdf_native/mode.m | 5 + @netcdf_native/netcdf_native.m | 217 +++++++ @netcdf_native/private/read.m | 263 ++++++++ @netcdf_native/save.m | 7 + @netcdf_native/set.m | 26 + @netcdf_native/subsasgn.m | 49 ++ @netcdf_native/subsref.m | 41 ++ {@netcdf => @netcdf_native}/tsgqc_netcdf.csv | 16 +- @netcdf_native/tsgqc_netcdf.m | 5 + {@netcdf => @netcdf_native}/tsgqc_netcdf.xls | Bin 50688 -> 50176 bytes @netcdf_native/write.m | 421 ++++++++++++ INSTALL | 14 +- tsg_doc/CORTSG_format_gosud.doc | Bin 409600 -> 408576 bytes tsg_io/readTsgDataNetCDF.m | 288 +++++---- tsg_io/read_file_woa.m | 207 +++--- tsg_io/writeTSGDataNetCDF.m | 642 ++++++++++--------- 26 files changed, 1836 insertions(+), 516 deletions(-) create mode 100644 @hashtable/size.m create mode 100644 @netcdf_native/display.m create mode 100644 @netcdf_native/file.m create mode 100644 @netcdf_native/get.m create mode 100644 @netcdf_native/getConstantNames.m create mode 100644 @netcdf_native/mode.m create mode 100644 @netcdf_native/netcdf_native.m create mode 100644 @netcdf_native/private/read.m create mode 100644 @netcdf_native/save.m create mode 100644 @netcdf_native/set.m create mode 100644 @netcdf_native/subsasgn.m create mode 100644 @netcdf_native/subsref.m rename {@netcdf => @netcdf_native}/tsgqc_netcdf.csv (92%) create mode 100644 @netcdf_native/tsgqc_netcdf.m rename {@netcdf => @netcdf_native}/tsgqc_netcdf.xls (81%) create mode 100644 @netcdf_native/write.m diff --git a/@dynaload/display.m b/@dynaload/display.m index 2dbd0e7..6ced587 100644 --- a/@dynaload/display.m +++ b/@dynaload/display.m @@ -13,7 +13,7 @@ result = elements(self); % % ----------------------- % wks = whos('global'); % for i=1:length(wks) -% if strcmp(wks(i).class, 'us191.dynaload') +% if strcmp(wks(i).class, 'dynaload') % var = wks(i).name; % end % end diff --git a/@dynaload/dynaload.m b/@dynaload/dynaload.m index 5c480c4..c827948 100644 --- a/@dynaload/dynaload.m +++ b/@dynaload/dynaload.m @@ -104,23 +104,23 @@ function self = dynaload(varargin) % test constructor argument % ------------------------- switch nargin - + case 0 % can't create default object [file, path, filterIndex] = uigetfile(... {'*.csv','Ascii-file (*.csv)';... '*.xls','Excel-file (*.xls)'}, 'Select file'); if ~any(file) self.file = 'memory'; - return + filterIndex = -1; + else + self.file = fullfile(path, file); end - self.file = fullfile(path, file); - + case 1 if strcmp(varargin{1}, 'memory') self.file = 'memory'; - return - end - if( isa(varargin{1}, 'char')) + filterIndex = -1; + elseif( isa(varargin{1}, 'char')) file = which(varargin{1}); if isempty( file) file = varargin{1}; @@ -138,10 +138,10 @@ switch nargin else error('Wrong input argument'); end - + otherwise error('Wrong number of input arguments'); - + end % init class properties diff --git a/@dynaload/save.m b/@dynaload/save.m index 518e55f..dfa35c8 100644 --- a/@dynaload/save.m +++ b/@dynaload/save.m @@ -1,6 +1,6 @@ function save(self, varargin) -% us191.dynaload.save -% it's a wrapper on us191.dynaload.write method +% dynaload save +% it's a wrapper on dynaload.write method % % $Id$ diff --git a/@dynaload/write.m b/@dynaload/write.m index 194580c..d6ecfcf 100644 --- a/@dynaload/write.m +++ b/@dynaload/write.m @@ -1,15 +1,15 @@ function write(self, varargin) % write dynaload memory structure to ASCII CSV file (Comma Separated Value) % nc is a dynaload objet in memory load from xls file. for example: -% >> nc = us191.dynaload('test.xls'); +% >> nc = dynaload('test.xls'); % >> nc -% us191.dynaload +% dynaload % % file: test.xls % -% DIMENSIONS us191.hashtable -% VARIABLES us191.hashtable -% ATTRIBUTES us191.hashtable +% DIMENSIONS hashtable +% VARIABLES hashtable +% ATTRIBUTES hashtable % % >> s = nc.VARIABLES.SSTP % diff --git a/@hashtable/elements.m b/@hashtable/elements.m index d610e8b..f9bed61 100644 --- a/@hashtable/elements.m +++ b/@hashtable/elements.m @@ -8,6 +8,10 @@ function data = elements(hash) % Copyright (c) 2004 Matthew Krauski (mkrauski@uci.edu), CNLM, UC Irvine -data(:,1) = hash.keys; -data(:,2) = hash.data; +if isempty( hash ) + data = {}; +else + data(:,1) = hash.keys; + data(:,2) = hash.data; +end diff --git a/@hashtable/size.m b/@hashtable/size.m new file mode 100644 index 0000000..642e516 --- /dev/null +++ b/@hashtable/size.m @@ -0,0 +1,6 @@ +function out = size(self) +% get hashtable size + +% Return the number of keys in hashtable +% -------------------------------------- +out = numel(self.keys); diff --git a/@netcdf_native/display.m b/@netcdf_native/display.m new file mode 100644 index 0000000..d6086ab --- /dev/null +++ b/@netcdf_native/display.m @@ -0,0 +1,53 @@ +function display(self) +% display netcdf object + + +% diplay help in hypertext link +% ----------------------------- +fprintf('<a href="matlab:help netcdf">netcdf</a>\n\n'); +if self.mode ~= -1 + fprintf('\tfile: ''%s''\n', self.file); +end +%fprintf('\tdynaload: ''%s''\n', self.dynaload); +fprintf('\tmode: ''%s''\n', self.mode); +if self.autonan + theAutonan = 'true'; +else + theAutonan = 'false'; +end +fprintf('\tautonan: %s\n', theAutonan); +if self.autoscale + theAutoscale = 'true'; +else + theAutoscale = 'false'; +end +fprintf('\tautoscale: %s\n\n', theAutoscale); + +% call protected function hashtable.elements that return +% <nx2> cell array of key/value +% ------------------------------------------------------ +result = elements(self); + +% % get workspace variables +% % ----------------------- +% wks = whos('global'); +% for i=1:length(wks) +% if strcmp(wks(i).class, '.netcdf') +% var = wks(i).name; +% end +% end + +% loop over key +% ------------- +%nb = size(result); + +for key = result' + fprintf('\t%-25s\t%2d [%-10s]\n', ... + key{1}, size(key{2}), class(key{2})); +end +fprintf('\n'); + +% diplay methods list in hypertext link +% ------------------------------------- +disp('list of <a href="matlab:methods(''.netcdf'')">methods</a>'); +end \ No newline at end of file diff --git a/@netcdf_native/file.m b/@netcdf_native/file.m new file mode 100644 index 0000000..77be740 --- /dev/null +++ b/@netcdf_native/file.m @@ -0,0 +1,5 @@ +function file = file(self) + +file = self.file; + +end \ No newline at end of file diff --git a/@netcdf_native/get.m b/@netcdf_native/get.m new file mode 100644 index 0000000..d1a2666 --- /dev/null +++ b/@netcdf_native/get.m @@ -0,0 +1,28 @@ +function value = get( self, prop ) +% GET: Get profil properties from specified object + +% $Id: get.m 121 2007-02-05 17:16:33Z jgrelet $ + +if nargin==1 % list properties if only object is given + disp('profil properties:') + fprintf(' file, mode, nc_id, autonan, autoscale'); + return +end + +if ~ischar(prop), error('GET: prop must be string.'), end + +switch prop + + case 'file' + value = self.file; + case 'mode' + value = self.mode; + case 'nc_id' + value = self.nc_id; + case 'autonan' + value = self.autonan; + case 'autoscale' + value = self.autoscale; + otherwise + error(['GET: ', prop,' is not a valid netcdf property.']); +end \ No newline at end of file diff --git a/@netcdf_native/getConstantNames.m b/@netcdf_native/getConstantNames.m new file mode 100644 index 0000000..042bbdc --- /dev/null +++ b/@netcdf_native/getConstantNames.m @@ -0,0 +1,17 @@ +function name = getConstantNames(self, xtype) +switch xtype + case 1 % NC_BYTE 1 + name = 'byte'; + case 2 % NC_CHAR 2 + name = 'char'; + case 3 % NC_SHORT 3 + name = 'short'; + case 4 % NC_INT, NC_LONG 4 + name = 'int'; + case 5 %NC_FLOAT 5 + name = 'float'; + case 6 % NC_DOUBLE 6 + name = 'double'; + otherwise + error( 'unhandled data type %d\n', xtype ); +end diff --git a/@netcdf_native/mode.m b/@netcdf_native/mode.m new file mode 100644 index 0000000..231ef16 --- /dev/null +++ b/@netcdf_native/mode.m @@ -0,0 +1,5 @@ +function mode = mode(self) + +mode = self.mode; + +end \ No newline at end of file diff --git a/@netcdf_native/netcdf_native.m b/@netcdf_native/netcdf_native.m new file mode 100644 index 0000000..4578eba --- /dev/null +++ b/@netcdf_native/netcdf_native.m @@ -0,0 +1,217 @@ +function self = netcdf_native(varargin) +% +% file @netcdf_native/netcdf_native + +arg = nargin; + +if arg == 1 && isempty(varargin{1}) + arg = 0; +end + +theFile = ''; theDynaload = 'memory'; theMode = 'MEMORY'; + +switch arg + + % with not arg, read netcdf file with uigetfile + % --------------------------------------------- + case 0 + [theFileName, thePathName] = uigetfile(... + {'*.nc;*.cdf','NetCDF (*.nc,*.cdf)'}, 'Select file'); + if any(theFileName) + theFile = fullfile(thePathName, theFileName); + theMode = 'NC_NOWRITE'; + theDynaload = 'memory'; + else + theFile = ''; + end + + % with one arg, read given netcdf filename + % ---------------------------------------- + case 1 + if( strcmp(varargin{1}, 'memory')) + theDynaload = 'memory'; + theFile = 'memory'; + theMode = 'MEMORY'; + + elseif( isa(varargin{1}, 'char')) + theFile = varargin{1}; + if isempty(exist(theFile, 'file')) + error('netcdf: %s file don''t exist.\n', theFile ); + end + [thePathName, theFileName, theFileExt] = fileparts(varargin{1}); + + switch theFileExt + case {'.nc', 'cdf'} + theMode = 'NC_NOWRITE'; + % a revoir ... + theDynaload = 'memory'; + case {'.csv', '.xls'} + theDynaload = varargin{1}; + theMode = 'DYNALOAD'; + otherwise + error('Wrong file type'); + end + + else + error('Wrong input argument'); + end + + % work on given netcdf filename with mode and dynaload dexcriptor + % file + % --------------------------------------------------------------- + case 2 + + % is the mode numeric. + if ischar(varargin{2}) + % convert it in numeric value + switch (varargin{2}) + case { 'NC_WRITE', 'NC_CLOBBER', 'NC_NOCLOBBER', 'NC_SHARE' }; + theMode = varargin{2}; + otherwise + error('Wrong mode to create netcdf file: %s', varargin{2}); + end + else + % a modifier sur mode est numeric + theMode = varargin{2}; + end + + otherwise + error('Wrong number of input arguments'); +end + +d = dynaload( theDynaload ); + +self.file = theFile; +%self.dynaload = theDynaload; +self.mode = theMode; +self.nc_id = 0; +self.autonan = logical(1); +self.autoscale = logical(0); + +% constants for netcdf data types +% ------------------------------- +self.NC_BYTE = netcdf.getConstant('NC_BYTE'); % 1 +self.NC_CHAR = netcdf.getConstant('NC_CHAR'); % 2 +self.NC_SHORT = netcdf.getConstant('NC_SHORT'); % 3 +self.NC_INT = netcdf.getConstant('NC_INT'); % 4 +self.NC_LONG = netcdf.getConstant('NC_LONG'); % 4 +self.NC_FLOAT = netcdf.getConstant('NC_FLOAT'); % 5 +self.NC_DOUBLE = netcdf.getConstant('NC_DOUBLE'); % 6 + +self.NC_MEMORY = -1; + +% constants for netcdf.open() +% --------------------------- +%self.NC_NOWRITE: Read-only access +self.NC_NOWRITE = netcdf.getConstant('NC_NOWRITE'); % 0 + +%self.NC_WRITE: Read-write access +self.NC_WRITE = netcdf.getConstant('NC_WRITE'); % 1 + +% constants for netcdf.create() +% ----------------------------- +self.NC_CLOBBER = netcdf.getConstant('NC_CLOBBER'); % 0 + +%self.NC_NOCLOBBER: Prevent overwriting of existing file with the same name. +self.NC_NOCLOBBER = netcdf.getConstant('NC_NOCLOBBER'); % 4 + +%self.NC_SHARE: Allow synchronous file updates. +self.NC_SHARE = netcdf.getConstant('NC_SHARE'); % 2048 + +%self.NC_64BIT_OFFSET: Allow easier creation of files and variables which +% are larger than two gigabytes. +self.NC_64BIT_OFFSET = netcdf.getConstant('NC_64BIT_OFFSET'); % 512 + +self.NC_ERR = netcdf.getConstant('NC2_ERR'); % -1 +self.NC_EBADDIM = netcdf.getConstant('NC_EBADDIM'); +self.NC_EBADID = netcdf.getConstant('NC_EBADID'); +self.NC_EBADNAME = netcdf.getConstant('NC_EBADNAME'); +self.NC_EBADTYPE = netcdf.getConstant('NC_EBADTYPE'); +self.NC_ECHAR = netcdf.getConstant('NC_ECHAR'); +self.NC_EDIMSIZE = netcdf.getConstant('NC_EDIMSIZE'); +self.NC_EEDGE = netcdf.getConstant('NC_EEDGE'); +self.NC_EEXIST = netcdf.getConstant('NC_EEXIST'); +self.NC_EGLOBAL = netcdf.getConstant('NC_EGLOBAL'); +self.NC_EINDEFINE = netcdf.getConstant('NC_EINDEFINE'); +self.NC_EINVAL = netcdf.getConstant('NC_EINVAL'); +self.NC_EINVALCOORDS = netcdf.getConstant('NC_EINVALCOORDS'); +self.NC_EMAXATTS = netcdf.getConstant('NC_EMAXATTS'); +self.NC_EMAXDIMS = netcdf.getConstant('NC_EMAXDIMS'); +self.NC_EMAXNAME = netcdf.getConstant('NC_EMAXNAME'); +self.NC_EMAXVARS = netcdf.getConstant('NC_EMAXVARS'); +self.NC_ENAMEINUSE = netcdf.getConstant('NC_ENAMEINUSE'); +self.NC_ENFILE = netcdf.getConstant('NC_ENFILE'); +self.NC_ENOMEM = netcdf.getConstant('NC_ENOMEM'); +self.NC_ENORECVARS = netcdf.getConstant('NC_ENORECVARS'); +self.NC_ENOTATT = netcdf.getConstant('NC_ENOTATT'); +self.NC_ENOTINDEFINE = netcdf.getConstant('NC_ENOTINDEFINE'); +self.NC_ENOTNC = netcdf.getConstant('NC_ENOTNC'); +self.NC_ENOTVAR = netcdf.getConstant('NC_ENOTVAR'); +self.NC_EPERM = netcdf.getConstant('NC_EPERM'); +self.NC_ERANGE = netcdf.getConstant('NC_ERANGE'); +self.NC_ESTRIDE = netcdf.getConstant('NC_ESTRIDE'); +self.NC_ESTS = netcdf.getConstant('NC_ESTS'); +self.NC_ETRUNC = netcdf.getConstant('NC_ETRUNC'); +self.NC_EUNLIMIT = netcdf.getConstant('NC_EUNLIMIT'); +self.NC_EUNLIMPOS = netcdf.getConstant('NC_EUNLIMPOS'); +self.NC_EVARSIZE = netcdf.getConstant('NC_EVARSIZE'); +self.NC_FATAL = netcdf.getConstant('NC_FATAL'); % 1 +self.NC_FILL = netcdf.getConstant('NC_FILL'); % 0 +self.NC_FILL_BYTE = netcdf.getConstant('NC_FILL_BYTE'); % -127 +self.NC_FILL_CHAR = netcdf.getConstant('NC_FILL_CHAR'); % 0 +self.NC_FILL_DOUBLE = netcdf.getConstant('NC_FILL_DOUBLE'); % 9.9692e+036 +self.NC_FILL_FLOAT = netcdf.getConstant('NC_FILL_FLOAT'); % 9.9692e+036 +self.NC_FILL_INT = netcdf.getConstant('NC_FILL_INT'); % -2.1475e+009 +self.NC_FILL_SHORT = netcdf.getConstant('NC_FILL_SHORT'); % -32767 +self.NC_FORMAT_64BIT = netcdf.getConstant('NC_FORMAT_64BIT'); % 2 +self.NC_FORMAT_CLASSIC = netcdf.getConstant('NC_FORMAT_CLASSIC'); % 1 +self.NC_GLOBAL = netcdf.getConstant('NC_GLOBAL'); % -1 +self.NC_LOCK = netcdf.getConstant('NC_LOCK'); % 1024 +self.NC_MAX_ATTRS = netcdf.getConstant('NC_MAX_ATTRS'); % 8192 +self.NC_MAX_DIMS = netcdf.getConstant('NC_MAX_DIMS'); % 1024 +self.NC_MAX_NAME = netcdf.getConstant('NC_MAX_NAME'); % 256 +self.NC_MAX_VARS = netcdf.getConstant('NC_MAX_VARS'); % 8192 +self.NC_MAX_VAR_DIMS = netcdf.getConstant('NC_MAX_VAR_DIMS'); % 1024 +self.NC_NAT = netcdf.getConstant('NC_NAT'); % 0 +self.NC_NOERR = netcdf.getConstant('NC_NOERR'); % 0 +self.NC_NOFILL = netcdf.getConstant('NC_NOFILL'); % 256 +self.NC_SIZEHINT_DEFAULT = netcdf.getConstant('NC_SIZEHINT_DEFAULT'); % 0 +self.NC_UNLIMITED = netcdf.getConstant('NC_UNLIMITED'); % 0 +self.NC_VERBOSE = netcdf.getConstant('NC_VERBOSE'); % 2 + +% bless class +% ----------- +self = class(self, 'netcdf_native', d); + +% call private function following mode state +% a revoir !!!!!!!!!!!!!!! +% ------------------------------------------ +switch self.mode + case 'NC_NOWRITE' % NOWRITE mode + [self] = read(self, true); + case 'NC_WRITE' % READ/WRITE mode + self = put(self,'DIMENSIONS', hashtable); + self = put(self,'VARIABLES', hashtable); + self = put(self,'ATTRIBUTES', hashtable); + case 'NC_NOCLOBBER' % NOCLOBBER mode + case 'NC_CLOBBER' % NOCLOBBER mode + self = put(self,'DIMENSIONS', hashtable); + self = put(self,'VARIABLES', hashtable); + self = put(self,'ATTRIBUTES', hashtable); + % case self.NC_SHARE % SHARE mode + % % not implemeted + case 'NC_64BIT_OFFSET' + % % + case 'DYNALOAD' + % % + case 'MEMORY' + self = put(self,'DIMENSIONS', hashtable); + self = put(self,'VARIABLES', hashtable); + self = put(self,'ATTRIBUTES', hashtable); + otherwise + error('Wrong mode argument'); +end + + + + diff --git a/@netcdf_native/private/read.m b/@netcdf_native/private/read.m new file mode 100644 index 0000000..81e0990 --- /dev/null +++ b/@netcdf_native/private/read.m @@ -0,0 +1,263 @@ +function self = read(self, varargin) +% +%varargin{1} ........... waitbar (boolean) +% +% $Id$ + +% no waitbar per default +% ---------------------- +wbool = false; + +% check varargin +% -------------- +if nargin > 1 + if varargin{1} + wbool = true; + end +end + +% check file validity +% ------------------- +self.nc_id = netcdf.open(self.file, self.mode); +if self.nc_id == -1 + error(['netcdf:netcdf', ... + 'The specified data file ''%s'' does not exist\n' ... + 'or is not in the directory which is on the MATLAB path'],... + self.file); +end + +% update waitbar title with Tex mode disable +% ------------------------------------------ +if wbool + %set(0,'DefaulttextInterpreter','none') + wb = waitbar(0, ['Loading file. Please wait...']); + hchild = get(wb,'children'); + htitle = get(hchild,'title'); + set(htitle,'Interpreter','None'); +end + +% create a temporary hashtable use to store data read in file +% ----------------------------------------------------------- +hash = hashtable; + +% returns the number of dimensions, variables, global attributes and +% eventually unlimited dimension in a netCDF file. +% ------------------------------------------------------------------ +[n_dims, nvars, ngatts, unlimdimid] = netcdf.inq(self.nc_id); + +% get dimensions and store to netcdf hashtable +% -------------------------------------------- +for dimid = 0:n_dims-1 + + % initialize empty structure + % -------------------------- + s = []; + + % Get name and length of id dimension + % ------------------------------------- + [dimname, dimlen] = netcdf.inqDim(self.nc_id, dimid); + + % Get dimid (dimension id) from name + % normalement pas besoin, a verifier.... + % ---------------------------------- + %dimid = netcdf.inqDimId(self.nc_id, dimname); + + % fill dimension structure with name and boolean for unlimdimid + % ------------------------------------------------------------- + s.dimlen = dimlen; + if(dimid == unlimdimid) + s.unlimited = true; + else + s.unlimited = false; + end + + % put dimension name and length to temporary hashtable + % ---------------------------------------------------- + hash = put(hash, dimname, s); +end + +% put dimensions hashtable to netcdf hashtable +% -------------------------------------------- +self = put(self, 'DIMENSIONS', hash); + +% clear temporary hashtable for next use +% -------------------------------------- +hash = clear(hash); +% bug (TODO), on ne peut pas utiliser hash = hash.clear !!!! +% 09/03/2009: ce n'est pas un bug, voir subsref de hashtable + +% get variables and store to netcdf hashtable +% ------------------------------------------- +for id = 0:nvars-1 + + % moves the waitbar cursor + % ------------------------ + if wbool + waitbar( id/nvars, wb); + end + + % initialize empty structure + % -------------------------- + s = []; + + % return information about variable + % --------------------------------- + [varName, xtype, dimids, natts] = netcdf.inqVar(self.nc_id, id); + + % fill structure s with variable attribute names and values + % --------------------------------------------------------- + for attid = 0:natts-1 + + % get attribute name + % ------------------ + attName = netcdf.inqAttName(self.nc_id, id, attid); + + % if attribute name is '_FillValue', transforme to 'FillValue, + % because Matlab dosn't handle member name begining with '_' + % ------------------------------------------------------------ + if strcmp(attName, '_FillValue') + s.('FillValue_') = netcdf.getAtt(self.nc_id, id, attName); + + else + + % otherwise, dynamically fill attribute member of structure s + % with it's value + % ----------------------------------------------------------- + s.(attName) = netcdf.getAtt(self.nc_id, id, attName); + end + + end % end for loop over variable attributes + + % add internal type__ member from xtype 2->char, 6->double + % -------------------------------------------------------- + s.type__ = getConstantNames(self, xtype); + + % fill temporary structure s with value + % ------------------------------------- + s.data__ = netcdf.getVar(self.nc_id, id); + + % If a NetCDF variable has valid scale_factor and add_offset + % attributes, then the data is scaled accordingly. + % ---------------------------------------------------------- + if self.autoscale + if isfield(s, 'scale_factor') && isfield(s, 'add_offset') + s.data__ = s.data__ * s.scale_factor + s.add_offset; + end + end + + % replace FillValue_ by NaN only for numeric variable + % autonan mode is set to true by default + % --------------------------------------------------- + if self.autonan && isfield(s, 'FillValue_') + switch(xtype) + case self.NC_CHAR + % For now, do nothing. + + case { self.NC_DOUBLE, self.NC_FLOAT, self.NC_LONG,... + self.NC_SHORT, self.NC_BYTE } + + % sometimes, FillValue could be a char a badformed netcdf + % files + % -------------------------------------------------------- + if isnumeric(s.('FillValue_')) + s.data__(s.data__ == s.('FillValue_')) = NaN; + else + s.data__(s.data__ == str2double(s.('FillValue_'))) = NaN; + + % verrue, pour les fichiers Roscop netcdf mal formés + % -------------------------------------------------- + if str2double(s.('FillValue_')) > 1e35 + s.data__(s.data__ >= 1e35) = NaN; + end + end + + otherwise + error( 'unhandled datatype %d\n', xtype ); + end + end + + % if var is char and as vertical alignment, transpose it + % ------------------------------------------------------ + % if xtype == self.NC_CHAR && (size(s.data__, 1) ~= 1) + % s.data__ = s.data__'; + % end + + % add internal dimension__ member + % Because MATLAB uses FORTRAN-style ordering, however, the order of + % the dimension IDs is reversed relative to what would be obtained + % from the C API + % ---------------------------- + if length(dimids) > 1 + s.data__ = permute(s.data__, fliplr(1:length(dimids))); + dimids = fliplr(dimids); + end + + + for dimid = 1:numel(dimids) + dimname = netcdf.inqDim(self.nc_id, dimids(dimid)); + + % we need to reverse dimid + % a verifier imperativement avec plusieurs dimensions + % --------------------------------------- + s.dimension__{dimid} = dimname; + end + + % put variable name and value to temporary hashtable + % -------------------------------------------------- + hash = put(hash, varName, s); + +end + + +% put variable temporary hash to netcdf hashtable +% ----------------------------------------------- +self = put(self, 'VARIABLES', hash); + +% clear temporary hashtable for next use +% -------------------------------------- +hash = clear(hash); + +% get gloabal attributes and store to netcdf hashtable +% ----------------------------------------------------- +for id = 0:ngatts-1 + + % initialize empty structure + % -------------------------- + s = []; + + % Get the name of the global attribute associated with the + % variable. + % -------------------------------------------------------- + gattName = netcdf.inqAttName(self.nc_id, ... + self.NC_GLOBAL, id); + + % Get value of global attribute. + % ------------------------------ + attVal = netcdf.getAtt(self.nc_id, ... + self.NC_GLOBAL, gattName); + + % fill global attribute structure + % ------------------------------- + s.key__ = gattName; + s.data__ = attVal; + + % put attribute name and value to temporary hashtable + % --------------------------------------------------- + hash = put(hash, gattName, s); +end + +% put attribute temporary hash to netcdf hashtable +% ------------------------------------------------ +self = put(self, 'ATTRIBUTES', hash); + +% Close waitbar +% ------------- +if wbool + close(wb) +end + +% close netcdf file +% ----------------- +netcdf.close(self.nc_id) + +end % end of read function \ No newline at end of file diff --git a/@netcdf_native/save.m b/@netcdf_native/save.m new file mode 100644 index 0000000..d37b0c4 --- /dev/null +++ b/@netcdf_native/save.m @@ -0,0 +1,7 @@ +function save(self, varargin) +% netcdf.save +% it's a wrapper on netcdf write method +% +% $Id$ + +write(self, varargin); diff --git a/@netcdf_native/set.m b/@netcdf_native/set.m new file mode 100644 index 0000000..2152847 --- /dev/null +++ b/@netcdf_native/set.m @@ -0,0 +1,26 @@ +function self = set( self, varargin ) + +% modifier set pour modifier plusieurs propriete +% (voir doc Programing Matlab, page 8-41) + +if nargin == 3 + prop = varargin{1}; + value = varargin{2}; + + switch prop + + case 'file' + self.file = value; + case 'mode' + self.mode = value; + case 'nc_id' + self.nc_id = value; + case 'autonan' + self.autonan = value; + case 'autoscale' + self.autoscale = value; + otherwise + error('Unrecognized property name ''%s''.', prop); + end + +end \ No newline at end of file diff --git a/@netcdf_native/subsasgn.m b/@netcdf_native/subsasgn.m new file mode 100644 index 0000000..3e14aa6 --- /dev/null +++ b/@netcdf_native/subsasgn.m @@ -0,0 +1,49 @@ +function self = subsasgn(self, s, val) +% Subscripted assignment for object Hashtable +% ------------------------------------------- + +% see help for subsasgn +% --------------------- +switch (length(s)) + case 3 + switch s(3).type + case '.' + h = get(self.dynaload, s(1).subs); + t = get(h, s(2).subs); + t.(s(3).subs) = val; + h = put(h, s(2).subs, t); + self = put(self, s(1).subs, h); + otherwise + error('Invalid type.') + end + case 2 + switch s(1).type + case '.' + if isstruct(val) + h = get(self.dynaload, s(1).subs); + h = put(h, s(2).subs, val); + self = put(self, s(1).subs, h); + else + error('value must be a structure'); + end + otherwise + error('Invalid type.') + end + case 1 + switch s.type + case '()' + if (length(s.subs) ~= 1) + error('Only single indexing is supported.'); + end + self = put(self, s.subs{1}, val); + case '.' + switch s.subs + case {'autonan', 'autoscale' } + builtin('subsasgn', self, s, val); + otherwise + self = put(self, s.subs, val); + end + otherwise + error('Invalid type.') + end +end diff --git a/@netcdf_native/subsref.m b/@netcdf_native/subsref.m new file mode 100644 index 0000000..cab086a --- /dev/null +++ b/@netcdf_native/subsref.m @@ -0,0 +1,41 @@ +function val = subsref(self, s) +% overloading subscripted reference for objects +% Dot Notation vs. Function Notation +% +% MATLAB classes support both function and dot notation syntax for calling methods. +% For example, if write is a method of the class of object nc, then calling +% write with function notation would be: +% +% nc = write(nc, 'mode'); +% +% The equivalent method call using dot notation is: +% +% nc = nc.write('mode') +% +% However, in certain cases, the results for dot notation can differ with respect +% to how MATLAB dispatching works: +% +% If there is an overloaded subsref, it is invoked whenever using dot-notation. +% That is, the statement is first tested to see if it is subscripted assignment. +% +% If there is no overloaded subsref, then setColor must be a method of X. +% An ordinary function or a class constructor is never called using this notation. +% +% $Id: subsref.m 411 2009-07-23 16:26:40Z jgrelet $ + +% see help from substruct and subsref +% ----------------------------------- +switch s(1).type + case '.' + switch s(1).subs + case {'write', 'save'} + builtin('subsref', self, s); + case {'autonan','autoscale'} + val = builtin('subsref', self, s); + otherwise + + % call dynaload subsref + % --------------------- + val = subsref(self.dynaload, s); + end +end diff --git a/@netcdf/tsgqc_netcdf.csv b/@netcdf_native/tsgqc_netcdf.csv similarity index 92% rename from @netcdf/tsgqc_netcdf.csv rename to @netcdf_native/tsgqc_netcdf.csv index 9651b09..4f6a7a5 100644 --- a/@netcdf/tsgqc_netcdf.csv +++ b/@netcdf_native/tsgqc_netcdf.csv @@ -3,7 +3,7 @@ % % $DIMENSIONS$ -#;key__;value;unlimited;# +#;key__;dimlen;unlimited;# #;char;integer;logical;# #;DAYD;;0;# #;DAYD_EXT;;0;# @@ -36,18 +36,18 @@ $VARIABLES$ #;CNDC_CAL;'DAYD';float;CONDUCTIVITY CALIBRATED;sea_water_electrical_conductivity;S/meter;;0;7;%6.3f;99999;;;0.001;Conductivity calibrated using linearization coefficient;;DAYD;# #;CNDC_FREQ;'DAYD';float;SENSOR CONDUCTIVITY FREQUENCY;;hz;;0;20000;%9.3f;99999;;;0.001;Sensor conductivity frequency measured by TSG;;DAYD;# #;CNDC_CALCOEF;'NCOEF_CAL';double;CONDUCTIVITY CALIBRATION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;CNDC_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;CONDUCTIVITY CALIBRATION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# +#;CNDC_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;CONDUCTIVITY CALIBRATION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# #;CNDC_LINCOEF;'NCOEF_LIN';double;CONDUCTIVITY LINEAR DRIFT CORRECTION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;CNDC_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;CONDUCTIVITY LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# +#;CNDC_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;CONDUCTIVITY LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# #;SSJT;'DAYD';float;WATER JACKET TEMPERATURE;;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Temperature within TSG or (Water Jacket Temperature). Warning, this is not ocean SST temperature. Is is used for salinity computation. The reduction applied is the same as for conductivity. Temperature is ITS-90;;DAYD;# #;SSJT_QC;'DAYD';byte;WATER JACKET TEMPERATURE QUALITY FLAG;;;;0;9;%1d;;;;;Quality flag applied on water jacket temperature values;0;DAYD;# #;SSJT_STD;'DAYD';float;WATER JACKET TEMPERATURE STANDARD DEVIATION;;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Water jacket temperature standard deviation for data wich have been reduced (with a mean or median);;DAYD;# #;SSJT_CAL;'DAYD';float;WATER JACKET TEMPERATURE CALIBRATED;;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Water jacket temperature calibrated using linearization coefficients;;DAYD;# #;SSJT_FREQ;'DAYD';float;WATER JACKET SENSOR TEMPERATURE FREQUENCY;;hz;;0;20000;%9.3f;99999;;;0.001;Frequency of water jacket temperature sensor ;;DAYD;# #;SSJT_CALCOEF;'NCOEF_CAL';double;TEMPERATURE CALIBRATION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;SSJT_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;TEMPERATURE CALIBRATION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# +#;SSJT_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;TEMPERATURE CALIBRATION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# #;SSJT_LINCOEF;'NCOEF_LIN';double;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;SSJT_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# +#;SSJT_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# #;SSJT_ADJUSTED;'DAYD';float;WATER JACKET TEMPERATURE ADJUSTED;;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Adjusted water jacket temperature with external data (ARGO,CTD,XBT,...);;DAYD;# #;SSJT_ADJUSTED_ERROR;'DAYD';float;ERROR ON ADJUSTED WATER JACKET TEMPERATURE;;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Error on adjusted water jacket temperature with external data (ARGO,CTD, XBT,…);;DAYD;# #;SSJT_ADJUSTED_QC;'DAYD';byte;WATER JACKET TEMPERATURE QUALITY FLAG;;;;0;9;%1d;;;;;Quality flag applied on adjusted water jacket temperature values;0;DAYD;# @@ -74,9 +74,9 @@ $VARIABLES$ #;SSTP_DEPH_MIN;'N1';float;MINIMUM DEPTH OF WATER INTAKE FOR TEMPERATURE MEASUREMENT;;meter;;0;100;%6.3f;99999;;;;;;;# #;SSTP_DEPH_MAX;'N1';float;MAXIMUM DEPTH OF WATER INTAKE FOR TEMPERATURE MEASUREMENT;;meter;;0;100;%6.3f;99999;;;;;;;# #;SSTP_CALCOEF;'NCOEF_CAL';double;TEMPERATURE CALIBRATION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;SSTP_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;TEMPERATURE CALIBRATION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# +#;SSTP_CALCOEF_CONV;'NCOEF_CAL','STRING8';char;TEMPERATURE CALIBRATION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','a','b','c','d','f0';;;# #;SSTP_LINCOEF;'NCOEF_LIN';double;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS;;;;;;;99999;;;;Calibration coefficients are real value, date is in julian day from REFERENCE_DATE_TIME;;;# -#;SSTP_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;NaN;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# +#;SSTP_LINCOEF_CONV;'NCOEF_LIN','STRING8';char;TEMPERATURE LINEAR DRIFT CORRECTION COEFFICIENTS CONVENTION;;;;;;;;;;;Calibration coefficient convention is an enumeration of coefficients name, ex: 'date','offset','slope';;;# #;SSTP_EXT;'DAYD_EXT';float;SEA SURFACE TEMPERATURE FROM EXTERNAL DATA;sea_surface_temperature;degree_Celsius;;-1.5;38;%6.3f;99999;;;0.001;Sea Surface Temperature (SST) from external data instrument (ARGO,CTD, XBT);;DAYD_EXT;# #;SSTP_EXT_QC;'DAYD_EXT';byte;SEA SURFACE TEMPERATURE FROM EXTERNAL DATA QUALITY FLAG;;;;0;9;%1d;;;;;Quality flag applied on external sea surface temperature data values;0;DAYD_EXT;# #;SSTP_EXT_TYPE;'DAYD_EXT','STRING4';char;TYPE OF EXTERNAL SEA SURFACE TEMPERATURE DATA ORIGIN;;;;;;;;;;;ARGO,CTD,XBT,...;;DAYD_EXT;# @@ -84,7 +84,7 @@ $VARIABLES$ #;SSPS_EXT_QC;'DAYD_EXT';byte;SEA SURFACE SALINITY FROM EXTERNAL DATA QUALITY FLAG;;;;0;9;%1d;;;;;Quality flag applied on external sea surface salinity data values;0;DAYD_EXT;# #;SSPS_EXT_TYPE;'DAYD_EXT','STRING4';char;TYPE OF EXTERNAL SEA SURFACE SALINITY DATA ORIGIN;;;;;;;;;;;WS (Water Sample),ARGO,CTD,XBT,...;;DAYD_EXT;# #;SSPS_EXT_ANALDATE;'DAYD_EXT','STRING14';char;DATE OF WATER SAMPLE SURFACE SALINITY ANALYSIS;;;yyyymmddhhmmss;;;;;;;;Date of sea surface salinity water sample analysis;;DAYD_EXT;# -#;SSPS_EXT_BOTTLE;'DAYD_EXT','STRING4';char;SEA SURFACE SALINITY BOTTLE NUMBER;;;;;;;NaN;;;;Number of sea surface salinity water sample;;DAYD_EXT;# +#;SSPS_EXT_BOTTLE;'DAYD_EXT','STRING4';char;SEA SURFACE SALINITY BOTTLE NUMBER;;;;;;;;;;;Number of sea surface salinity water sample;;DAYD_EXT;# % $ATTRIBUTES$ #;key__;name;conventions;uicontrolType;string;value;length;height;horizontalAlignment;comment;# diff --git a/@netcdf_native/tsgqc_netcdf.m b/@netcdf_native/tsgqc_netcdf.m new file mode 100644 index 0000000..f71cae6 --- /dev/null +++ b/@netcdf_native/tsgqc_netcdf.m @@ -0,0 +1,5 @@ +% tsgqc_netcdf.m +% convert Excel file to csv + +d = dynaload('tsgqc_netcdf.xls') +write( d, 'tsgqc_netcdf.csv') \ No newline at end of file diff --git a/@netcdf/tsgqc_netcdf.xls b/@netcdf_native/tsgqc_netcdf.xls similarity index 81% rename from @netcdf/tsgqc_netcdf.xls rename to @netcdf_native/tsgqc_netcdf.xls index 1a347919a7d50ddd48ea36de9e6f20732be4eadb..6cfc733e1124ed974522220c71252f848f3541d7 100644 GIT binary patch delta 1797 zcmZ8hdrXs86hEh}NU8F{d|*;5Tkt_=CKe}(LIz3^HlaXUdAj!FArvSDrgn(PtEk9J zIAhLDmMK|uGlt=pEScL94H}m%CNtTxC7LYGKg{Bc`p-6X%Fg}#(q&8Y{q8-#d+s^E zbI<MV8q{|k)*oGq-cg&`-~oW?!YNc{aXDfNdG(h6;Q^I7q2dJ<f7X1=dL5)F3k@j| zaTilK4n#p#Zk|2gnd``R_CYMzNRC<<7u))LEz{*H%H862*?CO@I}Ju%#2h8h6tfiT zX>($!^3|%(^v12(wl%M&!H`MWyE<etMgUa%{EanD%Hx!Lbgy3taBnHV<SKyjWdJ)8 z0ixppzTmNi$7U;lfzus&8NiJEtp=Flaq${}PkG@<62J(@%&`I7oD1-4GQeJ%*q9Zu ze*?hxspL&-BxmY!UF(ZVTk2BOeTZ~y)9<*0LYs$82poz({*fhZ{rtIE0Ncs{a{T}{ z#git-;9lj@_E@wGu;T$;6wCn=*9zbCWIW~5nFI6K8l?MqCvkvA^UXL&=kf{PqD)5u z4pEIm%pa+9O9A0-nl4DdVfs;>`wDrk3>BIX_t5boE51!LMSVQab=vs^N1Rrvfzyga z(VR+xAYFAP@cLb+729Z$D~K&L<T}Zi<ep;u#eEW6se8w@Inl5TvPdqLCA$`3F&8O8 zHeD|^OWVSn@XH}%i5+vvQDWs7ZH)V9vLuM@WG<DN?<f`gX~z9@z0}FfiZa1C7(1w~ zEXX+@^85gq%LDj24U}7@H?#~ERxXrWFPEk58W*oA$*eHP<%Ky_30prw7SvI9#VZT4 z!3EiH1`hIf22Ke}xm0mQ#{v#{gn((r_vwa5NJ{bwrj+pm>h%V(lb(2GDPIe}oV&aP zoYWv&C0Cd;@PW8#QcmEVS7j^q(L*_iUF55j(M{h~3jThj84r@F%E^qUN-(`uR-B-7 zRX$4#M-q1;ksBnz1K(fjok&zmK0)iMgV;}Jt7Yt_`87V?8La7Me57LguAP!YQ?f>F zZZ6%~C2EMRwc-)lTpPs0q}0k`ueF+2LY?r+t25&v^3{pMzg5=}_Da^g{61?~U-EzS zC9C>Er1-?S&8-*FZ1raBrM&te7SNS?8GC3|gHSx$AQUfX6gb}4hJ_^g|84Fjt+{T$ zytPQHCGB~0!<LRuh7I)+r-7a=(Q?#9KpLxx*ZAkx@TuP(t|MKmqp(TnYiE3yK5nw( z2;FV6;Zd>#oYFg5me*7r<J1{ggcoQ$kSd9*Qvjdy)rr)_KnB@z7SMx0mo%#F$ly0c z^uDuMbab-WipS|%v)wqx>tDkQkVy|q7SY<4h4>!XTbvSU`?jc=j?w9svqn)vmZ*U| ztuNt6)ZMz8pHBl#wPr>OUc4(=!Xrv~*4l$nN;^RD5r8{`;!RTi*c};-RW#dk51Z&- z?*^$@%Mq!bU<s`Y1*B4qGpbw}O@}r}jT#rFa(?<VWVHk|&ZOS+@~|cT@)eZc7m#*p z+&p#Ho647cReJPLe7{qYwWT<9sgk<;%~(ql{dV*!xBI6NeKbB`$2#T403lW>fguM< z)!L>6byE#p8#ZGz{WfgJT}skOA4c3h2B4f!OI<m-LK-{ugR$%8C$rIG-=bc*bt+z> zu5`os^BZF$pUuwB7O-&2m|3D4CBpE|ETR1~p;514>~bS@q({>3%OTz06ZH7<K6KEY OE1mO-*$-j_0Q>{~mgU$0 delta 2001 zcmZ8hYfMyE5T4lugk9wNpg;?V1`w|V3WTm=w-J>Ev9LUrHz<1nfkhtc7F{2Rir@ng zSEdhZ)u1ufSdH3i8)Irx(VE6+nlz22jcL-RKl-CJ#`H&H6*~8LU7@+TbI#0sGxN>4 zbI-2*#$7{3Um7Y$(xU^M0C1(`5~`D!fGCsG82dljD=f!_ye{Opj)^uJKvu&8EJ2Ys zESv{Skdt3%vpe#O?2ZAL4XLD<!XxWEFMKP?%1ZNBmY3PsCxMTKOkZP>T4<gX6KDWP zN>bM*nF35!7&ODG-1O8;)t%gLR-aq!SpIJ!fEoiZoCJ_N51@DvKyVzuHD2Aku9E>G z<^yyuW;+7Bo(yo2*Pl`VIsyP5zR9>CfbevHb29*LWdKyt=+c~^txEvzXVLQRcB;rq zFm$`ro~#&TK6Gr^pIAxz-X1oafm88o1~bjKz7-ab1JF|g;OpSkt;MR}S^t*MtKKe{ zjj=<F?lqVokOL%uZ~L;J^T`B4C~Jr4N#Q9!B4`>SNJm_@6LwQhQ9KS)Ymv5pCie4- z2{+Nz;&?nv<6_@l!h6+MVn*Cd$4h10N_R>JcwgYK;RYIU$Wl9#(aMK00SUIyT}M3o zA30>)Mp0#6^pLOY6w8eAODumYKZWm6f5rW|CPwGzvEdv&4{TIXspRGAwg^5C3G)1! zfi+e{tq3r(w%YHTnkz!&%BRPbHe5rNDw(S~S#GDnDlhhsp;}>kQ?+IvX1Rl|RXf-c zTBBJkEPKgW<7JlPyx&QN+Ae&TJhgIcfnG0`yM4tyR_A`cR*?#IMBEGR^hRBDqTO$s z8Ws0~CHsDa92lj7x|F$PpgrecFTdyDw5F*;b>A46Txq>Vu7~9(bhcij`&3vGoH8CK zyVHxi=#Ep7iu7U$d{9wPMixbu%Kf$grd>guN<8NpQDodpUnySPO_>c!NCl&Az#Lde z+3we7EQAoodgyvXH14AZ4GyfJ#f@5=y-~)Kw58D%>){A(3Kxmvc<lN|OyMHY*a@0w z^x`0GZc?z1#+zImxpMt(mbZn>ZC)!C>%3CM;H;s~o3$PvvOGvZEnYl8y)B9#m8PQ} zwaEU;Y11mFiOTyavsL?e7g~4tV>9&FrLI@88Pj4jMC<_dy0o+X-lZ{l=8DFCifZ#> z3HjO-+(S>=v_9%KXnp)5q_cf1meSAduZCmUwBcAL^4e%lhmz;e`?XFRY%5G1Y^xaT zmm_24Tyg%N#42*-)0Z7Ke`oLLotZi{#+fWXqII1%^wF8lbUZ>2Ivt50=yjKi_z(Ts zEdIiLbBO`GNA|9)Krw(c)OA7{(NWoq&}L?-Tl@BPZW)i!UbhX2?zz+PW14U~r1y2~ zN|FC4IX8YbQ@apyUb3ff&jLI{7EiKqA<UpA&x)`V#%dQ{3O`|Cs_N-O)5Law(}MsR zht%cW-C@{7SNs0LE;_wuiBzT62o`5oO@Hj^l4^8Yh_KaCbN>>lL${fPt&=YF%du|V zW)}ZMonO<gm$meyze`%D+d@TDz3LulG@_G!*zZVg&|Q(j)#%r>`{yPn)EbQxhtx`? z2W+UQM-N;<Y@;;?ZRk>c2MMuBO*mA9$?Nstc#*2vuT4!AFH&u!lfyP_QGXvEz`)d_ z026AbZ$wg~R(*jZmt!bq^jZj9%uM|B@Le>jzmCR9<Q@xB4VNvmgNvBd#N^~8W#5bn zu6(IDZ${0m*Olk&oj6jmePpaX*tuo(+4G!*y2ttrc_-=V*fuPtO}BQ2Rxui8X#(Is D>G%6r diff --git a/@netcdf_native/write.m b/@netcdf_native/write.m new file mode 100644 index 0000000..e723d8c --- /dev/null +++ b/@netcdf_native/write.m @@ -0,0 +1,421 @@ +function write(self, varargin) +% self = write(self, varargin) +% write method from netcdf class, create and write data to netcdf file +% also call by save method +% +% % Input +% ----- +% self ........... netcdf object +% varargin{1} ........... netcdf file +% varargin{2} ........... file mode +% varargin{3} ........... waitbar (boolean) +% +% Output +% ------ +% self ........... netcd object +% +% $Id: write.m 411 2009-07-23 16:26:40Z jgrelet $ + +%% check arguments +% TODOS: modify and test +% ---------------------- +wbool = false; +self.file = char(varargin{1}); +if nargin > 2 + self.mode = char(varargin{2}); +end +if nargin > 3 + if varargin{3} + wbool = true; + end +end + +% get keys list of dimensions, variables and attributes from netcdf instance +% -------------------------------------------------------------------------- +ncd_keys = keys( get(self.dynaload, 'DIMENSIONS')); +ncv_keys = keys( get(self.dynaload, 'VARIABLES')); +nca_keys = keys( get(self.dynaload, 'ATTRIBUTES')); + +% display more info about write file on console +% --------------------------------------------- +fprintf('\nWRITE_NETCDF_FILE\n'); tic; +fprintf('...writing %s : ', self.file); + +% Initialize loading NetCDF file waitbar +% -------------------------------------- +if wbool + wb = waitbar(0, 'Initializing NetCDF file. Please wait...'); + + % We cannot change title property to 'none' (default set as 'tex') inside + % waitbar function (line 81), see + % http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725 + % ----------------------------------------------------------------------- + hchild = get(wb,'children'); + htitle = get(hchild,'title'); + set(htitle,'Interpreter','None'); +end + +%% Open or create netcdf file +% ------------------------- +switch(self.mode) + case {'NC_CLOBBER'} + self.nc_id = netcdf.create(self.file, self.mode); + case 'NC_WRITE' + self.nc_id = netcdf.open(self.file, self.mode); + otherwise +end + + +%% Create NetCDF DIMENSIONS +% ------------------------- +for i=1:numel(ncd_keys) + key__ = ncd_keys{i}; + s = get(get(self.dynaload, 'DIMENSIONS'), key__); + %value = s.value; + dimlen = s.dimlen; + + % % create unlimited dimension. Any unlimited dimension is therefore last + % % in the list of dimension IDs in defVar function, todo below... + % % --------------------------------------------------------------------- + % if s.unlimited + % unlimDimId = netcdf.defDim(self.nc_id, key__, ... + % netcdf.getConstant('NC_UNLIMITED')); + % end + + % if dimension is not set, get size from variable + % a faire evoluer car peu sur, et a voir pour tableaux + % ----------------------------------------------- + if isempty(dimlen) + t = get(get(self.dynaload, 'VARIABLES'), key__); + if isfield(t, 'data__') + netcdf.defDim(self.nc_id, key__, numel(t.data__)); + end + else + try + netcdf.defDim(self.nc_id, key__, dimlen); + catch ME + error('\nError: netcdf.%s (line %d), nc_id: %d, key__: %s dimlen: %d\n\tin function call: %s (%s)',... + ME.stack(2).name, ME.stack(2).line, self.nc_id, key__, dimlen, ME.stack(1).name, ME.message); + end + end + +end + +% display filename after 'Interpreter','None' initialization to prevent +% display console warning +% --------------------------------------------------------------------- +if wbool + waitbar(1/50,wb, ['Writing file: ' self.file ' Please wait...']); +end + +%% Create and write NetCDF GLOBAL ATTRIBUTES +% ----------------------------------- + +% get netcdf global attribute constant +% ------------------------------------ +nga = self.NC_GLOBAL; +%nga = netcdf.getConstant('NC_GLOBAL'); + +for i=1:numel(nca_keys) + key = nca_keys{i}; + s = get(get(self.dynaload, 'ATTRIBUTES'), key); + %value = s.data__; + if isfield(s, 'data__') + + % Create an attribute associated with the variable. + % ------------------------------------------------- + try + netcdf.putAtt(self.nc_id, nga, key, s.data__); + catch ME + fprintf('\nError: netcdf.%s (line %d)\n\tin function call: %s (%s)',... + ME.stack(2).name, ME.stack(2).line, ME.stack(1).name, ME.message); + end + end +end + +% Leave define mode. +% ------------------ +netcdf.endDef(self.nc_id) + + + +%% Adds NetCDF VARIABLES +% ---------------------- +for i=1:numel(ncv_keys) + + % Re-enter define mode. + % --------------------- + netcdf.reDef(self.nc_id); + + % initialise varstruct + % nc_addvar use a structure with four fields: + % Name + % Nctype + % Dimension + % Attribute is also a structure array + % see help nc_addvar + % ----------------------------------- + %varstruct = []; + + + % get variable name + % ----------------- + key = ncv_keys{i}; + + % display waitbar + % --------------- + if wbool + waitbar( i/numel(ncv_keys), wb, ['writing ' key ' variable']); + end + + % uncomment for debug only, display variable + % ------------------------------------------ + % key + + % get structure + % ------------- + s = get(get(self.dynaload, 'VARIABLES'), key); + + % get dimension(s) dimids from ncetcdf file, s.dimension is cell + % -------------------------------------------------------------- + %dimids = []; + + % utiliser numel(s.dimension__) et dimids(i) pour creer le tableau des + % dimids + if ~isfield(s, 'dimension__') + s %#ok<NOPRT> + error('field dimension__ not defined in variables structure ...'); + end + + try + % preallocate dimids + % ------------------ + dimids = zeros(size(s.dimension__)); + + % fill dimids array from dim name + % ------------------------------- + for ind=1:length(dimids) + dimid = netcdf.inqDimID(self.nc_id, s.dimension__{ind}); + dimids(ind) = dimid; + end + + % find index of unlimited dimension + % --------------------------------- + %ind = find(dimids == unlimDimId); + + % reshape dimids array with unlimited value at the start + % ------------------------------------------------------- + % if ~isempty(ind) + % dimids = [ dimids(1:ind-1), circshift(dimids(ind+1:end),[0 -1])]; + % dimids = [circshift(dimids(1:ind),[0 1]), dimids(ind+1:end)]; + % end + + catch ME + fprintf('\nNotice: netcdf.%s, dimension %s don''t exist or is empty',... + ME.stack(2).name, s.dimension__{ind}); + + % Leave define mode and go to next variable (key__) + % ------------------------------------------------- + netcdf.endDef(self.nc_id); + continue; + end + + % We need to flip the dimensions + % ------------------------------ + dimids = fliplr(dimids); + %netcdf.reDef(self.nc_id); + + % create variable and get it's varid + % ---------------------------------- + varid = netcdf.defVar(self.nc_id, key, s.type__, dimids); + + % dynamically reate variable attributes + % get structure fieldnames from hashtable + % --------------------------------------- + ncv_fieldNames = fieldnames(s); + + % loop over fieldname (variable attributes), first is key (code) + % -------------------------------------------------------------- + for j=1:numel(ncv_fieldNames) + + % get fielname and value + % ---------------------- + fieldName = ncv_fieldNames{j}; + value = s.(fieldName); + + % jump each internal fieldname (eg key__, data__, type__, dimension__) + % or empty fieldname + % ------------------------------------------------------------- + if ~isempty(regexp(fieldName, '__$','ONCE')) || isempty(value) + continue; + end + + % change struct fieldname FillValue_ to attribute _FillValue + % ---------------------------------------------------------- + if strcmp( fieldName, 'FillValue_') + fieldName = '_FillValue'; + end + + % BE CARREFULL: it's an very important notice + % value assign to attribute _FillValue should have same type + % as variable, true for all numeric atribute variable + % ------------------------------------------------------ + if isnumeric(value) + switch s.type__ + case {'double', 'real'} + value = double(value); + case {'float', 'single'} + value = single(value); + case {'int', 'integer', 'int32'} + value = int32(value); + case {'short', 'int16'} + value = int16(value); + case {'int8'} + value = int8(value); + case {'byte', 'uint8'} + value = uint8(value); + case 'char' + value = char(value); + end + end + + % Write variable netCDF attribute + % ------------------------------- + netcdf.putAtt(self.nc_id, varid, fieldName, value); + + end + + % Leave define mode and enter data mode to write data. + % ----------------------------------------------------- + netcdf.endDef(self.nc_id); + + % if there is not data for variable or data__ empty + % ------------------------------------------------ + if ~isfield(s, 'data__') || isempty(s.data__) + + % and it's missing_value attribute is set, fill data with missing_value + % ------------------------------------------------------------------- + if isfield(s, 'missing_value') && ~isempty(s.missing_value) + + % get dimension id from name + % -------------------------- + dimsize = numel(s.dimension__); + + % rajouter un test iscell + + % set dimids size array + % ---------------------- + dimids = zeros(dimsize, 1); + + % fill dimids array with dimension(s) Id + % -------------------------------------- + try + for dim = 1: dimsize + dimids(dim) = netcdf.inqDimID(self.nc_id, s.dimension__{dim}); + end + catch ME + msg = sprintf('dimension: %s don''t exist', s.dimension__{dim}); + fprintf('\nWarning: netcdf.%s (line %d)\n\tin function call: %s',... + ME.stack(2).name, ME.stack(2).line, msg); + continue; + end + + % get size of pre-allocate dimsize + % ------------------------------- + dimlen = zeros(dimsize, 1); + + % get dimension name and length from it's dimid + % --------------------------------------------- + for dim = 1: dimsize + [dimname, dimlen(dim)] = netcdf.inqDim(self.nc_id, dimids(dim)); + end + + % fill data with missing_value + % could use repmat to do that: s.data__ = repmat(1, dimlen, 1)); + % -------------------------------------------------------------- + if dimsize == 1 + s.data__ = ones(dimlen,1) * s.missing_value; + else + s.data__ = ones(dimlen) * s.missing_value; + end + else + + % if variable don't have attribute missing_value, exit loop and go to + % next variable + % ------------------------------------------------------------------- + continue; + end + + end + + % BE CAREFULL: + % cast variable with the right netcdf nctype + % ------------------------------------------ + switch s.type__ + case {'double', 'real'} + value = double(s.data__); + case {'float', 'single'} + value = single(s.data__); + case {'int', 'integer', 'int32'} + value = int32(s.data__); + case {'short', 'int16'} + value = int16(s.data__); + case {'byte', 'int8'} + value = int8(s.data__); + case 'char' + value = char(s.data__); + end + + % matlab use FORTRAN indexing (k,j,i) and lib netcdf come from C style + % indexing (i,j,k) + % Matlab documentation is not very clear about this very important tips + % see: + % http://www.mathworks.com/access/helpdesk/help/techdoc/index.html?/access/helpdesk/help/techdoc/matlab_prog/brrbr9v-1.html&http://www.mathworks.fr/cgi-bin/texis/webinator/search/ + % ------------------------------------------- + value = permute(value, fliplr(1:ndims(value))); + + % If a NetCDF variable has valid scale_factor and add_offset + % attributes, then the data is scaled accordingly. + % ---------------------------------------------------------- + if self.autoscale + if isfield(s, 'scale_factor') && isfield(s, 'add_offset') + value = (value - s.add_offset) / s.scale_factor; + end + end + + % If a NetCDF variable has valid _FillValue attributes, then + % the data is filled accordingly. + % ---------------------------------------------------------- + if self.autonan && isfield(s, 'FillValue_') + value(isnan(value)) = s.FillValue_; + end + + % Write data to variable. + % ----------------------- + try + netcdf.putVar(self.nc_id, varid, value); + catch exception + fprintf('write variable error: %s', s.key__'); + s %#ok<NOPRT> + fprintf('check your dynaload csv or xls file !!!\n'); + error('Matla:netcdf.write:netcdf.putVar',... + 'Cannot write netcdf file\n%s.', exception.message); %#ok<CTPCT> + + end +end % end for ncv variables loop + +% Close waitbar +% ------------- +if wbool + close(wb) +end + +% Display time to write file on console +% ------------------------------------- +t = toc; fprintf('...done (%6.2f sec).\n\n',t); + +% close netcdf file +% ----------------- +netcdf.close(self.nc_id) + +end % end of write function \ No newline at end of file diff --git a/INSTALL b/INSTALL index e555cd5..2da0be5 100644 --- a/INSTALL +++ b/INSTALL @@ -16,16 +16,18 @@ Installation: http://mexcdf.sourceforge.net/downloads/ voir lien egalement sous http://www.ird.fr/us191/spip.php?article18 - - Installer la toolbox NetCDF sous Matlab depuis: + - Pour les versions Matlab R2006b a R2008a, il faut installer la toolbox + MEXNC/NetCDF depuis: http://mexcdf.sourceforge.net/downloads/ voir lien egalement sous http://www.ird.fr/us191/spip.php?article18# + + Pour les versions de Matlab >= R2008b, le programme utilise le support + natif des fichiers NetCDF disponible a partir de cette version. Il faut + supprimer du path Matlab tout lien vers la toolbox mexnc -Etapes d'installation MEXNC - Sous Windows - Sous Linux - Installation toolbox NETCDF - - Depuis la version 1.0RC5, le format netcdf des fichiers de climato + - Installation des fichiers de climatologie: + Depuis la version 1.0RC5, le format netcdf des fichiers de climato a change, et une version WOA05 a ete generee. Le logiciel fonctionne indefferement avec les fichiers WOA01 ou WOA04 (a choisir dans le menu options/preference diff --git a/tsg_doc/CORTSG_format_gosud.doc b/tsg_doc/CORTSG_format_gosud.doc index f75e35ed1721d5193a8f2dd67421ec55f71a4738..d50d2c21856a5fb22dbc667941129158a11aa8ee 100644 GIT binary patch delta 58766 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 V!KSfB2Q9S`rPs_kjZDtU{{VNQLeKyJ delta 58960 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 al$&a4_d_j{%`V!+R+QZHZ@02kQ2q!1V;1fJ diff --git a/tsg_io/readTsgDataNetCDF.m b/tsg_io/readTsgDataNetCDF.m index 324417e..eaf2f11 100644 --- a/tsg_io/readTsgDataNetCDF.m +++ b/tsg_io/readTsgDataNetCDF.m @@ -15,119 +15,189 @@ function [error] = readTsgDataNetCDF( hMainFig, filename) % % $Id$ -% clear all variables in base workspace -% ------------------------------------- -%clear all; - -% Get the data from the application GUI -% ------------------------------------- -tsg = getappdata( hMainFig, 'tsg_data'); - -% Display read file info on console -% --------------------------------- -fprintf('\nREAD_NETCDF_FILE\n'); tic; - -% Open netCDF file -% ---------------- -nc = netcdf(filename,'read'); -if isempty(nc) - msg_error = ['TSG_GOSUD file_lecture : Open file error : ' filename]; - warndlg( msg_error, 'NetCDF error dialog'); - sprintf('...cannot locate %s\n', filename); - error = -1; - return; -end - -% Display more info about read file on console -% -------------------------------------------- -fprintf('...reading %s : ', filename); - -% populate tsg.file structure -% --------------------------- -[tsg.file.pathstr, tsg.file.name, tsg.file.ext, tsg.file.versn] = ... - fileparts(filename); -tsg.file.type = 'NETCDF'; - - -% get global attributes: meta data -% -------------------------------- -global_att = att(nc); - -for i=1:length(global_att) - - % extract name and value from netcdf globals attributes - % ----------------------------------------------------- - attribute = name(global_att{i}); - value = global_att{i}(:); - - % assign globals attributes in base workspace - % ------------------------------------------- - assignin('base', attribute, value); - - % populate tsg structure with globals attributes - % ---------------------------------------------- - tsg.(attribute) = value; -end - -% get variables describing TSG installation -% ----------------------------------------- -variables = var(nc); - -for i=1:length(variables) - - % extract name and value from netcdf variables - % -------------------------------------------- - variable = name(variables{i}); - %value = variables{i}(:); - nv = nc{i}; - - % use autonan mode, remplace fillValue with NaN - % --------------------------------------------- - nv = autonan(nv, true); - - % finally replace fillvalue with NaN when only fillvalue exist - % ------------------------------------------------------------ -% if ~isempty(fillval(nc{i})) -% value(value == fillval(nc{i})) = NaN; -% end - - % assign netcdf variables in base workspace - % ----------------------------------------- - assignin('base', variable, nv(:)); - - % populate tsg structure with netcdf variables - % -------------------------------------------- - tsg.(variable) = nv(:); - - % transforme julian days variables to Matlab datenum - % -------------------------------------------------- - if strmatch('DAYD', variable) - tsg.(variable) = julianToDatenum(tsg.(variable)); - end +%% Check Netcdf library version +% ----------------------------- +switch ( version('-release') ) + case { '11', '12', '13', '14', '2006a', '2006b', '2007a', '2007b', '2008a' } + read_netcdf_toolbox; + otherwise + read_netcdf_native; end -% Keep somme information for the log file -% --------------------------------------- -tsg.report.tsgfile = filename; - -% Save the data in the application GUI -% ------------------------------------ -setappdata( hMainFig, 'tsg_data', tsg ); - % Perform somme automatic tests % ----------------------------- automaticQC( hMainFig ) -% Close the file -% -------------- -close(nc); - -% Display time to read file on console -% ------------------------------------ -t = toc; fprintf('...done (%5.2f sec).\n\n',t); -% Everything OK -% ------------- -error = 1; - -end +%% Nested functions +% ----------------- + +% use Charles Dunham toolbox +% -------------------------- + function read_netcdf_toolbox + + % Get the data from the application GUI + % ------------------------------------- + tsg = getappdata( hMainFig, 'tsg_data'); + + % Display read file info on console + % --------------------------------- + fprintf('\nREAD_NETCDF_FILE\n'); tic; + + % Open netCDF file + % ---------------- + nc = netcdf(filename,'read'); + if isempty(nc) + msg_error = ['TSG_GOSUD file_lecture : Open file error : ' filename]; + warndlg( msg_error, 'NetCDF error dialog'); + sprintf('...cannot locate %s\n', filename); + error = -1; + return; + end + + % Display more info about read file on console + % -------------------------------------------- + fprintf('...reading %s : ', filename); + + % populate tsg.file structure + % --------------------------- + [tsg.file.pathstr, tsg.file.name, tsg.file.ext, tsg.file.versn] = ... + fileparts(filename); + tsg.file.type = 'NETCDF'; + + % get global attributes: meta data + % -------------------------------- + global_att = att(nc); + + for i=1:length(global_att) + + % extract name and value from netcdf globals attributes + % ----------------------------------------------------- + attribute = name(global_att{i}); + value = global_att{i}(:); + + % assign globals attributes in base workspace + % ------------------------------------------- + assignin('base', attribute, value); + + % populate tsg structure with globals attributes + % ---------------------------------------------- + tsg.(attribute) = value; + end + + % get variables describing TSG installation + % ----------------------------------------- + variables = var(nc); + + for i=1:length(variables) + + % extract name and value from netcdf variables + % -------------------------------------------- + variable = name(variables{i}); + %value = variables{i}(:); + nv = nc{i}; + + % use autonan mode, remplace fillValue with NaN + % --------------------------------------------- + nv = autonan(nv, true); + + % finally replace fillvalue with NaN when only fillvalue exist + % ------------------------------------------------------------ + % if ~isempty(fillval(nc{i})) + % value(value == fillval(nc{i})) = NaN; + % end + + % assign netcdf variables in base workspace + % ----------------------------------------- + assignin('base', variable, nv(:)); + + % populate tsg structure with netcdf variables + % -------------------------------------------- + tsg.(variable) = nv(:); + + % transforme julian days variables to Matlab datenum + % -------------------------------------------------- + if strmatch('DAYD', variable) + tsg.(variable) = julianToDatenum(tsg.(variable)); + end + end + + % Keep somme information for the log file + % --------------------------------------- + tsg.report.tsgfile = filename; + + % Save the data in the application GUI + % ------------------------------------ + setappdata( hMainFig, 'tsg_data', tsg ); + + % Close the file + % -------------- + close(nc); + + % Display time to read file on console + % ------------------------------------ + t = toc; fprintf('...done (%5.2f sec).\n\n',t); + + % Everything OK + % ------------- + error = 1; + + end % end of read_netcdf_toolbox + +% use native toolbox +netcdf since R2008b +% --------------------------------------- + function read_netcdf_native + + % Get the data from the application GUI + % ------------------------------------- + tsg = getappdata( hMainFig, 'tsg_data'); + + % Display read file info on console + % --------------------------------- + fprintf('\nREAD_NETCDF_FILE\n'); tic; + + % populate tsg.file structure + % --------------------------- + [tsg.file.pathstr, tsg.file.name, tsg.file.ext, tsg.file.versn] = ... + fileparts(filename); + tsg.file.type = 'NETCDF'; + + % Open netCDF file + % ---------------- + nc = netcdf_native(filename); + + % loop over all variables and get associated value in tsg structure + % ----------------------------------------------------------------- + for key = keys(nc.VARIABLES) + var = char(key); + tsg.(var) = nc.VARIABLES.(var).data__; + end + + % loop over all gloabal attributes and get associated value in + % tsg structure + % ----------------------------------------------------------------- + for key = keys(nc.ATTRIBUTES) + att = char(key); + tsg.(att) = nc.ATTRIBUTES.(att).data__; + end + + % Save the data in the application GUI + % ------------------------------------ + setappdata( hMainFig, 'tsg_data', tsg ); + + + % Close the file + % -------------- + %close(nc); + + % Display time to read file on console + % ------------------------------------ + t = toc; fprintf('...done (%5.2f sec).\n\n',t); + + % Everything OK + % ------------- + error = 1; + + end % end of read_netcdf_native + +end % end of readTsgDataNetCDF diff --git a/tsg_io/read_file_woa.m b/tsg_io/read_file_woa.m index 569af21..b8ce62c 100644 --- a/tsg_io/read_file_woa.m +++ b/tsg_io/read_file_woa.m @@ -7,98 +7,135 @@ function levitus = read_file_woa(file) % check for file existence % ------------------------ filesfound = checkforfiles( file ); -if filesfound == 0; - warning(['Couldn''t find ', file]); - levitus = -1; - return; +if filesfound == 0; + warning(['Couldn''t find ', file]); + levitus = -1; + return; end -% il existe un "bug" dans netcdf ligne 409, ncitem retourne une -% erreur dans la variable result - -% open file in read mode -% ---------------------- -nc = netcdf(which(file), 'nowrite'); - -% Initialise la waitbar de chargement du fichier netCDF -% ----------------------------------------------------- -%wb = waitbar(0,''); - -% We cannot change title property to 'none' (default set as 'tex') inside -% waitbar function (line 81), see -% http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725 -% ----------------------------------------------------------------------- -% hchild = get(wb,'children'); -% htitle = get(hchild,'title'); -% set(htitle,'Interpreter','None'); -% ('none' as opposed to 'latex') to leave underscores as underscores -set(0,'DefaulttextInterpreter','none') - - -% update waitbar title with Tex mode disable -% ------------------------------------------ -wb = waitbar(0, ['Loading file: ' file ' Please wait...']); - -% loop for every variable -% ----------------------- -for i = 1:length(var(nc)) - - % first initialize the dimensions of the data array, this is due to a bug - % (?) in netcdf toolbox, with an array [1,1,n,m], netcdf operator (:) - % return only [n,m]. This is true for surface climatology file !!!!! - % ------------------------------------------------------------------- - data = zeros(size(nc{i})); - - % return current variable (object) - % -------------------------------- - nv = nc{i}; - - % use autonan mode on this variable (object), remplace fillValue with NaN - % ----------------------------------------------------------------------- - nv = autonan(nv, true); - - % then fill with values, we use a temporary object data, because nv - % is an netcdf object, see line 75 - % ----------------------------------------------------------------- - data(:) = nv(:); - - % other Matlab way to replace fillValue by NaN - % -------------------------------------------- - % if ~isempty(fillval(nc{i})) - % data(data == fillval(nc{i})) = NaN; - % end - - % assign dynamically variable in structure levitus - % ------------------------------------------------ - % be carreful !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - % don't use data(:) ... - % whem data dimensions are [1,1,m,n] - % [m,n] = data(:) - % [1,1,m,n] = data - % ------------------------------------------------ - levitus.(name(nv)) = data; - - % moves the waitbar cursor - % ------------------------ - waitbar( i/length(nv), wb); - +%% Check Netcdf library version +% ----------------------------- +switch ( version('-release') ) + case { '11', '12', '13', '14', '2006a', '2006b', '2007a', '2007b', '2008a' } + read_netcdf_toolbox; + otherwise + read_netcdf_native; end -% close waitbar -% ------------- -close(wb); -% close netcdf file +%% Nested functions % ----------------- -close(nc); +% use Charles Dunham toolbox +% -------------------------- + function read_netcdf_toolbox + % il existe un "bug" dans netcdf ligne 409, ncitem retourne une + % erreur dans la variable result + + % open file in read mode + % ---------------------- + nc = netcdf(which(file), 'nowrite'); + + % Initialise la waitbar de chargement du fichier netCDF + % ----------------------------------------------------- + %wb = waitbar(0,''); + + % We cannot change title property to 'none' (default set as 'tex') inside + % waitbar function (line 81), see + % http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725 + % ----------------------------------------------------------------------- + % hchild = get(wb,'children'); + % htitle = get(hchild,'title'); + % set(htitle,'Interpreter','None'); + % ('none' as opposed to 'latex') to leave underscores as underscores + set(0,'DefaulttextInterpreter','none') + + + % update waitbar title with Tex mode disable + % ------------------------------------------ + wb = waitbar(0, ['Loading file: ' file ' Please wait...']); + + % loop for every variable + % ----------------------- + for i = 1:length(var(nc)) + + % first initialize the dimensions of the data array, this is due to a bug + % (?) in netcdf toolbox, with an array [1,1,n,m], netcdf operator (:) + % return only [n,m]. This is true for surface climatology file !!!!! + % ------------------------------------------------------------------- + data = zeros(size(nc{i})); + + % return current variable (object) + % -------------------------------- + nv = nc{i}; + + % use autonan mode on this variable (object), remplace fillValue with NaN + % ----------------------------------------------------------------------- + nv = autonan(nv, true); + + % then fill with values, we use a temporary object data, because nv + % is an netcdf object, see line 75 + % ----------------------------------------------------------------- + data(:) = nv(:); + + % other Matlab way to replace fillValue by NaN + % -------------------------------------------- + % if ~isempty(fillval(nc{i})) + % data(data == fillval(nc{i})) = NaN; + % end + + % assign dynamically variable in structure levitus + % ------------------------------------------------ + % be carreful !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + % don't use data(:) ... + % whem data dimensions are [1,1,m,n] + % [m,n] = data(:) + % [1,1,m,n] = data + % ------------------------------------------------ + levitus.(name(nv)) = data; + + % moves the waitbar cursor + % ------------------------ + waitbar( i/length(nv), wb); + + end + + % close waitbar + % ------------- + close(wb); + + % close netcdf file + % ----------------- + close(nc); + end + +% use native toolbox +netcdf since R2008b +% --------------------------------------- + function read_netcdf_native + + % Open netCDF file + % ---------------- + nc = netcdf_native(file); + + for key = keys(nc.VARIABLES) + var = char(key); + data = zeros( size( nc.VARIABLES.(var).data__ ) ); + data(:) = nc.VARIABLES.(var).data__; + levitus.(var) = data; + end + + end -%---------------------------------------------------------------------- -%---------------------------------------------------------------------- -function filesfound = checkforfiles( file ) +% check if file exist +%-------------------- + function filesfound = checkforfiles( file ) + + if exist(file,'file'); + filesfound = 1; + else + filesfound = 0; + end + + end -if exist(file,'file'); - filesfound = 1; -else - filesfound = 0; end diff --git a/tsg_io/writeTSGDataNetCDF.m b/tsg_io/writeTSGDataNetCDF.m index f2ce6ea..3617c42 100644 --- a/tsg_io/writeTSGDataNetCDF.m +++ b/tsg_io/writeTSGDataNetCDF.m @@ -20,307 +20,371 @@ function [error] = writeTSGDataNetCDF( hTsgGUI, filename) % % $Id$ -%% Open the file -% ------------- -nc = netcdf(filename, 'clobber'); -if isempty(nc) - msg_error = ['TSG_GOSUD file_lecture : Open file error : ' filename]; - warndlg( msg_error, 'NetCDF write dialog'); - error = -1; - return; +%% Check Netcdf library version +% ----------------------------- +switch ( version('-release') ) + case { '11', '12', '13', '14', '2006a', '2006b', '2007a', '2007b', '2008a' } + write_netcdf_toolbox; + otherwise + write_netcdf_native; end -% Display read file info on console -% --------------------------------- -fprintf('\nWRITE_NETCDF_FILE\n'); tic; - -% Initialize loading NetCDF file waitbar -% -------------------------------------- -wb = waitbar(0, 'Initializing NetCDF file. Please wait...'); - -% We cannot change title property to 'none' (default set as 'tex') inside -% waitbar function (line 81), see -% http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725 -% ----------------------------------------------------------------------- -hchild = get(wb,'children'); -htitle = get(hchild,'title'); -set(htitle,'Interpreter','None'); - -% Get the data from the application GUI -% ------------------------------------- -tsg = getappdata( hTsgGUI, 'tsg_data'); - -% Get an instance of dynaload objetc from file 'tsgqc_netcdf.csv' -% ----------------------------------------------------------------- -dyna = dynaload('tsgqc_netcdf.csv'); - -% Get attributes & variables list from dynaload instance -% ------------------------------------------------------ -ncd_keys = keys(dyna.DIMENSIONS); -ncv_keys = keys(dyna.VARIABLES); -nca_keys = keys(dyna.ATTRIBUTES); - -%ncv_fieldNames = get_fieldnames(ncv); - -% Display more info about write file on console -% --------------------------------------------- -fprintf('...writing %s : ', filename); - -%% Create NetCDF file: -% ------------------- -% for key = ncd_keys -% k = char(key); -% value = dyna.DIMENSIONS.(key).value; -% if ~isempty(value) -% nc(k) = value; -% elseif ~isempty(tsg.(k)) -% nc(k) = numel(tsg.(k)); -% end -% end - -% Variable dimensions -% ------------------- -nc('DAYD') = numel(tsg.DAYD); - -% if dimension is empty (no data), don't create, because create empty -% dimension quive it unlimited dimension, but you can only have one -% unlimited dimension in a file -% -------------------------------------------------------------------- - -% number of external SSPS or SSTP comparison -% ------------------------------------------ -if ~isempty(tsg.DAYD_EXT) - nc('DAYD_EXT') = numel(tsg.DAYD_EXT); -end - -% number of calibration coefficients -% ---------------------------------- -nc('NCOEF_CAL') = tsg.dim.CALCOEF; % 7 - -% number of linearisation coefficients -% ------------------------------------ -nc('NCOEF_LIN') = tsg.dim.LINCOEF; % 2 - -% Fixed dimensions -% ---------------- -nc('STRING256') = tsg.dim.STRING256; -nc('STRING14') = tsg.dim.STRING14; -nc('STRING8') = tsg.dim.STRING8; -nc('STRING4') = tsg.dim.STRING4; -nc('N1') = tsg.dim.N1; - -% display filename after 'Interpreter','None' initialization to prevent -% display console warning -% --------------------------------------------------------------------- -waitbar(1/50,wb,['Writing file: ' filename ' Please wait...']); - -%% Create NetCDF global attributes -% ------------------------------- -for key = nca_keys - global_att = char(key); - nc.(global_att) = tsg.(global_att); -end - -% Create NetCDF variables and attributes -% -------------------------------------- -for key = ncv_keys - - variable = char(key); - - % get structure - % ------------- - s = dyna.VARIABLES.(variable); - - ncv_fieldNames = fieldnames(s); - - % get variable dimension declare in file - % -------------------------------------- - %dimension = get(ncv, variable, ncv_fieldNames{1}); - dimension = s.dimension__; - - % get variable type declare in file - % -------------------------------------- - %nctype = get(ncv, variable, ncv_fieldNames{2}); - nctype = s.type__; - - % create netcdf variable with dimension, dimension must be a cell - % --------------------------------------------------------------- - switch nctype - case 'double' - nc{variable} = ncdouble(dimension); - case 'float' - nc{variable} = ncfloat(dimension); - case 'int' - nc{variable} = ncint(dimension); - case 'short' - nc{variable} = ncshort(dimension); - case 'byte' - nc{variable} = ncbyte(dimension); - case 'char' - nc{variable} = ncchar(dimension); - otherwise - % by default, display a warning and cast to char - % ---------------------------------------------- - warning('tsgqc_GUI:writeTSGDataNetCDF', ... - 'unknow type ''%s'' for dimension ''%s'' of ''%s'' variable.\nCheck your %s file', ... - nctype, dimension{1}, variable, file(ncv)); - - nc{variable} = ncchar(dimension); - end - - % create variable attribute, jump dimension and nctype fields - % ----------------------------------------------------------- - for j=1:numel(ncv_fieldNames) - - % see new us191.netcdf toolbox, we'll not use index to get type - % but structure with special variable, eg: s.type__ - % ------------------------------------------------- - fieldName = ncv_fieldNames{j}; - value = s.(fieldName); - %nctype = get(ncv, variable, ncv_fieldNames{2}); +%% Nested functions +% ----------------- - % jump each internal fieldname (eg key__, data__, type__, dimension__) - % or empty fieldname - % ------------------------------------------------------------- - if ~isempty(regexp(fieldName, '__$','ONCE')) || isempty(value) - continue; +% use Charles Dunham toolbox +% -------------------------- + function write_netcdf_toolbox + + + %% Open the file + % -------------- + nc = netcdf(filename, 'clobber'); + if isempty(nc) + msg_error = ['TSG_GOSUD file_lecture : Open file error : ' filename]; + warndlg( msg_error, 'NetCDF write dialog'); + error = -1; + return; end - % if field is empy, it's not a attribute for this netcdf variable - % --------------------------------------------------------------- - if ~isempty(value) - - % ncatt(theAttname, theAttvalue, theParent) uses the class of - % theAttvalue as theAtttype ('char' or 'double'). - % need to be cast before assignment + % Display read file info on console + % --------------------------------- + fprintf('\nWRITE_NETCDF_FILE\n'); tic; + + % Initialize loading NetCDF file waitbar + % -------------------------------------- + wb = waitbar(0, 'Initializing NetCDF file. Please wait...'); + + % We cannot change title property to 'none' (default set as 'tex') inside + % waitbar function (line 81), see + % http://www.mathworks.co.uk/matlabcentral/newsreader/view_thread/115725 + % ----------------------------------------------------------------------- + hchild = get(wb,'children'); + htitle = get(hchild,'title'); + set(htitle,'Interpreter','None'); + + % Get the data from the application GUI + % ------------------------------------- + tsg = getappdata( hTsgGUI, 'tsg_data'); + + % Get an instance of dynaload objetc from file 'tsgqc_netcdf.csv' + % ----------------------------------------------------------------- + dyna = dynaload('tsgqc_netcdf.csv'); + + % Get attributes & variables list from dynaload instance + % ------------------------------------------------------ + ncd_keys = keys(dyna.DIMENSIONS); + ncv_keys = keys(dyna.VARIABLES); + nca_keys = keys(dyna.ATTRIBUTES); + + %ncv_fieldNames = get_fieldnames(ncv); + + % Display more info about write file on console + % --------------------------------------------- + fprintf('...writing %s : ', filename); + + %% Create NetCDF file: + % ------------------- + % for key = ncd_keys + % k = char(key); + % value = dyna.DIMENSIONS.(key).value; + % if ~isempty(value) + % nc(k) = value; + % elseif ~isempty(tsg.(k)) + % nc(k) = numel(tsg.(k)); + % end + % end + + % Variable dimensions + % !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + % pour l'instant, code en dur, a revoir !!!!!!!!!!!!!!!! + % ------------------- + nc('DAYD') = numel(tsg.DAYD); + + % if dimension is empty (no data), don't create, because create empty + % dimension quive it unlimited dimension, but you can only have one + % unlimited dimension in a file + % -------------------------------------------------------------------- + + % number of external SSPS or SSTP comparison + % ------------------------------------------ + if ~isempty(tsg.DAYD_EXT) + nc('DAYD_EXT') = numel(tsg.DAYD_EXT); + end + + % number of calibration coefficients + % ---------------------------------- + nc('NCOEF_CAL') = tsg.dim.CALCOEF; % 7 + + % number of linearisation coefficients + % ------------------------------------ + nc('NCOEF_LIN') = tsg.dim.LINCOEF; % 2 + + % Fixed dimensions + % ---------------- + nc('STRING256') = tsg.dim.STRING256; + nc('STRING14') = tsg.dim.STRING14; + nc('STRING8') = tsg.dim.STRING8; + nc('STRING4') = tsg.dim.STRING4; + nc('N1') = tsg.dim.N1; + + % display filename after 'Interpreter','None' initialization to prevent + % display console warning + % --------------------------------------------------------------------- + waitbar(1/50,wb,['Writing file: ' filename ' Please wait...']); + + %% Create NetCDF global attributes + % ------------------------------- + for key = nca_keys + global_att = char(key); + nc.(global_att) = tsg.(global_att); + end + + % Create NetCDF variables and attributes + % -------------------------------------- + for key = ncv_keys + + variable = char(key); + + % get structure + % ------------- + s = dyna.VARIABLES.(variable); + + ncv_fieldNames = fieldnames(s); + + % get variable dimension declare in file + % -------------------------------------- + %dimension = get(ncv, variable, ncv_fieldNames{1}); + dimension = s.dimension__; + + % get variable type declare in file + % -------------------------------------- + %nctype = get(ncv, variable, ncv_fieldNames{2}); + nctype = s.type__; + + % create netcdf variable with dimension, dimension must be a cell + % --------------------------------------------------------------- + switch nctype + case 'double' + nc{variable} = ncdouble(dimension); + case 'float' + nc{variable} = ncfloat(dimension); + case 'int' + nc{variable} = ncint(dimension); + case 'short' + nc{variable} = ncshort(dimension); + case 'byte' + nc{variable} = ncbyte(dimension); + case 'char' + nc{variable} = ncchar(dimension); + otherwise + % by default, display a warning and cast to char + % ---------------------------------------------- + warning('tsgqc_GUI:writeTSGDataNetCDF', ... + 'unknow type ''%s'' for dimension ''%s'' of ''%s'' variable.\nCheck your %s file', ... + nctype, dimension{1}, variable, file(ncv)); + + nc{variable} = ncchar(dimension); + end + + % create variable attribute, jump dimension and nctype fields % ----------------------------------------------------------- - if isnumeric(value) - - % BE CAREFUL - % bug dans la toolbox, le cast autre que double met l'attribut a 0 - % keep these lines comment !!!!!!!!!!!! + for j=1:numel(ncv_fieldNames) -% switch nctype -% case {'double', 'real', 'float', 'single'} -% value = double(value); -% case {'float', 'single'} -% value = single(value); -% case {'int', 'integer', 'int32'} -% value = int32(value); -% case {'short', 'int16'} -% value = int16(value); -% case 'int8' -% value = int8(value); -% % bug dans la toolbox, le type uint ne marche pas -% % on met int8 -% case {'byte', 'uint8'} -% value = int8(value); -% end - nc{variable}.(fieldName) = value; - - else - nc{variable}.(fieldName) = char(value); + % see new us191.netcdf toolbox, we'll not use index to get type + % but structure with special variable, eg: s.type__ + % ------------------------------------------------- + fieldName = ncv_fieldNames{j}; + value = s.(fieldName); + %nctype = get(ncv, variable, ncv_fieldNames{2}); + + % jump each internal fieldname (eg key__, data__, type__, dimension__) + % or empty fieldname + % ------------------------------------------------------------- + if ~isempty(regexp(fieldName, '__$','ONCE')) || isempty(value) + continue; + end + + % if field is empy, it's not a attribute for this netcdf variable + % --------------------------------------------------------------- + if ~isempty(value) + + % ncatt(theAttname, theAttvalue, theParent) uses the class of + % theAttvalue as theAtttype ('char' or 'double'). + % need to be cast before assignment + % ----------------------------------------------------------- + if isnumeric(value) + + % BE CAREFUL + % bug dans la toolbox, le cast autre que double met l'attribut a 0 + % keep these lines comment !!!!!!!!!!!! + + % switch nctype + % case {'double', 'real', 'float', 'single'} + % value = double(value); + % case {'float', 'single'} + % value = single(value); + % case {'int', 'integer', 'int32'} + % value = int32(value); + % case {'short', 'int16'} + % value = int16(value); + % case 'int8' + % value = int8(value); + % % bug dans la toolbox, le type uint ne marche pas + % % on met int8 + % case {'byte', 'uint8'} + % value = int8(value); + % end + nc{variable}.(fieldName) = value; + + else + nc{variable}.(fieldName) = char(value); + end + + end end - end - end -end - -%% Create data to NetCDF file: -% --------------------------- - -% Write global attributes -% ----------------------- -for i=1:numel(nca_keys) - - % get netcdf global attribute name - % -------------------------------- - global_attr = nca_keys{i}; - - nc.(global_attr) = tsg.(global_attr); -end - -% Convert Matlab julian days (datenum) to 1950 reference -% ------------------------------------------------------ -d = strmatch('DAYD',ncv_keys); -for i=1:numel(d) - tsg.(ncv_keys{d(i)}) = datenumToJulian(tsg.(ncv_keys{d(i)})); -end - -% shift to [-180 180] longitude range if needed -% --------------------------------------------- -if ~isempty(tsg.indcross) - tsg.LONX = mod(tsg.LONX+180,360)-180; -end - -% Write measurements (tsg.variables) -% ---------------------------------- -for i=1:numel(ncv_keys) - - % get netcdf variable name - % ------------------------ - variable = ncv_keys{i}; - - % display waitbar - % --------------- - waitbar( i/numel(ncv_keys), wb, ['writing ' variable ' variable']); - - % get ncvar type object nv - % ------------------------ - nv = nc{variable}; - - % be careful, if dimension DAYD_EXT not defined (no sample data), - % nv is empty - % --------------------------------------------------------- - if ~isempty(nv) - - % set autonan mode (FillValue_) - % ----------------------------- - nv = autonan(nv,1); - - % tsg variable is not empty, fill nv with it - % ------------------------------------------ - if ~isempty(tsg.(variable)) - - % fill netcdf variables nv with corresponding tsg variable - % -------------------------------------------------------- - nv(:) = tsg.(variable); - - % In case of unlimited dimension, for example only, use: - % nc{variable}(1:length(tsg.(variable))) = tsg.(variable) - % ------------------------------------------------------- - - else - - % if default_value defined, fill nv with it - % ----------------------------------------- - if ~isempty(nv.default_value(:)) - % a verifier, doit marcher pour tout les type de variable - %nv(:) = int8(nv.default_value(:)); - nv(:) = nv.default_value(:); + + %% Create data to NetCDF file: + % --------------------------- + + % Write global attributes + % ----------------------- + for i=1:numel(nca_keys) + + % get netcdf global attribute name + % -------------------------------- + global_attr = nca_keys{i}; + + nc.(global_attr) = tsg.(global_attr); + end + + % Convert Matlab julian days (datenum) to 1950 reference + % ------------------------------------------------------ + d = strmatch('DAYD',ncv_keys); + for i=1:numel(d) + tsg.(ncv_keys{d(i)}) = datenumToJulian(tsg.(ncv_keys{d(i)})); + end + + % shift to [-180 180] longitude range if needed + % --------------------------------------------- + if ~isempty(tsg.indcross) + tsg.LONX = mod(tsg.LONX+180,360)-180; + end + + % Write measurements (tsg.variables) + % ---------------------------------- + for i=1:numel(ncv_keys) + + % get netcdf variable name + % ------------------------ + variable = ncv_keys{i}; + + % display waitbar + % --------------- + waitbar( i/numel(ncv_keys), wb, ['writing ' variable ' variable']); + + % get ncvar type object nv + % ------------------------ + nv = nc{variable}; + + % be careful, if dimension DAYD_EXT not defined (no sample data), + % nv is empty + % --------------------------------------------------------- + if ~isempty(nv) + + % set autonan mode (FillValue_) + % ----------------------------- + nv = autonan(nv,1); + + % tsg variable is not empty, fill nv with it + % ------------------------------------------ + if ~isempty(tsg.(variable)) + + % fill netcdf variables nv with corresponding tsg variable + % -------------------------------------------------------- + nv(:) = tsg.(variable); + + % In case of unlimited dimension, for example only, use: + % nc{variable}(1:length(tsg.(variable))) = tsg.(variable) + % ------------------------------------------------------- + + else + + % if default_value defined, fill nv with it + % ----------------------------------------- + if ~isempty(nv.default_value(:)) + % a verifier, doit marcher pour tout les type de variable + %nv(:) = int8(nv.default_value(:)); + nv(:) = nv.default_value(:); + end + + end + end - + end + + % Close waitbar + % ------------- + close(wb) + + % Close NetCDF file + % ----------------- + endef(nc) + close(nc) + + % Display time to write file on console + % ------------------------------------- + t = toc; fprintf('...done (%6.2f sec).\n\n',t); + + % Everything OK + % ------------- + error = 1; + + end % end of nested function nc_write_old +% use native toolbox +netcdf since R2008b +% --------------------------------------- + function write_netcdf_native + + % Get the data from the application GUI + % ------------------------------------- + tsg = getappdata( hTsgGUI, 'tsg_data'); + + % Get an instance of dynaload object from file 'tsgqc_netcdf.csv' + % ----------------------------------------------------------------- + nc = netcdf_native('tsgqc_netcdf.csv'); + + % loop over all variables and get associated value in tsg structure + % ----------------------------------------------------------------- + for key = keys(nc.VARIABLES) + var = char(key); + nc.VARIABLES.(var).data__ = tsg.(var); + end + + % loop over all global attributes and get associated value in tsg + % structure + % ----------------------------------------------------------------- + for key = keys(nc.ATTRIBUTES) + att = char(key); + nc.ATTRIBUTES.(att).data__ = tsg.(att); + end + + % try + + % write netcdf file with and display waibar (boolean) + % --------------------------------------------------- + write( nc, filename, 'NC_CLOBBER', true ); + + % Everything OK + % ------------- + error = 1; + % catch + % error = -1; + % end + end -end - -% Close waitbar -% ------------- -close(wb) - -% Close NetCDF file -% ----------------- -endef(nc) -close(nc) +end % end od writeTSGDataNetCDF -% Display time to write file on console -% ------------------------------------- -t = toc; fprintf('...done (%6.2f sec).\n\n',t); - -% Everything OK -% ------------- -error = 1; - -end -- GitLab