function topoplotER(cfg, varargin) % TOPOPLOTER plots the topographic distribution of 2-Dimensional datatypes as % event-related fields (ERF), potentials (ERP), the powerspectrum or coherence spectum % that was computed using the TIMELOCKALYSIS, TIMELOCKGRANDAVERAGE, FREQANALYSIS or % FREQDESCRIPTIVES functions, as a 2-D circular view (looking down at the top of the head). % % Use as: % topoplotER(cfg, data) % % cfg.xparam = first dimension in data in which a selection is made % 'time' or 'freq' (default depends on data.dimord) % cfg.zparam = field that contains the data to be plotted as color % 'avg', 'powspctrm' or 'cohspctrm' (default depends on data.dimord) % cfg.xlim = 'maxmin' or [xmin xmax] (default = 'maxmin') % cfg.zlim = 'maxmin', 'absmax' or [zmin zmax] (default = 'maxmin') % cfg.cohrefchannel = Name of reference-channel, only for visualizing coherence % cfg.baseline = 'yes','no' or [time1 time2] (default = 'no'), see TIMELOCKBASELINE or FREQBASELINE % cfg.baselinetype = 'absolute' or 'relative' (default = 'absolute') % cfg.comment = string 'no' 'auto' or 'xlim' (default = 'auto') % 'auto': date, xparam and zparam limits are printed % 'xlim': only xparam limits are printed % cfg.commentpos = string or two numbers, position of comment (default 'leftbottom') % 'lefttop' 'leftbottom' 'middletop' 'middlebottom' 'righttop' 'rightbottom' % 'title' to place comment as title % 'layout' to place comment as specified for COMNT in layout % [x y] coordinates % cfg.interactive = Interactive plot 'yes' or 'no' (default = 'no') % In a interactive plot you can select areas and produce a new % interactive plot when a selected area is clicked. Multiple areas % can be selected by holding down the SHIFT key. % cfg.layout = specification of the layout, see below % % The layout defines how the channels are arranged. You can specify the % layout in a variety of ways: % - you can give the name of an ascii layout file with extension *.lay % - you can give the name of an electrode file % - you can give an electrode definition, i.e. "elec" structure % - you can give a gradiometer definition, i.e. "grad" structure % If you do not specify any of these and the data structure contains an % electrode or gradiometer structure, that will be used for creating a % layout. If you want to have more fine-grained control over the layout % of the subplots, you should create your own layout file. % % TOPOPLOTER calls the function TOPOPLOT to do the actual plotting. See % the help of that function for more configuration options. % % See also: % topoplot, topoplotTFR, singleplotER, multiplotER % Undocumented local options: % cfg.cohtargetchannel % cfg.layoutname % The following additional cfg parameters are used when plotting 3-dimensional % data (i.e. when topoplotTFR calls topoplotER): % cfg.yparam field to be plotted on y-axis % cfg.ylim 'maxmin' or [ymin ymax] (default = 'maxmin') % This function depends on TIMELOCKBASELINE which has the following options: % cfg.baseline, documented % cfg.channel % cfg.blcwindow % cfg.previous % cfg.version % % This function depends on FREQBASELINE which has the following options: % cfg.baseline, documented % cfg.baselinetype % Copyright (C) 2005-2006, F.C. Donders Centre % % $Log: topoplotER.m,v $ % Revision 1.38 2007/01/09 10:41:43 roboos % Added the option cfg.renderer, default is opengl. Interactive plotting % on linux/VNC sometimes does not work, using cfg.renderer='painters' % seems to fix it. % % Revision 1.37 2006/07/20 18:50:18 ingnie % minor change to axis scaling % % Revision 1.36 2006/07/17 12:39:08 ingnie % added absmax option for cfg.zlim, updated documentation % % Revision 1.35 2006/06/19 11:11:37 roboos % fixed small bug in the conversion of coherence data: first select labels for the channels, then for the channelcombinations % % Revision 1.34 2006/05/30 14:16:42 ingnie % updated documentation % % Revision 1.33 2006/05/26 12:47:28 ingnie % added error when labels in layout and labels in data do not match and therefore % no data is selected to be plotted % % Revision 1.32 2006/05/23 16:05:21 ingnie % updated documentation % % Revision 1.31 2006/05/09 12:21:23 ingnie % use OldStyle way to call topoplot.m, added some comment options, added comment % position, updated help % % Revision 1.30 2006/04/27 11:42:39 jansch % removed taking the absolute in the case of coherence-spectra % % Revision 1.29 2006/04/27 09:37:04 ingnie % changed comment options, fixed bug when style=blank, added dimord % subj_chan_time, updated documentation % % Revision 1.28 2006/04/20 09:58:34 roboos % updated documentation % % Revision 1.27 2006/03/22 19:09:19 jansch % removed overwriting of cfg.layout with chanX and chanY, caused unexplicable % crashed if toggling more than once between singleplotTFR and topoplotER in % the interactive mode. % % Revision 1.26 2006/03/17 14:30:38 denpas % Removed local drawTopoplot subroutine. topoplotER.m now calls topoplot.m to % plot the topoplot. % % Revision 1.25 2006/03/14 14:55:09 roboos % fixed detection of datatype for timelockbaseline or ferqbaseline % changed from DOS into UNIX format % % Revision 1.24 2006/03/14 08:09:22 roboos % added copyrigth and cvs log statement % cla % Multiple data sets are not supported for topoplot: if length(varargin)>1 error('Multiple data sets are not supported for topoplotER/topoplotTFR.'); end data = varargin{1}; % For backward compatibility with old data structures: data = fixdimord(data); % Set other config defaults: if ~isfield(cfg, 'xlim'), cfg.xlim = 'maxmin'; end if ~isfield(cfg, 'ylim'), cfg.ylim = 'maxmin'; end if ~isfield(cfg, 'zlim'), cfg.zlim = 'maxmin'; end if ~isfield(cfg, 'style'), cfg.style = 'both'; end if ~isfield(cfg, 'gridscale'), cfg.gridscale = 67; end if ~isfield(cfg, 'interplimits'), cfg.interplimits = 'head'; end if ~isfield(cfg, 'interpolation'), cfg.interpolation = 'v4'; end if ~isfield(cfg, 'contournum'), cfg.contournum = 6; end if ~isfield(cfg, 'shading'), cfg.shading = 'flat'; end if ~isfield(cfg, 'comment'), cfg.comment = 'auto'; end if ~isfield(cfg, 'commentpos'), cfg.commentpos = 'leftbottom'; end if ~isfield(cfg, 'ecolor'), cfg.ecolor = [0 0 0]; end if ~isfield(cfg, 'emarker'), cfg.emarker = 'o'; end if ~isfield(cfg, 'emarkersize'), cfg.emarkersize = 2; end if ~isfield(cfg, 'fontsize'), cfg.fontsize = 8; end if ~isfield(cfg, 'hcolor'), cfg.hcolor = [0 0 0]; end if ~isfield(cfg, 'hlinewidth'), cfg.hlinewidth = 2; end if ~isfield(cfg, 'baseline'), cfg.baseline = 'no'; end %to avoid warning in timelock/freqbaseline if ~isfield(cfg, 'interactive'), cfg.interactive = 'no'; end if ~isfield(cfg, 'renderer'), cfg.renderer = 'opengl'; end % Set x/y/zparam defaults according to data.dimord value: if strcmp(data.dimord, 'chan_time') if ~isfield(cfg, 'xparam'), cfg.xparam='time'; end if ~isfield(cfg, 'yparam'), cfg.yparam=''; end if ~isfield(cfg, 'zparam'), cfg.zparam='avg'; end elseif strcmp(data.dimord, 'subj_chan_time') && isfield(data,'avg') %for GA structure with keep individual data if ~isfield(cfg, 'xparam'), cfg.xparam='time'; end if ~isfield(cfg, 'yparam'), cfg.yparam=''; end if ~isfield(cfg, 'zparam'), cfg.zparam='avg'; end elseif strcmp(data.dimord, 'chan_freq') if ~isfield(cfg, 'xparam'), cfg.xparam='freq'; end if ~isfield(cfg, 'yparam'), cfg.yparam=''; end if ~isfield(cfg, 'zparam'), cfg.zparam='powspctrm'; end elseif strcmp(data.dimord, 'chan_freq_time') if ~isfield(cfg, 'xparam'), cfg.xparam='time'; end if ~isfield(cfg, 'yparam'), cfg.yparam='freq'; end if ~isfield(cfg, 'zparam'), cfg.zparam='powspctrm'; end elseif strcmp(data.dimord, 'chan_comp') % Add a pseudo-axis with the component numbers: data.comp = 1:size(data.topo,2); % Rename the field with topographic label information: data.label = data.topolabel; if ~isfield(cfg, 'xparam'), cfg.xparam='comp'; end if ~isfield(cfg, 'yparam'), cfg.yparam=''; end if ~isfield(cfg, 'zparam'), cfg.zparam='topo'; end end % Old style coherence plotting with cohtargetchannel is no longer supported: if isfield(cfg,'cohtargetchannel'), error('cfg.cohtargetchannel is obsolete, check the documentation for help about coherence plotting.'); end % Create time-series of small topoplots: if ~ischar(cfg.xlim) & length(cfg.xlim)>2 % Switch off interactive mode: cfg.interactive = 'no'; xlims = cfg.xlim; % Iteratively call topoplotER with different xlim values: for i=1:length(xlims)-1 subplot(ceil(sqrt(length(xlims)-1)), ceil(sqrt(length(xlims)-1)), i); cfg.xlim = xlims(i:i+1); topoplotER(cfg, data); end return end % Check for unconverted coherence spectrum data: if (strcmp(cfg.zparam,'cohspctrm')) && isfield(data, 'labelcmb') % A reference channel is required: if ~isfield(cfg,'cohrefchannel'), error('no reference channel specified'); end % Convert 2-dimensional channel matrix to a single dimension: sel1 = strmatch(cfg.cohrefchannel, data.labelcmb(:,2)); sel2 = strmatch(cfg.cohrefchannel, data.labelcmb(:,1)); fprintf('selected %d channels for coherence\n', length(sel1)+length(sel2)); data.cohspctrm = data.cohspctrm([sel1;sel2],:,:); data.label = [data.labelcmb(sel1,1);data.labelcmb(sel2,2)]; data.labelcmb = data.labelcmb([sel1;sel2],:); data = rmfield(data, 'labelcmb'); end % Apply baseline correction: if ~strcmp(cfg.baseline, 'no') if strcmp(cfg.xparam, 'freq') || strcmp(cfg.yparam, 'freq') data = freqbaseline(cfg, data); else data = timelockbaseline(cfg, data); end end % Get physical min/max range of x: if strcmp(cfg.xlim,'maxmin') xmin = min(getsubfield(data, cfg.xparam)); xmax = max(getsubfield(data, cfg.xparam)); else xmin = cfg.xlim(1); xmax = cfg.xlim(2); end % Replace value with the index of the nearest bin xmin = nearest(getsubfield(data, cfg.xparam), xmin); xmax = nearest(getsubfield(data, cfg.xparam), xmax); % Get physical min/max range of y: if ~isempty(cfg.yparam) if strcmp(cfg.ylim,'maxmin') ymin = min(getsubfield(data, cfg.yparam)); ymax = max(getsubfield(data, cfg.yparam)); else ymin = cfg.ylim(1); ymax = cfg.ylim(2); end % Replace value with the index of the nearest bin: ymin = nearest(getsubfield(data, cfg.yparam), ymin); ymax = nearest(getsubfield(data, cfg.yparam), ymax); end % make dat structure with one value for each channel dat = getsubfield(data, cfg.zparam); if ~isempty(cfg.yparam), xdim=strmatch(cfg.xparam, tokenize(data.dimord, '_')); ydim=strmatch(cfg.yparam, tokenize(data.dimord, '_')); S=[]; S.type='()'; S.subs={}; for d=1:ndims(dat) switch d case xdim S.subs=[S.subs {xmin:xmax}]; case ydim S.subs=[S.subs {ymin:ymax}]; otherwise S.subs=[S.subs {':'}]; end end dat = subsref(dat, S); dat = nanmean(nanmean(dat, ydim), xdim); else dat = dat(:, xmin:xmax); dat = nanmean(dat, 2); end dat = dat(:); % Read or create the layout that will be used for plotting if ~isfield(cfg, 'layout') if isfield(cfg, 'layoutname') cfg.layout = cfg.layoutname; % backward compatible cfg fieldname elseif isfield(data,'grad') cfg.layout = data.grad; % create layout from gradiometer definition elseif isfield(data, 'elec') cfg.layout = data.elec; % create layout from electrode definition else cfg.layout = 'CTF151s.lay'; % revert to the FCDC default end end lay = createlayout(cfg.layout); % Select the channels in the data that match with the layout: [seldat, sellay] = match_str(data.label, lay.label); if isempty(seldat) error('labels in data and labels in layout do not match'); end datavector = dat(seldat); % Select x and y coordinates and labels of the channels in the data chanX = lay.prj(sellay,1); chanY = lay.prj(sellay,2); chanLabels = lay.label(sellay); % Get physical min/max range of z: if strcmp(cfg.zlim,'maxmin') zmin = min(datavector); zmax = max(datavector); elseif strcmp(cfg.zlim,'absmax') zmin = -max(max(abs(datavector))); zmax = max(max(abs(datavector))); else zmin = cfg.zlim(1); zmax = cfg.zlim(2); end % specify the x and y coordinates of the comment as stated in the layout if strcmp(cfg.commentpos,'layout') cfg.commentpos = []; ind_COMNT = strmatch('COMNT', lay.label); cfg.commentpos(1) = lay.prj(ind_COMNT,1); cfg.commentpos(2) = lay.prj(ind_COMNT,2); end % make cfg.comment for topoplot.m if strcmp(cfg.comment, 'no') cfg = rmfield(cfg,'comment'); elseif strcmp(cfg.comment, 'auto') comment = date; if ~isempty(cfg.xparam) if strcmp(cfg.xlim,'maxmin') comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.xparam, data.(cfg.xparam)(xmin), data.(cfg.xparam)(xmax)); else comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.xparam, cfg.xlim(1), cfg.xlim(2)); end end if ~isempty(cfg.yparam) if strcmp(cfg.ylim,'maxmin') comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.yparam, data.(cfg.yparam)(ymin), data.(cfg.yparam)(ymax)); else comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.yparam, cfg.ylim(1), cfg.ylim(2)); end end if ~isempty(cfg.zparam) comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.zparam, zmin, zmax); end cfg.comment = comment; elseif strcmp(cfg.comment, 'xlim') if strcmp(cfg.xlim,'maxmin') comment = sprintf('%0s=[%.3g %.3g]', cfg.xparam, data.(cfg.xparam)(xmin), data.(cfg.xparam)(xmax)); else comment = sprintf('%0s=[%.3g %.3g]', cfg.xparam, cfg.xlim(1), cfg.xlim(2)); end cfg.comment = comment; elseif ~isstr(cfg.comment) error('cfg.comment must be string'); end % Draw topoplot: tmpcfg = rmfield (cfg,'layout'); topoplot(tmpcfg,chanX,chanY,datavector,chanLabels); % The remainder of the code is meant to make the figure interactive hold on; % Scale the channel locations between -0.45 and +0.45, which fits with the head outline: chanX = 0.9*((chanX-min(chanX))/(max(chanX)-min(chanX))-0.5); %should be the same as in topoplot.m chanY = 0.9*((chanY-min(chanY))/(max(chanY)-min(chanY))-0.5); if strcmp(cfg.interactive, 'yes') userData.hFigure = gcf; userData.hAxes = gca; for i=1:10 userData.hSelection{i} = plot(0,0); set(userData.hSelection{i}, ... 'XData', [0], ... 'YData', [0], ... 'Color', [0 0 0], ... 'EraseMode', 'xor', ... 'LineStyle', '--', ... 'LineWidth', 1.5, ... 'Visible', 'on'); userData.range{i} = []; end userData.iSelection = 0; userData.plotType = 'topoplot'; userData.selecting = 0; userData.selectionType = ''; userData.selectAxes = 'z'; userData.lastClick = []; userData.cfg = cfg; userData.data = data; userData.chanX = chanX; userData.chanY = chanY; userData.chanLabels = chanLabels; tag = sprintf('%.5f', 10000 * rand(1)); set(gcf, 'Renderer', cfg.renderer); set(gcf, 'Tag', tag); set(gcf, 'UserData', userData); set(gcf, 'WindowButtonMotionFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 0);']); set(gcf, 'WindowButtonDownFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 1);']); set(gcf, 'WindowButtonUpFcn', ['plotSelection(get(findobj(''Tag'', ''' tag '''), ''UserData''), 2);']); end axis off; hold off;