R/topoplot.R

Defines functions topoplot

Documented in topoplot

###
topoplot<-function(erpobj, startmsec=-200, endmsec=1200, win.ini, win.end, exclude = NULL,
elec.coord=NULL, projection="orthographic", palette.col="jet", palette.steps=10, return.coord = FALSE,
zlim=NULL, interpolation = "cubicspline", extrap = TRUE, interp.points = 500, return.notfound=FALSE, mask = TRUE,  contour=TRUE, x.rev=FALSE,
draw.elec.pos=TRUE,  elec.pos.toplot="all", elec.pos.pch=19, elec.pos.cex=1, draw.nose=FALSE, draw.ears=FALSE, draw.elec.lab=TRUE, elec.lab.adj=c(0.5, NA), elec.lab.cex=1, elec.lab.toplot=elec.pos.toplot, head.col="black", head.lwd=1, ...)

{
	requireNamespace("akima")
	#  initial checks
	#projection checks
	if (!projection%in%c("orthographic", "equalarea")){
		stop("Available projectios are: orthographic, equalarea
		The projection specified is: ", projection, call.=F)
	}
	
	# palette col checks
	available.palettes=c("jet", "heat")
	if ((!palette.col[1]%in%available.palettes)&length(palette.col)==1){
		
		available.palettes=paste("\"",available.palettes,"\"", collapse=", ", sep="")
		stop("To create a palette for the plot, at least two colors must be specified.
		Type colors() to see a list of available colors.
		Two default palettes are available:", available.palettes, ".", call.=F)
		
	}
	
	# check zlims (SEE AFTER) to avoid code duplication I moved this section after iterpolation.
	# In the case of cubic interpolation, interpolated data may be higher or lower than original data.	
	# for this reason I check the limits afterwards.
	
	
	
	
	##################
	# RETRIEVE ELECTRODE POSITIONS
	########################
	if (is.null(elec.coord)){
		elec.coord=structure(list(el.name = structure(c(81L, 117L, 82L, 68L, 70L, 
69L, 10L, 11L, 3L, 1L, 2L, 4L, 7L, 5L, 6L, 8L, 45L, 43L, 41L, 
39L, 77L, 40L, 42L, 44L, 46L, 66L, 64L, 62L, 60L, 61L, 63L, 65L, 
67L, 74L, 72L, 51L, 49L, 47L, 59L, 48L, 50L, 52L, 73L, 71L, 75L, 
57L, 55L, 53L, 54L, 56L, 58L, 76L, 121L, 119L, 17L, 15L, 13L, 
14L, 16L, 18L, 120L, 118L, 128L, 23L, 21L, 19L, 20L, 22L, 24L, 
129L, 125L, 123L, 30L, 28L, 26L, 38L, 27L, 29L, 31L, 124L, 122L, 
126L, 36L, 34L, 32L, 33L, 35L, 37L, 127L, 99L, 97L, 95L, 93L, 
90L, 115L, 92L, 94L, 96L, 98L, 91L, 113L, 111L, 112L, 114L, 105L, 
103L, 101L, 110L, 102L, 104L, 100L, 106L, 108L, 109L, 85L, 89L, 
86L, 107L, 87L, 88L, 78L, 80L, 79L, 12L, 83L, 84L, 9L, 25L, 116L, 
130L, 131L, 132L, 133L, 134L), .Label = c("AF3", "AF4", "AF7", 
"AF8", "AFF1h", "AFF2h", "AFF5h", "AFF6h", "AFp10h", "AFp3", 
"AFp4", "AFp9h", "C1", "C2", "C3", "C4", "C5", "C6", "CCP1h", 
"CCP2h", "CCP3h", "CCP4h", "CCP5h", "CCP6h", "Centroid", "CP1", 
"CP2", "CP3", "CP4", "CP5", "CP6", "CPP1h", "CPP2h", "CPP3h", 
"CPP4h", "CPP5h", "CPP6h", "CPz", "F1", "F2", "F3", "F4", "F5", 
"F6", "F7", "F8", "FC1", "FC2", "FC3", "FC4", "FC5", "FC6", "FCC1h", 
"FCC2h", "FCC3h", "FCC4h", "FCC5h", "FCC6h", "FCz", "FFC1h", 
"FFC2h", "FFC3h", "FFC4h", "FFC5h", "FFC6h", "FFT7h", "FFT8h", 
"Fp1", "Fp2", "Fpz", "FT10", "FT7", "FT8", "FT9", "FTT7h", "FTT8h", 
"Fz", "I1", "I2", "Iz", "Left", "Nasion", "NFp1h", "NFp2h", "O1", 
"O2", "OI1h", "OI2h", "Oz", "P1", "P10", "P2", "P3", "P4", "P5", 
"P6", "P7", "P8", "P9", "PO10", "PO3", "PO4", "PO7", "PO8", "PO9", 
"POO1", "POO10h", "POO2", "POO9h", "POz", "PPO1h", "PPO2h", "PPO3h", 
"PPO4h", "Pz", "Ref", "Right", "T10", "T7", "T8", "T9", "TP10", 
"TP7", "TP8", "TP9", "TPP7h", "TPP8h", "TTP7h", "TTP8h", "Cz", 
"T3", "T4", "T6", "T5"), class = "factor"), d = c(108, 114, 110, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 
69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 99, 120, 69, 
69, 69, 69, 69), y = c(0, 0, 0.923, 0.9511, 1, 0.9511, 0.9565, 
0.9565, 0.809, 0.892, 0.8919, 0.809, 0.7777, 0.8289, 0.8289, 
0.7777, 0.5878, 0.6343, 0.6726, 0.6979, 0.7067, 0.6979, 0.6726, 
0.6343, 0.5878, 0.4741, 0.5107, 0.5384, 0.5533, 0.5533, 0.5384, 
0.5107, 0.4741, 0.2852, 0.309, 0.3373, 0.3612, 0.377, 0.3826, 
0.377, 0.3612, 0.3373, 0.309, 0.2852, 0.164, 0.1779, 0.1887, 
0.1944, 0.1944, 0.1887, 0.1779, 0.164, -1e-04, 0, 1e-04, 1e-04, 
2e-04, 1e-04, 1e-04, 1e-04, 0, 0, -0.1639, -0.1778, -0.1883, 
-0.194, -0.194, -0.1884, -0.1778, -0.1639, -0.2852, -0.309, -0.3372, 
-0.3609, -0.3767, -0.3822, -0.3767, -0.3608, -0.3372, -0.309, 
-0.2853, -0.474, -0.5106, -0.5384, -0.5532, -0.5532, -0.5384, 
-0.5106, -0.474, -0.5429, -0.5878, -0.6342, -0.6724, -0.6975, 
-0.7063, -0.6975, -0.6724, -0.6342, -0.5878, -0.5429, -0.8108, 
-0.8284, -0.8284, -0.8108, -0.7467, -0.809, -0.8918, -0.923, 
-0.8918, -0.809, -0.7467, -0.9739, -0.9739, -0.873, -0.9511, 
-1, -0.9511, -0.873, -0.9679, -0.9679, -0.8785, -0.923, -0.8785, 
0.8732, 0.9601, 0.9601, 0.8732, 0, 0, 0, 0, 0, -0.5878, -0.5878
), x = c(-0.9237, 0.9237, 0, -0.309, 0, 0.3091, -0.2508, 0.2508, 
-0.5878, -0.3554, 0.3553, 0.5878, -0.5417, -0.122, 0.122, 0.5417, 
-0.809, -0.721, -0.5399, -0.2888, 0, 0.2888, 0.5399, 0.721, 0.809, 
-0.8642, -0.7218, -0.4782, -0.1672, 0.1672, 0.4782, 0.7218, 0.8642, 
-0.8777, -0.9511, -0.8709, -0.6638, -0.3581, 0, 0.3581, 0.6638, 
0.8709, 0.9511, 0.8777, -0.9669, -0.8184, -0.5466, -0.1919, 0.1919, 
0.5466, 0.8184, 0.9669, -0.9237, -1, -0.9237, -0.7066, -0.3824, 
0.3824, 0.7066, 0.9237, 1, 0.9237, -0.9669, -0.8185, -0.5465, 
-0.1918, 0.1918, 0.5465, 0.8185, 0.9669, -0.8777, -0.9511, -0.8712, 
-0.6635, -0.358, 0, 0.358, 0.6635, 0.8712, 0.9511, 0.8777, -0.8639, 
-0.722, -0.4786, -0.1673, 0.1673, 0.4786, 0.722, 0.8638, -0.7472, 
-0.809, -0.7211, -0.5401, -0.2889, 0, 0.2889, 0.5401, 0.7211, 
0.809, 0.7472, -0.352, -0.122, 0.122, 0.3519, -0.5425, -0.5878, 
-0.3549, 0, 0.3549, 0.5878, 0.5425, -0.1285, 0.1286, -0.4448, 
-0.309, 0, 0.309, 0.4448, -0.1533, 0.1533, -0.2854, 0, 0.2854, 
-0.4449, -0.1564, 0.1564, 0.4449, 0, 0.9237, 0, -1, 1, 0.809, 
-0.809), z = c(-0.3826, -0.3826, -0.3824, 1e-04, 1e-04, 0, 0.1438, 
0.1437, 0, 0.2782, 0.2783, 0, 0.3163, 0.5452, 0.5452, 0.3163, 
0, 0.2764, 0.5043, 0.6542, 0.7067, 0.6542, 0.5043, 0.2764, 0, 
0.1647, 0.4651, 0.6925, 0.8148, 0.8148, 0.6925, 0.4651, 0.1647, 
-0.3826, 0, 0.3549, 0.6545, 0.8532, 0.9233, 0.8532, 0.6545, 0.3549, 
0, -0.3826, 0.1915, 0.5448, 0.8154, 0.9615, 0.9615, 0.8154, 0.5448, 
0.1915, -0.3826, 0, 0.3826, 0.7066, 0.9231, 0.9231, 0.7066, 0.3826, 
0, -0.3826, 0.1915, 0.5449, 0.8153, 0.9611, 0.9611, 0.8153, 0.5449, 
0.1915, -0.3826, -1e-04, 0.3552, 0.6543, 0.8534, 0.9231, 0.8534, 
0.6543, 0.3552, -1e-04, -0.3826, 0.1646, 0.4653, 0.6933, 0.8155, 
0.8155, 0.6933, 0.4653, 0.1646, -0.3826, -1e-04, 0.2764, 0.5045, 
0.6545, 0.7065, 0.6545, 0.5045, 0.2764, -1e-04, -0.3826, 0.4659, 
0.5453, 0.5453, 0.4659, -0.3825, 0, 0.2776, 0.3824, 0.2776, 0, 
-0.3825, 0.1822, 0.1822, -0.195, 0, 0, 0, -0.195, -0.195, -0.195, 
-0.3824, -0.3823, -0.3824, -0.1949, -0.1949, -0.1949, -0.1949, 
0, -0.5773, 1, 0, 0, -1e-04, -1e-04)), .Names = c("el.name", 
"d", "y", "x", "z"), row.names = c(NA, 134L), class = "data.frame")
			}
	
	# additional check if external electrode coordinate is supplied
	necessary.el.coord.cols=c("el.name", "y", "x")
	if (!all(necessary.el.coord.cols%in%names(elec.coord))) {
		colsnotfound=necessary.el.coord.cols[!necessary.el.coord.cols%in%names(elec.coord)]
		colsnotfound=paste("\"",colsnotfound,"\"", collapse=", ", sep="")
		stop("The electrode coordinate object should contain at least these columns: 
		1) \"el.name\": a column containing electrode names.
		2) \"y\": a column containing the y of electrode coordinates.
		3) \"x\": a column containing the x of electrode coordinates.
		The following column(s) have not been found in the object supplied: ", colsnotfound, call.=FALSE)
	}
	
	# show coordinates of electrodes already contained in the function and the function breaks
		if (return.coord == TRUE){
		return(elec.coord)
	} else {
	
	
	### EXCLUDE ELECTRODES
	
  if (!is.null(exclude)){
    erpobj=erpobj[,!names(erpobj)%in%exclude]
  }
	  
	# create a vector with the electrode to be included in the future steps
	#curr.el=names(erpobj[,!names(erpobj)%in%exclude])
	  
	curr.el=names(erpobj) 
	
	up.curr.el=toupper(curr.el)
	up.elec.coord.names=toupper(elec.coord$el.name)	
	
	found=names(erpobj)[up.curr.el%in%up.elec.coord.names]
	notfound=names(erpobj)[!up.curr.el%in%up.elec.coord.names]
	# notice that I use the indices with upper case, but I retrive the original names
	
	if (length(notfound)>0&return.notfound==FALSE){
		notfound.list=paste(notfound, "\n", sep="")
		warning("the following electrodes have not been found\n", notfound.list, call.=F)
		
		# update the curr.el object by automatically excluding not.found electrode
		curr.el=names(erpobj[,!names(erpobj)%in%notfound])
		erpobj=erpobj[,!names(erpobj)%in%notfound]
		}
	
	if (length(notfound)>0&return.notfound==TRUE&is.null(exclude)){
		return(notfound)
	}
	
	}
	
	# CRUCIAL!! I reorder both the names of elec.coord and the names of erpobj for matching
	# i have to do so, because I'm not taking into account the letter case.
	curr.elec.coord=elec.coord[up.elec.coord.names%in%up.curr.el,]
	curr.elec.coord=curr.elec.coord[order(as.character(curr.elec.coord$el.name)),]
	
	erpobj=erpobj[,order(names(erpobj))]
	

	# CHANGE X DIRECTIONS IF REQUIRED
	if (x.rev==TRUE){
		curr.elec.coord$x=-curr.elec.coord$x
	}
		
	### IMPORTANT restore the correct order of x and y.
	
		
	##################
	# PROJECT ELECTRODE COORDINATES FROM 3D TO 2D
	####################################
	
	if(projection=="orthographic"){
		x=curr.elec.coord$x
		y=curr.elec.coord$y
	}
	
	if (projection=="equalarea"){
	x=curr.elec.coord$x *sqrt(1/(1 + curr.elec.coord$z))
	y=curr.elec.coord$y *sqrt(1/(1 + curr.elec.coord$z))

	}
		
	
	#build palette
	if (palette.col[1]=="jet"){
		 mypalette <- colorRampPalette(c("#00007F", "blue", "#007FFF", "cyan", "#7FFF7F", "yellow", "#FF7F00", "red", "#7F0000"))
	
	} else {
		if (palette.col[1]=="heat"){
			 mypalette <- colorRampPalette(heat.colors(palette.steps))
	
		} else {
			mypalette <-colorRampPalette(palette.col)
		}
	}
	
	
	
	##################
	# RETRIEVE AMPLITUDE. If necessary compute average across timepoints. 
	####################################

	
	
	if (win.ini == win.end){
		ampl = as.numeric(erpobj[round(msectopoints(win.ini, dim(erpobj)[1], startmsec, endmsec)), ])
	} else {
		ampl = colMeans(erpobj[round(msectopoints(win.ini, dim(erpobj)[1], startmsec, endmsec)):
		round(msectopoints(win.end, dim(erpobj)[1], startmsec, endmsec)), ])
	}
	
	###
	# SET GRAPHIC MARGINS. They are not currently manipolable by user
	#####
	# I do this in this step because I interpolate also according to margins
	xlim=c(-1.3, 1.3)
	ylim=c(-1.3, 1.3)
	


	
	###############
	# INTERPOLATE DATA
	########################
	if (interpolation=="cubicspline"){
		interp.linear=F
	}
	if (interpolation=="linear"){
		interp.linear=T
		extrap=FALSE #notice that extrapolation is not possible with linear interp
	}
	
	interp.data=interp(x,y, ampl, xo=seq(xlim[1], xlim[2], length = interp.points), yo=seq(ylim[1], ylim[2], length = interp.points), linear=interp.linear, extrap= extrap)


		
	
	
	################
	# TOPOPLOT
	################
	
		
	
	# reduce margin to avoid in plotting that interpolation goes too much near the margins.
	interp.xlim.up=which(interp.data$x>(xlim[2]-0.1)) #o
	interp.xlim.inf=which(interp.data$x<(xlim[1]+0.1)) #

	interp.ylim.up=which(interp.data$y>(ylim[2]-0.1)) #
	interp.ylim.inf=which(interp.data$y<(ylim[1]+0.1))#
	
	# data trimming with the new limits imposed
	interp.data$z[c(interp.xlim.up, interp.xlim.inf),]=NA
	interp.data$z[,c(interp.ylim.up, interp.ylim.inf)]=NA
	
	
	#### CHECK PROBLEMS WITH ZLIM
	
	range.used=round(range(interp.data$z, na.rm=T),2)
	
	### create zlims if not specified
	# this lims are created to be symmetric (centered on 0) .
	if (is.null(zlim)){
		newlim=round(max(abs(range.used))) # of the absolute value of observed data I take the max. Then, I round with 0 digits.
		zlim=c(-newlim, newlim)
	}
	
	
	if ((min(zlim)>min(range.used))|(max(zlim)<max(zlim))){
		cat("WARNING: your data (after interpolation) are out of range as compared to the zlims specified.\n",
	"Your data range is:", paste(range.used, collapse= ", "), "\n",
		"Your zlims are:", paste(zlim, collapse=", "), "\n")
	} # 

	
	
		
	
	# plot
	# notice that xlim and ylim are reversed. But this doesn't matter.
	
	image(interp.data, col=mypalette(palette.steps), xlim=c(xlim[1], xlim[2]), ylim=c(ylim[1], ylim[2]), zlim=c(zlim[1], zlim[2]), frame.plot=FALSE, axes=FALSE, ...)

if (contour == TRUE){ 	
	
	cont_levels=seq(zlim[1], zlim[2], dist(zlim)/palette.steps)
	contour(interp.data, ylim=c(xlim[1], xlim[2]), xlim=c(ylim[1], ylim[2]), zlim=c(zlim[1], zlim[2]), frame.plot=FALSE, axes=FALSE, add=TRUE, drawlabels=FALSE, levels=cont_levels)
	
	}
	
	
		
	
	#####
	# ADD MASK
	#################
	
if (mask == TRUE){ 	
	#add mask (optional)
	
	plotcircle <- function (x, y, r, ...) 
	{
		#x^2+y^2 = r^2
	
		angle = seq(0, 2*pi, length = 200)
		xc = x + (r * cos(angle))
		yc = y + (r * sin(angle))
		polygon(xc, yc, ...)
		return(list(x = xc, y = yc))	
	}

	circ.coord=plotcircle(0,0,1, border = head.col, lwd = head.lwd)
	
	circle.points=length(circ.coord$x)

	#nota il grafico è costruito da dx verso sx, perchè i punti in circ.coord sono ordinati da 1 a -1 come x.
	pol.x=c(xlim[2], circ.coord$x[1:120], xlim[1], xlim[1], xlim[2], xlim[2])
	pol.y=c(0.4, -circ.coord$y[1:120], 0.4, ylim[1], ylim[1], 0.4)
	# nota che metto -0.2, ma sarebbe 0. non metto esattamente 0, perché altrimenti le due metà della maschera si sfiorano e rimane una piccola riga colorata.

	polygon(pol.x, pol.y, col="white", lty="blank")
	polygon(pol.x, -pol.y, col="white", lty="blank")
	}
	
	
		### ADD ELECTRODES AND TEXT
	#add electrodes locations and labels (optional)
	# TO BE ADJUSTED!!! devi permettere un controllo più fine di dimensioni elettrodi, caratteri, etc.
	
	if(elec.pos.toplot[1]=="all"){} else {
		x=x[which(curr.elec.coord$el.name%in%elec.pos.toplot)]
		y=y[which(curr.elec.coord$el.name%in%elec.pos.toplot)]
		}
	
	
	if (draw.elec.pos==TRUE){
		points(x,y, pch=elec.pos.pch, col="black", cex=elec.pos.cex)
	}
	
	if (draw.elec.lab==TRUE){
		if (elec.lab.toplot[1]=="all"){
			labels.toplot=curr.elec.coord$el.name
		} else {
				labels.toplot=curr.elec.coord$el.name[curr.elec.coord$el.name%in%elec.lab.toplot] 
				}
	text(x,y+0.1, labels=labels.toplot, adj=elec.lab.adj, cex=elec.lab.cex)
	}

	
	#### DRAW NOSE #####
	if (draw.nose==TRUE){
	
	Nose.left=structure(list(x = c(-0.165709488227992, -0.170777055719452, 
	-0.160641920736532, -0.120101380804849, -0.0542230034158648, 
	0), y = c(0.991010207442792, 1.00031714102429, 1.02885836681128, 
	1.06310783775568, 1.13731502480188, 1.16)), .Names = c("x", "y"
	), row.names = 2:7, class = "data.frame")

	Nose.right=structure(list(x = c(0.165709488227992, 0.170777055719452, 0.160641920736532, 
	0.120101380804849, 0.0542230034158648, 0), y = c(0.991010207442792, 
	1.00031714102429, 1.02885836681128, 1.06310783775568, 1.13731502480188, 
	1.16)), .Names = c("x", "y"), row.names = 2:7, class = "data.frame")
	
	lines(Nose.left, col=head.col, lwd=head.lwd)
	lines(Nose.right, col=head.col, lwd=head.lwd)
	}

	if (draw.ears==TRUE){
	ear.left=structure(list(x = c(-0.977931701030928, -1.00236254295533, -1.01632302405498, 
	                               -1.03028350515464, -1.04075386597938, -1.0473410652921, -1.0442439862543, 
	                               -1.0342439862543, -1.03395386597938, -1.0372439862543, -1.0423410652921, 
	                               -1.04273410652921, -1.0452439862543, -1.0472439862543, -1.035079338487972, 
	                               -1.03283290378007, -0.995382302405498, -0.977931701030928, -0.967461340206185
	  ), y = c(0.222551774378187, 0.236080757623062, 0.222551774378187, 
	           0.202258299510875, 0.181964824643563, 0.154906858153814, 0.121084400041628, 
	           0.0737329586845665, 0.033146008949943, -0.014205432407118, -0.054792382141742, 
	           -0.10890831512124, -0.13596628161099, -0.169788739723176, -0.203611197835363, 
	           -0.223904672702674, -0.244198147569986, -0.237433655947549, -0.230669164325112
	  )), .Names = c("x", "y"))
	
	
	# create ear right as symmetric of ear left on x-axis
	ear.right=list(x=-ear.left$x, y=ear.left$y)
	
	lines(ear.left, col=head.col, lwd=head.lwd)
	
	lines(ear.right, col=head.col, lwd=head.lwd)
	
	}
	
	##############
	### RETURN	SOME PARAMETERS
	#####################
	invisible(list(zlim=zlim, palette=mypalette(palette.steps), co=curr.elec.coord, x=x, y=y))
	}

Try the erpR package in your browser

Any scripts or data that you put into this service are public.

erpR documentation built on May 31, 2017, 5:01 a.m.