classdef FieldMultiInstrumentCopier < AssociatedDataset % Add others % % This class is for getting fields from other instruments on the same % satellite that do not have the same resolution but do have a 1-to-1 % relation with the instrument originally used for collocating. % An example is to collocate with mhs, then get hirs and amsu-a. % This does not apply if there are several instruments or datasets % with exactly the same footprints; in this case, one should use an % ordinary FieldCopier and use siblings instead. % % This is an abstract class. One implementation is % AssociatedPOESPlusCPR. % % WORK IN PROGRESS % % FIXME DOC % $Id$ properties (Transient, Abstract) % For instruments sharing a platform with the primary % % FIXME DOC fieldcopier_other_primary % For instruments sharing a platform with the secondary % % FIXME DOC fieldcopier_other_secondary end properties (Transient) % This is for fields that are copied from the core % % FIXME DOC fieldcopier_core % FIXME DOC fieldstruct_other end properties (Transient, SetAccess = protected) %members parent dependencies end methods (Abstract, Access = protected) % FIXME DOC [newline, newpos] = translate(self, processed_core, data_orig, dsname, data_other) end methods %% constructor function self = FieldMultiInstrumentCopier(varargin) self = self@AssociatedDataset(varargin{:}); % if isempty(self.members) % self.members = struct(); % temporary default % end end end methods (Access = {?SatDataset}) %% implementation of abstract methods function args = primary_arguments(self, varargin) args = self.fieldcopier_core.primary_arguments(varargin{:}); end function args = secondary_arguments(self, varargin) args = self.fieldcopier_core.secondary_arguments(varargin{:}); end function bool = needs_primary_data(self, varargin) fields = optargs(varargin, {'all'}); if isequal(fields, 'all') bool = self.fieldcopier_core.needs_primary_data(fields); else bool = self.fieldcopier_core.needs_primary_data(... intersect(fieldnames(self.fieldcopier_core.members), fields)); end end function bool = needs_secondary_data(self, varargin) fields = optargs(varargin, {'all'}); if isequal(fields, 'all') bool = self.fieldcopier_core.needs_secondary_data(fields); else bool = self.fieldcopier_core.needs_secondary_data(... intersect(fieldnames(self.fieldcopier_core.members), fields)); end end function fields = fields_needed_for_dependency(~, ~, ~) fields = {}; % no dependencies end function [out, localcols] = process_granule(self, processed_core, data1, date1, spec1, data2, date2, spec2, dependencies, depcols, fields) D = datasets(); %% copy fields for original instruments if isequal(fields, 'all') fields_core = fieldnames(self.fieldcopier_core.members); else fields_core = intersect(fieldnames(self.fieldcopier_core.members), fields); end [out_core, core_fc_cols] = self.fieldcopier_core.process_granule(... processed_core, data1, date1, spec1, data2, date2, spec2, ... dependencies, depcols, fields_core); if isequal(fields, 'all') assert(isequal(self.fieldcopier_core.cols, core_fc_cols), ... ['atmlab:' mfilename ':wrongcols'], ... 'Bug in cols :('); fields = fieldnames(self.members); end allmembers = self.fieldcopier_core.members; %% get fields for other instruments on primary % (ab)use a FieldCopier also for other instruments on primary % by editing processed_core and putting in the line/pos for the % other instruments instead of the original ones % the abstract method 'translate' is responsible for actually % performing this conversion modes = {'primary', 'secondary'}; for k = 1:length(modes) m = modes{k}; other_instruments{k} = fieldnames(self.(['fieldcopier_other_' m])); out_other{k} = {}; switch m % sometimes it's the primary where other instruments % are considered, sometimes it's the secondary. Prepare % all that's differente here to avoid code-repetition % later. case 'primary' data_orig = data1; date = date1; spec = spec1; line_i = self.parent.cols.LINE1; pos_i = self.parent.cols.POS1; fc_other = self.fieldcopier_other_primary; arg_i_other = 2; case 'secondary' data_orig = data2; date = date2; spec = spec2; line_i = self.parent.cols.LINE2; pos_i = self.parent.cols.POS2; fc_other = self.fieldcopier_other_secondary; arg_i_other = 5; otherwise error('BUG! CRASH! This place ought to be unreachable!'); end for i = 1:length(other_instruments{k}) other_instrument = other_instruments{k}{i}; %fields_from_other_instrument = intersect(fields, fieldnames(fc_other.(other_instrument).members)); fields_from_other_instrument = cellfun(@(X) safegetfield(fc_other.(other_instrument).members.(X), 'realname', X), intersect(fields, fieldnames(fc_other.(other_instrument).members)), 'UniformOutput', false); if isempty(fields_from_other_instrument) logtext(atmlab('OUT'), 'No need for %s this time\n', other_instrument); continue end logtext(atmlab('OUT'), 'Also taking same-satellite instrument %s\n', ... other_instrument); try % do not remove duplicates data_other = D.(other_instrument).read_granule(date, spec, fields_from_other_instrument, false); catch ME switch ME.identifier case {'atmlab:invalid_data'} logtext(atmlab('ERR'), 'Unable to read %s data, skipping and setting filler values\n', ... other_instrument); % return something of the right width flds = fieldnames(fc_other.(other_instrument).members); nrows = size(processed_core, 1); for fi = 1:length(flds) fld = flds{fi}; if ~isfield(fc_other.(other_instrument).members.(fld).atts, 'missing_value') error(['atmlab:' mfilename ':missingmissing'], ... ['Could not find %s.members.%s.atts.missing_value. ' ... 'Please define, I need this as a filler (see above).' ... 'Missing for the following fields: %s'], ... fc_other.(other_instrument).name, fld, ... sprintf('%s ', flds{cellfun(@(x) ~isfield(fc_other.(other_instrument).members.(x).atts, 'missing_value'), flds)})); end out_other{k}{i}(1:nrows, fc_other.(other_instrument).cols.(fld)) = ... fc_other.(other_instrument).members.(fld).atts.missing_value; end % hasdim = structfun(@(x)(isfield(x, 'dims')), fc_other.(other_instrument).members); % sz_singles = sum(~hasdim); % sz_multid = sum(cellfun(@(x)(fc_other.(other_instrument).members.(x).dims{2}), flds(hasdim))); % ncols = sz_singles + sz_multid; %nrows = size(processed_core, 1); % out_other{k}{i} = nan*zeros(nrows, ncols); %#ok continue; otherwise ME.rethrow(); end end pc_other = processed_core; [other_line, other_pos] = self.translate(processed_core, data_orig, other_instrument, data_other); pc_other(:, line_i) = other_line; pc_other(:, pos_i) = other_pos; % FieldCopier's process_granule also sets 'cols'-structure args = {pc_other, data1, date1, spec1, data2, spec2, dependencies, depcols, fields_from_other_instrument}; args{arg_i_other} = data_other; [out_other{k}{i}, cols_other{k}{i}] = fc_other.(other_instrument).process_granule(args{:}); %#ok % allmembers = catstruct(allmembers, fc_other.(other_instrument).members); end end % merge matrices and cols-structures appropiately out = out_core; newcols = core_fc_cols; for k = 1:length(modes) %m = modes{k}; for i = 1:length(out_other{k}) %[out, newcols] = self.merge_matrix(out, newcols, out_other{k}{i}, self.(['fieldcopier_other_' m]).(other_instruments{k}{i}).cols); [out, newcols] = self.merge_matrix(out, newcols, out_other{k}{i}, cols_other{k}{i}); end end % self.cols = newcols; % self.members = allmembers; localcols = newcols; end end end