Home > atmlab > collocations > FieldCopier.m

FieldCopier

PURPOSE ^

SYNOPSIS ^

This is a script file.

DESCRIPTION ^

CROSS-REFERENCE INFORMATION ^

This function calls: This function is called by:

SUBFUNCTIONS ^

DOWNLOAD ^

FieldCopier.m

SOURCE CODE ^

0001 classdef FieldCopier < AssociatedDataset
0002     % class to copy fields from original datasets to collocated datasets
0003     %
0004     % A common task associated with <a href="matlab:help CollocatedDataset">collocating</a> different <a href="matlab:help SatDataset">dataset</a>s
0005     % is to copy fields from the original datasets to use with the collocations.
0006     % FieldCopier implements <a href="matlab:help AssociatedDataset">AssociatedDataset</a>, meaning that it has at
0007     % least all the properties and methods that are indicated by AssociatedDataset.
0008     % Therefore, it can be used wherever documentation describes the need
0009     % for an AssociatedDataset.
0010     %
0011     % To copy fields as-is, create an instance of FieldCopier and pass this
0012     % on to the CollocatedDataset methods <a href="matlab:help CollocatedDataset/collocate_granule">collocate_granule</a>,
0013     % <a href="matlab:help CollocatedDataset/collocate_date">collocate_date</a>, <a href="matlab:help CollocatedDataset/collocate_and_store_date">collocate_and_store_date</a>, or <a href="matlab:help CollocatedDataset/collocate_and_store_date_range">collocate_and_store_date_range</a>.
0014     % Fieldnames are taken upon creation from two structures passed on to the
0015     % constructor: one for the primary, and one for the secondary. The
0016     % fieldnames of those structures are passed on to the respective
0017     % <a href="matlab:help SatDataset/reader">reader</a>s, who are expected to return structures with the
0018     % appropiate data stored in them with the same fieldnames.
0019     % The values for the fields in the structures passed on to the
0020     % constructor contain information on how to store the data, among other
0021     % things. See also the example below.
0022     %
0023     % When a FieldCopier is created, it is automatically registered with
0024     % the <a href="matlab:help CollocatedDataset">CollocatedDataset</a> to which it belongs, in <a href="matlab:help CollocatedDataset/associated">CollocatedDataset.associated</a>.
0025     % Therefore, only for some tasks involving a FieldCopier, one needs to
0026     % pass the reference to the method using it. For other tasks, this is
0027     % taken care of automatically.
0028     %
0029     % FieldCopier Properties:
0030     %
0031     %   fieldstruct_primary -   Describes what is copied from primary
0032     %   fieldstruct_secondary - Describes what is copied from secondary
0033     %  (remaining properties inherited from <a href="matlab:help AssociatedDataset">AssociatedDataset</a>.
0034     %   Use <a href="matlab:properties FieldCopier">properties</a> for a complete listing)
0035     %
0036     % FieldCopier Methods:
0037     %
0038     %   FieldCopier -           Create FieldCopier object
0039     %  (remaining methods implemented based on <a href="matlab:help AssociatedDataset">AssociatedDataset</a>)
0040     %
0041     % Example:
0042     %
0043     % % obtain object for <a href="matlab:help CollocatedDataset">CollocatedDataset</a>
0044     % >> D = <a href="matlab:help datasets">datasets</a>;
0045     % >> mhscpr = D.CollocatedDataset__1_mhs__2_cpr;
0046     % % define structures to be passed on when creating FieldCopier
0047     % % For this to work, the <a href="matlab:help SatDataset">SatDataset</a> in <a href="matlab:help CollocatedDataset/primary">mhscpr.primary</a>
0048     % % must have a <a href="matlab:help SatDataset/reader">reader</a> that accepts {'tb'} as a second argument,
0049     % % and then returns a structure containing a field 'tb' with appropriate
0050     % % data. Similarly, <a href="matlab:help CollocatedDataset/secondary">mhscpr.secondary</a>.reader shall take
0051     % % {'RO_ice_water_path'} as a second argument and return a structure
0052     % % with a field 'RO_ice_water_path'.
0053     % >> fc1 = struct('tb', struct('type', 'float', 'atts', struct('long_name', 'My BT')));
0054     % >> fc2 = struct('RO_ice_water_path', struct('type', 'int', 'atts', struct('long_name', 'My IWP')));
0055     % % create FieldCopier object with minimum of information
0056     % >> fc = FieldCopier(mhscpr, fc1, fc2, 'basedir', '/tmp/testing', ...
0057     %                     'subdir', '$YEAR4/$MONTH/$DAY', ...
0058     %                     'filename', 'collocations_$SAT.nc.gz');
0059     % % Collocate along with FieldCopier
0060     % >> mhscpr.collocate_and_store_date_range([2009 7 1], [2009 7 5], ...
0061     %                                          'noaa18', '', {fc});
0062     %
0063     % This code now creates two files for each date in the daterange (ten
0064     % files in total): one for the <a href="matlab:help CollocatedDataset">CollocatedDataset</a>, containing the very
0065     % core information, and one for the FieldCopier, containing two fields;
0066     % one copied from the primary <a href="matlab:help SatDataset">dataset</a>, and one copied from the secondary
0067     % <a href="matlab:help SatDataset">dataset</a>.
0068     %
0069     % TODO:
0070     %  - set some (but not all!!) clever properties for NetCDF
0071     %
0072     % See also: AssociatedDataset (abstract superclass), CollocatedDataset, SatDataset, Collapser, datasets
0073     %
0074     % See also the Collocation User's Guide.
0075     %
0076     % $Id: FieldCopier.m 8522 2013-07-02 13:13:41Z gerrit $
0077     
0078     properties (Transient, SetAccess = protected)
0079         
0080         % Structure with fields corresponding to primary
0081         %
0082         % The names of the fields correspond to fields as they will be
0083         % stored in the collocated NetCDF file. Unless otherwise indicated,
0084         % the same field name will be read from the primary dataset.
0085         %
0086         % The value for each field is itself a structure containing
0087         % information on (among other things) how to store the
0088         % field in the final NetCDF file. This structure can have
0089         % the following fields:
0090         %
0091         %   type    string, NetCDF-3 type, such as 'int',
0092         %           'float'. Full list <a href="http://www.unidata.ucar.edu/software/netcdf/docs/netcdf-c/NetCDF_002d3-Variable-Types.html#NetCDF_002d3-Variable-Types">in NetCDF docs</a>.
0093         %           This field is mandatory.
0094         %
0095         %   atts    structure, fields and values are all
0096         %           strings and are stored as NetCDF
0097         %           attributes. I suggest to use <a href="http://cf-pcmdi.llnl.gov/documents/cf-conventions/1.6/cf-conventions.html">CF</a>
0098         %           conventions.
0099         %
0100         %           NOTE: in the future, attributes not
0101         %           provided may possibly be guessed based on
0102         %           original data.
0103         %
0104         %   dataset string. This can be used in case the field
0105         %           is not in the same dataset, but in a different
0106         %           dataset based on the same instrument. This dataset
0107         %           must have exactly the same number of scanlines and
0108         %           scanpositions. For a dataset with less data but the
0109         %           same number of collocations (for example, a different
0110         %           instrument on the same satellite), use
0111         %           <a href="matlab:help FieldMultiInstrumentCopier">FieldMultiInstrumentCopier</a>
0112         %
0113         %   realname string. This can be passed if the field
0114         %           should be stored under a different name in
0115         %           the FieldCopier dataset than in the
0116         %           original dataset, for example, when
0117         %           different fields have the same name. Here,
0118         %           'realname' is the name as stored in the
0119         %           original data, and the structure fieldname
0120         %           is the name as stored in the FieldCopier data.
0121         fieldstruct_primary = struct;
0122         
0123         % Structure with fields corresponding to secondary.
0124         %
0125         % See <a href="matlab:help FieldCopier/fieldstruct_primary">fieldstruct_primary</a> for info.
0126         fieldstruct_secondary = struct;
0127         
0128         % Structure with all fields and info on how to store the NetCDF
0129         %
0130         % See also <a href="matlab:help AssociatedDataset.members">AssociatedDataset.members</a>
0131         %members = struct(); % set by constructor
0132         
0133         % Parent <a href="matlab:help CollocatedDataset">CollocatedDataset</a>.
0134         %
0135         % See also <a href="matlab:help AssociatedDataset.parent">AssociatedDataset.parent</a>
0136         parent = [];
0137         
0138         % FieldCopier has no dependencies
0139         %
0140         % See also <a href="matlab:help AssociatedDataset.dependencies">AssociatedDataset.dependencies</a>
0141         dependencies = {};
0142     end
0143     
0144     properties (Access = private)
0145         oldlocalcols;
0146     end
0147     
0148     methods
0149         %% constructor
0150         
0151         function self = FieldCopier(cd, fieldstruct1, fieldstruct2, varargin)
0152             % Creates a FieldCopier object
0153             %
0154             % Used to create a <a href="matlab:help FieldCopier">FieldCopier</a> object.
0155             %
0156             % FORMAT
0157             %
0158             %   fc = FieldCopier(cd, fs1, fs2, 'name', 'MyFC', ...
0159             %                    'basedir', '/some/where', ...
0160             %                    ...) % etc.
0161             %
0162             % IN
0163             %
0164             %   cd      CollocatedDataset   <a href="matlab:help CollocatedDataset">CollocatedDataset</a> that this FieldCopier belongs to.
0165             %
0166             %   fs1     structure
0167             %
0168             %       Full definition of fields and information on how to
0169             %       store. For full instructions on fields and values,
0170             %       see <a href="matlab:help FieldCopier/fieldstruct_primary">property documentation</a>.
0171             %
0172             %   fs2     structure
0173             %
0174             %       Like fs1, but corresponding to the secondary.
0175             %
0176             %   ...remaining arguments passed on to <a href="matlab:help SatDataset">SatDataset</a>,
0177             %   so those indicate name, where to store, etc.
0178             %
0179             % OUT
0180             %
0181             %   A FieldCopier object.
0182             
0183             self = self@AssociatedDataset(cd, {}, varargin{:}); % call parent constructor
0184             
0185             self.fieldstruct_primary = fieldstruct1;
0186             self.fieldstruct_secondary = fieldstruct2;
0187             
0188             
0189             % check consistence of fieldstructs
0190             
0191             allfields = catstruct(fieldstruct1, fieldstruct2);
0192             assert(length(fieldnames(fieldstruct1))+length(fieldnames(fieldstruct2))==length(fieldnames(allfields)), ...
0193                 ['atmlab:' mfilename ':duplicates'], [...
0194                 'Duplicate fieldnames between datasets are not permitted, but were found. ' ...
0195                 'I suggest to use the ''realname'' attribute for the <a href="matlab:help FieldCopier/fieldstruct_primary">fieldstruct</a>.']);
0196             self.members = allfields;
0197             
0198         end
0199     end
0200     
0201     methods (Access = {?SatDataset})
0202         %% implementation of abstract methods
0203         
0204         function args = primary_arguments(self, varargin)
0205             fields = optargs(varargin, {'all'});
0206             args = self.arguments(self.fieldstruct_primary, self.parent.primary, fields);
0207         end
0208         
0209         function args = secondary_arguments(self, varargin)
0210             fields = optargs(varargin, {'all'});
0211             args = self.arguments(self.fieldstruct_secondary, self.parent.secondary, fields);
0212         end
0213         
0214         function fields = fields_needed_for_dependency(~, ~, ~)
0215             fields = {}; % no dependencies
0216         end
0217         
0218         function [result, localcols] = process_granule(self, processed_core, data1, date1, spec1, data2, date2, spec2, ~, ~, varargin)
0219             fields = optargs(varargin, {'all'});
0220             allnames = fieldnames(self.members);
0221             if isequal(fields, 'all')
0222                 localnames = allnames;
0223             else
0224                 %localnames = intersect(allnames, fields);
0225                 % intersect destroys order, order is important...
0226                 localnames = allnames(cellfun(@(X)ismember(X, fields), allnames));
0227                 rqre_subset(fields, allnames); % if this fails, some fields do not exist
0228             end
0229             n_inprim = length(intersect(fieldnames(self.fieldstruct_primary), localnames));
0230             dimsizes = struct();
0231             % other datasets that may be required (structs filled as needed)
0232             D = datasets();
0233             % `also_read` keeps track of fields that need to be read from
0234             % sibling-datasets for the primary and the secondary,
0235             % respectively
0236             also_read = {struct(), struct()};
0237             dat_other = {struct(), struct()};
0238             reflat = {[], []};
0239             % will loop through all the names in total 3 times...
0240             % in the first loop, check:
0241             % - what names should be used to read
0242             % - if data should be from
0243             % sibling-datasets, i.e. datasets not occuring in the original
0244             % collocation, but on the same size (e.g. CPR RO or RVOD)
0245             for i = 1:length(localnames)
0246                 fieldnam = localnames{i};
0247                 if ~isfield(self.members.(fieldnam), 'realname')
0248                     self.members.(fieldnam).realname = fieldnam;
0249                 end
0250                 % if it has a 'dataset' defined, the field may be from a
0251                 % sibling-dataset
0252                 if isfield(self.members.(fieldnam), 'dataset')
0253                     ds = self.members.(fieldnam).dataset;
0254                     if i <= n_inprim
0255                         j = 1;
0256                     else
0257                         j = 2;
0258                     end
0259                     % add field to read from this DS to
0260                     % also_read{j}.(dataset_name)
0261                     % here, store the realname as it has in the original
0262                     % dataset, because that will in any case be the
0263                     % fieldname in the data-structure returned by
0264                     % .read_granule.
0265                     if ~isfield(also_read{j}, ds.name)
0266                         logtext(atmlab('OUT'), ...
0267                             'Shall read field %s (stored as %s) from sibling-dataset %s\n', ...
0268                             fieldnam, self.members.(fieldnam).realname, ds.name);
0269                         also_read{j}.(ds.name) = {self.members.(fieldnam).realname};
0270                     else
0271                         logtext(atmlab('OUT'), ...
0272                             'Shall also read field %s (stored as %s) from sibling-dataset %s\n', ...
0273                             fieldnam, self.members.(fieldnam).realname, ds.name);
0274                         also_read{j}.(ds.name) = [also_read{j}.(ds.name) self.members.(fieldnam).realname];
0275                     end
0276                 end % end of `if field from sibling`
0277             end % end of loop through all fields
0278             
0279             % Loop through primary, secondary to do two things:
0280             % - read siblings, if any
0281             % - set reflat:
0282             % Later, we may need to reshape some fields, for example if
0283             % they have more than one measurement per footprint. Therefore,
0284             % we need to know the number of scanlines and the number of
0285             % measurements per scanline. For this, we use the field `lat`
0286             % because it should always be there and have exactly one value
0287             % per footprint.
0288 
0289             for j = 1:2
0290                 if j == 1
0291                     dt = date1;
0292                     spc = spec1;
0293                     dat = data1; % for setting reflat
0294                 else
0295                     dt = date2;
0296                     spc = spec2;
0297                     dat = data2;
0298                 end
0299                 siblings_to_read = fieldnames(also_read{j});
0300                 for k = 1:length(siblings_to_read)
0301                     dsnm = siblings_to_read{k};
0302                     % read fields from this dataset as indicated by
0303                     % also_read{j}.(dataset_name)
0304                     logtext(atmlab('OUT'), ...
0305                         'Reading dataset %s to grab %d fields: %s\n', ...
0306                         dsnm, length(also_read{j}.(dsnm)), strjoin(also_read{j}.(dsnm), ', '));
0307                     % in this reading, do not remove duplicates, or there will
0308                     % be a mis-match between indices as provided by the
0309                     % collocations and indices as determined here
0310                     try
0311                         dat_other{j}.(dsnm) = D.(dsnm).read_granule(dt, spc, also_read{j}.(dsnm), false, false);
0312                     catch ME
0313                         switch ME.identifier
0314                             case {'atmlab:find_granule_by_datetime', 'atmlab:exec_system_cmd:shell', 'MATLAB:imagesci:validate:fileOpen'}
0315                                 warning(['atmlab:' mfilename ':nosibling'], ...
0316                                     'I tried to collect fields from dataset %s, but failed: %s\n', ...
0317                                     dsnm, ME.message);
0318                                 dat_other{j}.(dsnm) = 'failed';    
0319                             otherwise
0320                                 ME.rethrow();
0321                         end
0322                     end
0323                     % if this sibling has a reflat, store it in case we
0324                     % need it later
0325                     if isfield(dat_other{j}.(dsnm), 'lat')
0326                         % verify that no different reflat already there
0327                         if ~isempty(reflat{j}) && nanmax(abs(reflat{j} - dat_other{j}.(dsnm).lat)) > 0.01
0328                             logtext(atmlab('ERR'), ...
0329                                 ['Inconsistent latitude-fields between ' ...
0330                                  'sibling-datasets!  Up to %f different...\n'], ...
0331                                 nanmax(abs(reflat{j} - dat_other{j}.(dsnm).lat)));
0332                             dat_other{j}.(dsnm) = 'failed';
0333                         else
0334                             reflat{j} = dat_other{j}.(dsnm).lat;
0335                         end
0336                     end
0337                 end % loop through all siblings to read for prim or sec
0338                 
0339                 if isfield(dat, 'lat')
0340                     if ~isempty(reflat{j}) && nanmax(abs(reflat{j} - dat.lat)) > 0.01
0341                         logtext(atmlab('ERR'), ...
0342                             ['Inconsistent latitude-fields in core vs. ' ...
0343                              'sibling-dataset!\n']);
0344                         dat_other{j}.(dsnm) = 'failed';
0345                     else
0346                         reflat{j} = dat.lat;
0347                     end
0348                 end
0349             end % loop through primary, secondary
0350             
0351             % now all needed datasets have been read
0352             
0353             % In the second loop, we reshape data so that they all have
0354             % a dimension equal to the number of scan positions, and we
0355             % collect dimension data for other fields.  We also define the
0356             % localcols structure used to address the columns in 'result'
0357             % (note that this should be equal to self.cols if getting all
0358             % fields)
0359 
0360             localcols = struct();
0361             n_tot = 0;
0362             for i = 1:length(localnames)
0363                 fieldnam = localnames{i};
0364                 if i<=n_inprim
0365                     j = 1;
0366                     dat = data1;
0367                 else
0368                     dat = data2;
0369                     j = 2;
0370                 end
0371 %                 if isempty(fieldnames(dat))
0372 %                     logtext(atmlab('OUT'), 'No fields from either primary or secondary, hopefully sibling has same number of lats\n');
0373 %                     % test if they both are there. If there is no primary
0374 %                     % or secondary dataset, on e will be empty
0375 %                     testE = [~isempty(fieldnames(dat_other{1})) ~isempty(fieldnames(dat_other{2}))];
0376 %                     if all(testE)
0377 %                         reflat = cellfun(@(x) x.(self.members.(fieldnam).dataset.name).lat, dat_other, 'UniformOutput', false);
0378 %                     else
0379 %                         reflat = {dat_other{testE}.(self.members.(fieldnam).dataset.name).lat};
0380 %                         % workaround since relat{2} is called for later on
0381 %                         reflat(2) = reflat;
0382 %                     end
0383 %                 else
0384 %                     reflat = {data1.lat, data2.lat};
0385 %                 end
0386                 n_scanlines = size(reflat{j}, 1);
0387                 n_scanpos = size(reflat{j}, 2);
0388                 
0389                 % data either from prim/sec or from 'sibling'
0390                 if isfield(self.members.(fieldnam), 'dataset')
0391                     if isequal(dat_other{j}.(self.members.(fieldnam).dataset.name), 'failed')
0392                         % this happens if core existed, but sibling didn't
0393                         % remainder of loop is only used to enlarge fdata
0394                         % or set NetCDF props, problem persists further down
0395                         % when collecting data.
0396                         % Need to set localcols before proceeding.
0397                         % Alternative would be to look into persistency,
0398                         % did we have a previous localcols?
0399                         if isfield(self.members,  fieldnam) && isfield(self.members.(fieldnam), 'dims')
0400                             n = self.members.(fieldnam).dims{2};
0401                         elseif ~isempty(self.oldlocalcols)
0402                             n = length(self.oldlocalcols.(fieldnam));
0403                         else
0404                             warning(['atmlab:' mfilename ':unknownsize'], ...
0405                                 ['I couldn''t find field %s and ' ...
0406                                  'I''m not sure about it''s size. ' ...
0407                                  'I''ll guess 1 per measurement. ' ...
0408                                  'If I''m wrong, you''ll get trouble. ' ...
0409                                  'Consider defining %s.members.%s.dims.'], ...
0410                                  self.name, fieldnam);
0411                             n = 1;
0412                         end
0413                         localcols.(fieldnam) = (n_tot+1):(n_tot+n);
0414                         n_tot = n_tot + n;
0415                         continue
0416                     else
0417                         fdata = dat_other{j}.(self.members.(fieldnam).dataset.name).(self.members.(fieldnam).realname);
0418                     end
0419                 else
0420                     fdata = dat.(self.members.(fieldnam).realname);
0421                 end
0422                 % special case: fields with one value per scanline
0423                 if size(fdata, 1) == n_scanlines && size(fdata, 2) == 1
0424                     fdata = repmat(fdata, [1 n_scanpos]);
0425                 end
0426                 % 'n' is the number of measurements per lat/lon, e.g. the
0427                 % number of channels, number of height-bins, etc. Should be
0428                 % scalar...
0429                 n = numel(fdata)/(n_scanlines*n_scanpos);
0430                 assert(iswhole(n), ['atmlab:' mfilename ':dimensions'], ...
0431                     ['Dimension mismatch: scanlines: %d, scanpos: %d, ' ...
0432                     'field %s (stored as %s): %s'], n_scanlines, n_scanpos, fieldnam, ...
0433                     self.members.(fieldnam).realname, num2str(size(fdata)));
0434                 %% if needed, add dimension info to self.members
0435                 if (n>1)
0436                     % need to specify dimension in NetCDF, if it doesn't
0437                     % exist yet, we will need to create the dimension. To
0438                     % tell the writing routine that it needs to do so, add
0439                     % a field with a name and a number, but only if this
0440                     % dimension size is new
0441                     
0442                     % iff self.members.(fieldnam).dims is there
0443                     if ~(isfield(self.members, fieldnam) && ...
0444                             isfield(self.members.(fieldnam), 'dims'))
0445                         % find dimension name from dimension size
0446                         alldimnames = fieldnames(dimsizes);
0447                         alldimvalues = structfun(@(x)x, dimsizes);
0448                         if ismember(n, alldimvalues)
0449                             nm = alldimnames{alldimvalues==n};
0450                         else
0451                             nm = sprintf('AUTO_DIM%d_%d', length(alldimvalues)+1, n);
0452                             dimsizes.(nm) = n;
0453                         end
0454                         % store dimension name and size
0455                         self.members.(fieldnam).dims = {nm, n};
0456                     end
0457                     
0458                 end
0459                 localcols.(fieldnam) = (n_tot+1):(n_tot+n);
0460                 n_tot = n_tot + n;
0461             end
0462             
0463             self.members2cols();
0464             if isequal(fields, 'all')
0465                 assert(isequal(localcols, self.cols), ...
0466                     ['atmlab:' mfilename ':wrongcols'], ...
0467                     'Ambiguity in cols-structure.  This is a bug.  Stop');
0468             end
0469             self.oldlocalcols = localcols;
0470             
0471             ncollocs = size(processed_core, 1);
0472             nfields = max(cell2mat(struct2cell(localcols).'));
0473             %fields = fieldnames(self.cols);
0474             result = nan*zeros(ncollocs, nfields, self.mattype);
0475             %n_inprim = length(fieldnames(self.fieldstruct_primary));
0476             
0477             r1 = processed_core(:, self.parent.cols.LINE1);
0478             r2 = processed_core(:, self.parent.cols.LINE2);
0479             c1 = processed_core(:, self.parent.cols.POS1);
0480             c2 = processed_core(:, self.parent.cols.POS2);
0481 %            if (self.needs_primary_data() || ~isempty(fieldnames(dat_other{1})))
0482             if ~isempty(reflat{1})
0483                 i1 = sub2ind(size(reflat{1}), r1, c1);
0484             end
0485 %            if (self.needs_secondary_data() || ~isempty(fieldnames(dat_other{2})))
0486             if ~isempty(reflat{2})
0487                 i2 = sub2ind(size(reflat{2}), r2, c2);
0488             end
0489             
0490             % in the third loop, collect the data
0491             
0492             for i = 1:length(localnames)
0493                 field = localnames{i};
0494                 
0495                 % an empty reflat means I couldn't read any core or
0496                 % sibling, so I won't need the indices either
0497                 if i <= n_inprim
0498                     if ~isempty(reflat{1})
0499                         data = data1;
0500                         ii = i1;
0501                     end
0502                     j = 1;
0503                 else
0504                     if ~isempty(reflat{2})
0505                         data = data2;
0506                         ii = i2;
0507                     end
0508                     j = 2;
0509                 end
0510                 
0511                 % data either from prim/sec or from 'sibling'
0512                 if isfield(self.members.(field), 'dataset')
0513                     %logtext(atmlab('OUT'), ...
0514                     %    'Grabbing field %s from sibling-dataset %s\n', ...
0515                     %    field, self.members.(field).dataset.name);
0516                     if isequal(dat_other{j}.(self.members.(field).dataset.name), 'failed')
0517                         logtext(atmlab('ERR'), ...
0518                             ['But there''s a field I failed to read :(, ' ...
0519                             'so I''ll write a filler instead\n']);
0520                         if ~isfield(self.members.(field).atts, 'missing_value')
0521                             error(['atmlab:' mfilename ':missingmissing'], ...
0522                                 ['You asked me to collect field %s from sibling ' ...
0523                                 'dataset %s, but I couldn''t find any datafile (see above). ' ...
0524                                 'Therefore, I decided to set the ''filler'' value as ' ...
0525                                 'ought to be defined in %s.members.%s.atts.missing_value. Unfortunately, ' ...
0526                                 'there is no such field `missing_value`, so I don''t know how to ' ...
0527                                 'flag the missing data. Please define %s.members.%s.atts.missing_value ' ...
0528                                 'and try again.'], ...
0529                                 field, self.members.(field).dataset.name, ...
0530                                 self.name, field, self.name, field);
0531                         end
0532                         result(:, localcols.(field)) = self.members.(field).atts.missing_value;
0533                         %result(:, self.cols.(field)) = self.members.(field).atts.missing_value;
0534                         continue
0535                     end
0536                     fdata = dat_other{j}.(self.members.(field).dataset.name).(self.members.(field).realname);
0537                 else
0538                     fdata = data.(self.members.(field).realname);
0539                 end
0540                 
0541                 % if this fails, data.(field) may not be a column-vector as
0542                 % required
0543                 sz = size(fdata);
0544                 if isequal(sz, size(reflat{j}))
0545                     DD = fdata(:); % 1 point per lat/lon
0546                 elseif sz(1) == size(reflat{j}, 1) && sz(2) == 1 % e.g. time
0547                     DD = repmat(fdata, [1 size(data.lat, 2)]);
0548                     DD = DD(:);
0549                 else % multi-point per lat/lon
0550                     DD = reshape(fdata, prod(sz(1:end-1)), sz(end));
0551                 end
0552                 result(:, localcols.(field)) = DD(ii, :);
0553                 %result(:, self.cols.(field)) = DD(ii, :);
0554                 %result(:, self.cols.(field)) = data.(field)(ii, :);
0555             end
0556         end
0557         
0558         function out = needs_primary_data(self, varargin)
0559             fields = optargs(varargin, {'all'});
0560             out = self.needs_data(self.fieldstruct_primary, self.parent.primary, fields);            
0561         end
0562         
0563         function out = needs_secondary_data(self, varargin)
0564             fields = optargs(varargin, {'all'});
0565             out = self.needs_data(self.fieldstruct_secondary, self.parent.secondary, fields);            
0566         end
0567         
0568     end
0569     
0570     methods (Static, Access = private)
0571         function fargs = fieldargs(fieldstruct, coreparent)
0572             % determine from a fieldstruct what arguments to pass
0573             names = fieldnames(fieldstruct);
0574             fargs = {};
0575             k = 0;
0576             for i = 1:length(names)
0577                 if ~isfield(fieldstruct.(names{i}), 'dataset') || ...
0578                         strcmp(fieldstruct.(names{i}).dataset.name, coreparent.name)
0579                     k = k + 1;
0580                     if isfield(fieldstruct.(names{i}), 'realname')
0581                         fargs{k} = fieldstruct.(names{i}).realname; %#ok<AGROW>
0582                     else
0583                         fargs{k} = names{i}; %#ok<AGROW>
0584                     end
0585                 end
0586             end
0587         end
0588         
0589         function nm = get_realname_from_fieldstruct(nm)
0590             
0591         end
0592     end
0593     
0594     methods (Access = private)
0595         function out = needs_data(self, fs, parent, fields)
0596             % helper for needs_primary_data, needs_secondary_data
0597             all_fields = self.fieldargs(fs, parent);
0598             if isequal(fields, 'all')
0599                 fields = all_fields;
0600             end
0601             out = ~isempty(intersect(all_fields, cellfun(@(X) safegetfield(self.members.(X), 'realname', X), fields, 'UniformOutput', false)));
0602         end           
0603             
0604         function args = arguments(self, fs, parent, fields)
0605                         
0606             fargs = self.fieldargs(fs, parent);
0607             if isequal(fields, 'all')
0608                 args = fargs;
0609             else
0610                 %args = intersect(cellfun(@(X) safegetfield(self.members.(X), 'realname', X), fields, 'UniformOutput', false), fargs);
0611                 args = intersect(...
0612                     cellfun(...
0613                         @(X) safegetfield(self.members.(X), 'realname', X), ...
0614                         intersect(fieldnames(self.members), fields), ...
0615                         'UniformOutput', false), ...
0616                     fargs);
0617             end
0618         % cellfun(@(X) safegetfield(self.members.(X), 'realname', X), intersect(fields, fieldnames(self.members)), 'UniformOutput', false)
0619         end
0620     end
0621 end

Generated on Mon 15-Sep-2014 13:31:28 by m2html © 2005