% CONVERTLOCS - Convert electrode locations between coordinate systems
% using the EEG.chanlocs structure.
%
% Usage: >> newchans = convertlocs( chanlocs, 'command');
%
% Input:
% chanlocs - An EEGLAB EEG dataset OR a EEG.chanlocs channel locations structure
% 'command' - ['cart2topo'|'sph2topo'|'sphbesa2topo'| 'sph2cart'|'topo2cart'|'sphbesa2cart'|
% 'cart2sph'|'sphbesa2sph'|'topo2sph'| 'cart2sphbesa'|'sph2sphbesa'|'topo2sphbesa'|
% 'cart2all'|'sph2all'|'sphbesa2all'|'topo2all']
% These command modes convert between four coordinate frames: 3-D Cartesian
% (cart), Matlab spherical (sph), Besa spherical (sphbesa), and 2-D polar (topo)
% 'auto' -- Here, the function finds the most complex coordinate frame
% and constrains all the others to this one. It searches first for Cartesian
% coordinates, then for spherical and finally for polar. Default is 'auto'.
%
% Optional input
% 'verbose' - ['on'|'off'] default is 'off'.
%
% Outputs:
% newchans - new EEGLAB channel locations structure
%
% Ex: CHANSTRUCT = convertlocs( CHANSTRUCT, 'cart2topo');
% % Convert Cartesian coordinates to 2-D polar (topographic).
%
% Author: Arnaud Delorme, CNL / Salk Institute, 22 Dec 2002
%
% See also: READLOCS
% Copyright (C) Arnaud Delorme, CNL / Salk Institute, 22 Dec 2002, arno@salk.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.
function chans = convertlocs(chans, command, varargin)
if nargin < 1
help convertlocs;
return;
end
if ~isfield(chans, 'theta') && ~isfield(chans, 'X') && ~isfield(chans, 'radius') && ~isfield(chans, 'sph_theta_besa')
return
end
if nargin < 2
command = 'auto';
end
if nargin == 4 && strcmpi(varargin{2}, 'on')
verbose = 1;
else
verbose = 0; % off
end
% test if value exists for default
% --------------------------------
if strcmp(command, 'auto')
if isfield(chans, 'X') && any(~cellfun(@isempty, { chans.X }))
command = 'cart2all';
if verbose
disp('Make all coordinate frames uniform using Cartesian coords');
end
else
if isfield(chans, 'sph_theta') && ~isempty(chans(1).sph_theta)
command = 'sph2all';
if verbose
disp('Make all coordinate frames uniform using spherical coords');
end
else
if isfield(chans, 'sph_theta_besa') && ~isempty(chans(1).sph_theta_besa)
command = 'sphbesa2all';
if verbose
disp('Make all coordinate frames uniform using BESA spherical coords');
end
else
command = 'topo2all';
if verbose
disp('Make all coordinate frames uniform using polar coords');
end
end
end
end
end
% convert
% -------
switch command
case 'topo2sph'
theta = {chans.theta};
radius = {chans.radius};
indices = find(~cellfun('isempty', theta));
[sph_phi, sph_theta] = topo2sph( [ [ theta{indices} ]' [ radius{indices}]' ] );
if verbose
disp('Warning: electrodes forced to lie on a sphere for polar to 3-D conversion');
end
for index = 1:length(indices)
chans(indices(index)).sph_theta = sph_theta(index);
chans(indices(index)).sph_phi = sph_phi (index);
end
if isfield(chans, 'sph_radius')
meanrad = mean([ chans(indices).sph_radius ]);
if isempty(meanrad)
[chans(indices).sph_radius] = deal(85);
meanrad = 85;
end
else
[chans(indices).sph_radius] = deal(85);
meanrad = 85;
end
sph_radius(1:length(indices)) = {meanrad};
case 'topo2sphbesa'
chans = convertlocs(chans, 'topo2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2sphbesa', varargin{:}); % search for spherical coords
case 'topo2cart'
chans = convertlocs(chans, 'topo2sph', varargin{:}); % search for spherical coords
if verbose
disp('Warning: spherical coordinates automatically updated');
end
chans = convertlocs(chans, 'sph2cart', varargin{:}); % search for spherical coords
case 'topo2all'
chans = convertlocs(chans, 'topo2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2sphbesa', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2cart', varargin{:}); % search for spherical coords
case 'sph2cart'
sph_theta = {chans.sph_theta};
sph_phi = {chans.sph_phi};
indices = find(~cellfun('isempty', sph_theta));
if ~isfield(chans, 'sph_radius')
[chans(indices).sph_radius] = deal(85);
sph_radius(1:length(indices)) = {85};
else
sph_radius = {chans.sph_radius};
end
inde = find(cellfun('isempty', sph_radius));
if ~isempty(inde)
meanrad = mean( [ sph_radius{:} ]);
sph_radius(inde) = { meanrad };
end
[x, y, z] = sph2cart([ sph_theta{indices} ]'/180*pi, [ sph_phi{indices} ]'/180*pi, [ sph_radius{indices} ]');
for index = 1:length(indices)
chans(indices(index)).X = x(index);
chans(indices(index)).Y = y(index);
chans(indices(index)).Z = z(index);
end
case 'sph2topo'
if verbose
% disp('Warning: all radii constrained to one for spherical to topo transformation');
end
sph_theta = {chans.sph_theta};
sph_phi = {chans.sph_phi};
indices = find(~cellfun('isempty', sph_theta));
[chan_num,angle,radius] = sph2topo([ ones(length(indices),1) [ sph_phi{indices} ]' [ sph_theta{indices} ]' ], 1, 2); % using method 2
for index = 1:length(indices)
chans(indices(index)).theta = angle(index);
chans(indices(index)).radius = radius(index);
if ~isfield(chans, 'sph_radius') || isempty(chans(indices(index)).sph_radius)
chans(indices(index)).sph_radius = 85;
end
end
case 'sph2sphbesa'
% using polar coordinates
sph_theta = {chans.sph_theta};
sph_phi = {chans.sph_phi};
indices = find(~cellfun('isempty', sph_theta));
[chan_num,angle,radius] = sph2topo([ones(length(indices),1) [ sph_phi{indices} ]' [ sph_theta{indices} ]' ], 1, 2);
[sph_theta_besa, sph_phi_besa] = topo2sph([angle radius], 1, 1);
for index = 1:length(indices)
chans(indices(index)).sph_theta_besa = sph_theta_besa(index);
chans(indices(index)).sph_phi_besa = sph_phi_besa(index);
end
case 'sph2all'
chans = convertlocs(chans, 'sph2topo', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2sphbesa', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2cart', varargin{:}); % search for spherical coords
case 'sphbesa2sph'
% using polar coordinates
sph_theta_besa = {chans.sph_theta_besa};
sph_phi_besa = {chans.sph_phi_besa};
indices = find(~cellfun('isempty', sph_theta_besa));
[chan_num,angle,radius] = sph2topo([ones(length(indices),1) [ sph_theta_besa{indices} ]' [ sph_phi_besa{indices} ]' ], 1, 1);
%for index = 1:length(chans)
% chans(indices(index)).theta = angle(index);
% chans(indices(index)).radius = radius(index);
% chans(indices(index)).labels = int2str(index);
%end;
%figure; topoplot([],chans, 'style', 'blank', 'electrodes', 'labelpoint');
[sph_phi, sph_theta] = topo2sph([angle radius], 2);
for index = 1:length(indices)
chans(indices(index)).sph_theta = sph_theta(index);
chans(indices(index)).sph_phi = sph_phi (index);
end
case 'sphbesa2topo'
chans = convertlocs(chans, 'sphbesa2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2topo', varargin{:}); % search for spherical coords
case 'sphbesa2cart'
chans = convertlocs(chans, 'sphbesa2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2cart', varargin{:}); % search for spherical coords
case 'sphbesa2all'
chans = convertlocs(chans, 'sphbesa2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2all', varargin{:}); % search for spherical coords
case 'cart2topo'
chans = convertlocs(chans, 'cart2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2topo', varargin{:}); % search for spherical coords
case 'cart2sphbesa'
chans = convertlocs(chans, 'cart2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2sphbesa', varargin{:}); % search for spherical coords
case 'cart2sph'
if verbose
disp('WARNING: If XYZ center has not been optimized, optimize it using Edit > Channel Locations');
end
X = {chans.X};
Y = {chans.Y};
Z = {chans.Z};
indices = find(~cellfun('isempty', X));
[th, phi, radius] = cart2sph( [ X{indices} ], [ Y{indices} ], [ Z{indices} ]);
for index = 1:length(indices)
chans(indices(index)).sph_theta = th(index)/pi*180;
chans(indices(index)).sph_phi = phi(index)/pi*180;
chans(indices(index)).sph_radius = radius(index);
end
case 'cart2all'
chans = convertlocs(chans, 'cart2sph', varargin{:}); % search for spherical coords
chans = convertlocs(chans, 'sph2all', varargin{:}); % search for spherical coords
end