From c358936e5e96e0530215a34baaecef98b06fe206 Mon Sep 17 00:00:00 2001 From: Benedikt Ehinger Date: Sat, 16 Nov 2024 02:12:52 +0100 Subject: [PATCH] upgrade to template 10-05 locations (#58) * fix labels, fix #39 * added a backward compatability with warning * removed NaturalNeighbours for now until #55 is fixed * added example of contourf and surface to docs * added an example with custom bounds #38 * added 10-05 template electrodes; breaking: new default enlarge = 1.0 for eeg-topoplot only * renamed T3 is now T7 T4 is now T8 T5 is now P7 T6 is now P8 according to the MCN system * fixed 10/20 old labels work now too * fixed bug * added test for 10-20/10-05 * fix indent * formatting * s/begin/let * docstrings * s/warn/warning * docs fix * again * :facepalm: * I give up --------- Co-authored-by: Phillip Alday Co-authored-by: Phillip Alday --- assets/1005.tsv | 349 ++++++++++++++++++ assets/layout_10_05.bin | Bin 0 -> 5568 bytes ...bin.jl => write_template-positions-bin.jl} | 9 +- docs/make.jl | 3 + docs/src/eeg.md | 12 +- docs/src/functions.md | 1 - docs/src/topo_series.jl | 180 +++++++++ src/eeg.jl | 105 +++++- test/runtests.jl | 9 + 9 files changed, 655 insertions(+), 13 deletions(-) create mode 100644 assets/1005.tsv create mode 100644 assets/layout_10_05.bin rename assets/{write_10_20-bin.jl => write_template-positions-bin.jl} (56%) create mode 100644 docs/src/topo_series.jl diff --git a/assets/1005.tsv b/assets/1005.tsv new file mode 100644 index 0000000..dda25d5 --- /dev/null +++ b/assets/1005.tsv @@ -0,0 +1,349 @@ +label x y +AF1 -0.1371 0.6754 +AF10 0.8580 1.1810 +AF10h 0.7078 0.9743 +AF1h -0.0683 0.6700 +AF2 0.1371 0.6754 +AF2h 0.0683 0.6700 +AF3 -0.2781 0.6983 +AF3h -0.2069 0.6848 +AF4 0.2781 0.6983 +AF4h 0.2069 0.6848 +AF5 -0.4269 0.7404 +AF5h -0.3513 0.7166 +AF6 0.4269 0.7404 +AF6h 0.3513 0.7166 +AF7 -0.5878 0.8090 +AF7h -0.5056 0.7708 +AF8 0.5878 0.8090 +AF8h 0.5056 0.7708 +AF9 -0.8580 1.1810 +AF9h -0.7078 0.9743 +AFF1 -0.1588 0.5428 +AFF10 1.0322 1.0322 +AFF10h 0.8515 0.8515 +AFF1h -0.0790 0.5366 +AFF2 0.1588 0.5428 +AFF2h 0.0790 0.5366 +AFF3 -0.3244 0.5692 +AFF3h -0.2403 0.5535 +AFF4 0.3244 0.5692 +AFF4h 0.2403 0.5535 +AFF5 -0.5042 0.6197 +AFF5h -0.4120 0.5908 +AFF6 0.5042 0.6197 +AFF6h 0.4120 0.5908 +AFF7 -0.7071 0.7071 +AFF7h -0.6021 0.6576 +AFF8 0.7071 0.7071 +AFF8h 0.6021 0.6576 +AFF9 -1.0322 1.0322 +AFF9h -0.8515 0.8515 +AFFz 0.0000 0.5345 +AFp1 -0.1089 0.8246 +AFp10 0.6626 1.3007 +AFp10h 0.5467 1.0730 +AFp1h -0.0543 0.8217 +AFp2 0.1089 0.8246 +AFp2h 0.0543 0.8217 +AFp3 -0.2195 0.8369 +AFp3h -0.1639 0.8296 +AFp4 0.2195 0.8369 +AFp4h 0.1639 0.8296 +AFp5 -0.3339 0.8583 +AFp5h -0.2761 0.8463 +AFp6 0.3339 0.8583 +AFp6h 0.2761 0.8463 +AFp7 -0.4540 0.8910 +AFp7h -0.3931 0.8731 +AFp8 0.4540 0.8910 +AFp8h 0.3931 0.8731 +AFp9 -0.6626 1.3007 +AFp9h -0.5467 1.0730 +AFpz 0.0000 0.8207 +AFz 0.0000 0.6682 +C1 -0.1965 0.0000 +C1h -0.0973 0.0000 +C2 0.1965 0.0000 +C2h 0.0973 0.0000 +C3 -0.4088 0.0000 +C3h -0.2996 0.0000 +C4 0.4088 0.0000 +C4h 0.2996 0.0000 +C5 -0.6582 0.0000 +C5h -0.5271 0.0000 +C6 0.6582 0.0000 +C6h 0.5271 0.0000 +CCP1 -0.1976 -0.1008 +CCP1h -0.0978 -0.0991 +CCP2 0.1976 -0.1008 +CCP2h 0.0978 -0.0991 +CCP3 -0.4110 -0.1085 +CCP3h -0.3012 -0.1039 +CCP4 0.4110 -0.1085 +CCP4h 0.3012 -0.1039 +CCP5 -0.6620 -0.1244 +CCP5h -0.5300 -0.1151 +CCP6 0.6620 -0.1244 +CCP6h 0.5300 -0.1151 +CCPz 0.0000 -0.0985 +CP1 -0.1933 -0.2034 +CP1h -0.0958 -0.2000 +CP2 0.1933 -0.2034 +CP2h 0.0958 -0.2000 +CP3 -0.4013 -0.2183 +CP3h -0.2944 -0.2094 +CP4 0.4013 -0.2183 +CP4h 0.2944 -0.2094 +CP5 -0.6433 -0.2489 +CP5h -0.5164 -0.2311 +CP6 0.6433 -0.2489 +CP6h 0.5164 -0.2311 +CPP1 -0.1859 -0.3098 +CPP1h -0.0922 -0.3050 +CPP2 0.1859 -0.3098 +CPP2h 0.0922 -0.3050 +CPP3 -0.3843 -0.3307 +CPP3h -0.2826 -0.3181 +CPP4 0.3843 -0.3307 +CPP4h 0.2826 -0.3181 +CPP5 -0.6113 -0.3731 +CPP5h -0.4930 -0.3486 +CPP6 0.6113 -0.3731 +CPP6h 0.4930 -0.3486 +CPPz 0.0000 -0.3034 +CPz 0.0000 -0.1989 +Cz 0.0000 0.0000 +F1 -0.1747 0.4220 +F10 1.1810 0.8580 +F10h 0.9743 0.7078 +F1h -0.0867 0.4161 +F2 0.1747 0.4220 +F2h 0.0867 0.4161 +F3 -0.3592 0.4472 +F3h -0.2650 0.4321 +F4 0.3592 0.4472 +F4h 0.2650 0.4321 +F5 -0.5653 0.4970 +F5h -0.4586 0.4683 +F6 0.5653 0.4970 +F6h 0.4586 0.4683 +F7 -0.8090 0.5878 +F7h -0.6812 0.5356 +F8 0.8090 0.5878 +F8h 0.6812 0.5356 +F9 -1.1810 0.8580 +F9h -0.9743 0.7078 +FC1 -0.1933 0.2034 +FC1h -0.0958 0.2000 +FC2 0.1933 0.2034 +FC2h 0.0958 0.2000 +FC3 -0.4013 0.2183 +FC3h -0.2944 0.2094 +FC4 0.4013 0.2183 +FC4h 0.2944 0.2094 +FC5 -0.6433 0.2489 +FC5h -0.5164 0.2311 +FC6 0.6433 0.2489 +FC6h 0.5164 0.2311 +FCC1 -0.1976 0.1008 +FCC1h -0.0978 0.0991 +FCC2 0.1976 0.1008 +FCC2h 0.0978 0.0991 +FCC3 -0.4110 0.1085 +FCC3h -0.3012 0.1039 +FCC4 0.4110 0.1085 +FCC4h 0.3012 0.1039 +FCC5 -0.6620 0.1244 +FCC5h -0.5300 0.1151 +FCC6 0.6620 0.1244 +FCC6h 0.5300 0.1151 +FCCz 0.0000 0.0985 +FCz 0.0000 0.1989 +FFC1 -0.1859 0.3098 +FFC1h -0.0922 0.3050 +FFC2 0.1859 0.3098 +FFC2h 0.0922 0.3050 +FFC3 -0.3843 0.3307 +FFC3h -0.2826 0.3181 +FFC4 0.3843 0.3307 +FFC4h 0.2826 0.3181 +FFC5 -0.6113 0.3731 +FFC5h -0.4930 0.3486 +FFC6 0.6113 0.3731 +FFC6h 0.4930 0.3486 +FFCz 0.0000 0.3034 +FFT10 1.3007 0.6626 +FFT10h 1.0730 0.5467 +FFT7 -0.8910 0.4540 +FFT7h -0.7425 0.4070 +FFT8 0.8910 0.4540 +FFT8h 0.7425 0.4070 +FFT9 -1.3007 0.6626 +FFT9h -1.0730 0.5467 +FT10 1.3884 0.4512 +FT10h 1.1453 0.3722 +FT7 -0.9511 0.3090 +FT7h -0.7860 0.2738 +FT8 0.9511 0.3090 +FT8h 0.7860 0.2738 +FT9 -1.3884 0.4512 +FT9h -1.1453 0.3722 +FTT10 1.4418 0.2284 +FTT10h 1.1894 0.1884 +FTT7 -0.9877 0.1564 +FTT7h -0.8120 0.1376 +FTT8 0.9877 0.1564 +FTT8h 0.8120 0.1376 +FTT9 -1.4418 0.2284 +FTT9h -1.1894 0.1884 +Fp1 -0.3090 0.9511 +Fp1h -0.1564 0.9877 +Fp2 0.3090 0.9511 +Fp2h 0.1564 0.9877 +Fpz 0.0000 1.0000 +Fz 0.0000 0.4142 +I1 -0.4512 -1.3884 +I1h -0.2284 -1.4418 +I2 0.4512 -1.3884 +I2h 0.2284 -1.4418 +Iz 0.0000 -1.4598 +LPA -1.4598 -0.0000 +N1 -0.4512 1.3884 +N1h -0.2284 1.4418 +N2 0.4512 1.3884 +N2h 0.2284 1.4418 +NAS -0.0000 1.4598 +NFp1 -0.3722 1.1453 +NFp1h -0.1884 1.1894 +NFp2 0.3722 1.1453 +NFp2h 0.1884 1.1894 +NFpz -0.0000 1.2042 +Nz -0.0000 1.4598 +O1 -0.3090 -0.9511 +O1h -0.1564 -0.9877 +O2 0.3090 -0.9511 +O2h 0.1564 -0.9877 +OI1 -0.3722 -1.1453 +OI1h -0.1884 -1.1894 +OI2 0.3722 -1.1453 +OI2h 0.1884 -1.1894 +OIz 0.0000 -1.2042 +Oz 0.0000 -1.0000 +P1 -0.1747 -0.4220 +P10 1.1810 -0.8580 +P10h 0.9743 -0.7078 +P1h -0.0867 -0.4161 +P2 0.1747 -0.4220 +P2h 0.0867 -0.4161 +P3 -0.3592 -0.4472 +P3h -0.2650 -0.4321 +P4 0.3592 -0.4472 +P4h 0.2650 -0.4321 +P5 -0.5653 -0.4970 +P5h -0.4586 -0.4683 +P6 0.5653 -0.4970 +P6h 0.4586 -0.4683 +P7 -0.8090 -0.5878 +P7h -0.6812 -0.5356 +P8 0.8090 -0.5878 +P8h 0.6812 -0.5356 +P9 -1.1810 -0.8580 +P9h -0.9743 -0.7078 +PO1 -0.1371 -0.6754 +PO10 0.8580 -1.1810 +PO10h 0.7078 -0.9743 +PO1h -0.0683 -0.6700 +PO2 0.1371 -0.6754 +PO2h 0.0683 -0.6700 +PO3 -0.2781 -0.6983 +PO3h -0.2069 -0.6848 +PO4 0.2781 -0.6983 +PO4h 0.2069 -0.6848 +PO5 -0.4269 -0.7404 +PO5h -0.3513 -0.7166 +PO6 0.4269 -0.7404 +PO6h 0.3513 -0.7166 +PO7 -0.5878 -0.8090 +PO7h -0.5056 -0.7708 +PO8 0.5878 -0.8090 +PO8h 0.5056 -0.7708 +PO9 -0.8580 -1.1810 +PO9h -0.7078 -0.9743 +POO1 -0.1089 -0.8246 +POO10 0.6626 -1.3007 +POO10h 0.5467 -1.0730 +POO1h -0.0543 -0.8217 +POO2 0.1089 -0.8246 +POO2h 0.0543 -0.8217 +POO3 -0.2195 -0.8369 +POO3h -0.1639 -0.8296 +POO4 0.2195 -0.8369 +POO4h 0.1639 -0.8296 +POO5 -0.3339 -0.8583 +POO5h -0.2761 -0.8463 +POO6 0.3339 -0.8583 +POO6h 0.2761 -0.8463 +POO7 -0.4540 -0.8910 +POO7h -0.3931 -0.8731 +POO8 0.4540 -0.8910 +POO8h 0.3931 -0.8731 +POO9 -0.6626 -1.3007 +POO9h -0.5467 -1.0730 +POOz 0.0000 -0.8207 +POz 0.0000 -0.6682 +PPO1 -0.1588 -0.5428 +PPO10 1.0322 -1.0322 +PPO10h 0.8515 -0.8515 +PPO1h -0.0790 -0.5366 +PPO2 0.1588 -0.5428 +PPO2h 0.0790 -0.5366 +PPO3 -0.3244 -0.5692 +PPO3h -0.2403 -0.5535 +PPO4 0.3244 -0.5692 +PPO4h 0.2403 -0.5535 +PPO5 -0.5042 -0.6197 +PPO5h -0.4120 -0.5908 +PPO6 0.5042 -0.6197 +PPO6h 0.4120 -0.5908 +PPO7 -0.7071 -0.7071 +PPO7h -0.6021 -0.6576 +PPO8 0.7071 -0.7071 +PPO8h 0.6021 -0.6576 +PPO9 -1.0322 -1.0322 +PPO9h -0.8515 -0.8515 +PPOz 0.0000 -0.5345 +Pz 0.0000 -0.4142 +RPA 1.4598 0.0000 +T10 1.4598 0.0000 +T10h 1.2042 0.0000 +T7 -1.0000 0.0000 +T7h -0.8072 0.0000 +T8 1.0000 0.0000 +T8h 0.8072 -0.0000 +T9 -1.4598 -0.0000 +T9h -1.2042 -0.0000 +TP10 1.3884 -0.4512 +TP10h 1.1453 -0.3722 +TP7 -0.9511 -0.3090 +TP7h -0.7860 -0.2738 +TP8 0.9511 -0.3090 +TP8h 0.7860 -0.2738 +TP9 -1.3884 -0.4512 +TP9h -1.1453 -0.3722 +TPP10 1.3007 -0.6626 +TPP10h 1.0730 -0.5467 +TPP7 -0.8910 -0.4540 +TPP7h -0.7425 -0.4070 +TPP8 0.8910 -0.4540 +TPP8h 0.7425 -0.4070 +TPP9 -1.3007 -0.6626 +TPP9h -1.0730 -0.5467 +TTP10 1.4418 -0.2284 +TTP10h 1.1894 -0.1884 +TTP7 -0.9877 -0.1564 +TTP7h -0.8120 -0.1376 +TTP8 0.9877 -0.1564 +TTP8h 0.8120 -0.1376 +TTP9 -1.4418 -0.2284 +TTP9h -1.1894 -0.1884 \ No newline at end of file diff --git a/assets/layout_10_05.bin b/assets/layout_10_05.bin new file mode 100644 index 0000000000000000000000000000000000000000..e828a2385e91854f38127ecb0d14b9d1ff963f15 GIT binary patch literal 5568 zcmdUzc~n(Z6vkO-Dw&y_VFoRt>%<@ z$2j@nl%a>mW>d$>{&hQVPN(SiyC#+Q-NQciabfCLx#ex|$XNFL;nb78lg}`iz;t5J z)&XC2OUC#G24nmJjY%k-+wxorj~`vWgxS^1U;-27!I%d-4|Tt@p!-R#pT6^>H2)kL zGi31%U&zj-?>`F_oQ3yu2w_WG+j?|CwMy#e$X=*OkhGhBc5Gbe~_Kh=ss-AWp({^)63VzhsXg%K9PLe{mUKXMGm{Is;ya{Ot#18 z5Ln{jf9||+y?N1k_oDTxs_g-Wei=GCy4jlvzm2kzj0<~DO>8!?S31?O838M)Y%5cH zGN_Q#oJ!s|(@AB~&Hb7S9klhD=qd@TdT#f17V(vazisjEz!RoA7WhC53ES&=3i%&B zv!U$)3+j6T+xIb*G?P8}d`1fPwSdLXUauQcqhxWb_+1iKefGX^-H2J}ilhF*S)xaV ze(BwSXQwP#lys zMP~bZhqB6nn#y)+9L&1Ac62(!0ju;+#4}v(XkV=(JIlMY?kF(o469r%`0>oU$>-R8 zS_zL`OQTq258J^q@$~GjK^3Q)SY_XV^?TH-eV#Sbp*=dDRkqjh1m3>mqUL}vpwB7b zR{Aj|HZl!)}2(wIuq8NRK_|J)}2(w zc|m>$lgcD&oK6tu+OA2_L;EHq%!td`c9+&mA=%z zp-IxWlCat@i$4@U2j51z%gvb{i*+y=tj3G46dx$Q)L!S089r55jTiqcK3HYZ%MlIq zik4*fYw^kAw}r*mq5%1ce-~X-S@c@`z4&yMMb9N(`T+6sDvSRYAFnc=Cw_pDf8rmF z`xxY-F)oaJWaKBSjC^F|C#x*?LdZwC7h+-cUI@A^{jt%PLdRLgg`wjNU1yb{;|yJA zm8Cz1j!S>c!fJmEy=1k2gFdp13qv0nddVt79~pWH-N-gr`bg-b^pPyA&I5YM&`ao; zMef;*`z`nmV_X_`L+;sFSiNV14`Jk;p(FoN*!?6A wHkn~81+W_TZ7ej^`1@c isnan(x) ? -Inf : x, m) + xy = xys[i] + @show peak_xy + @show xy + #@test isapprox(xy, peak_xy; atol=0.02) + @show isapprox(xy, peak_xy; atol=0.02) + fig +end + +# ╔═╡ 872ac6a4-ddaa-4dfb-a40d-9d5ea55bdb3d +let + f = Figure() + axis = Axis(f[1, 1], aspect = 1) + xlims!(low = -2, high = 2) + ylims!(low = -2, high = 2) + + data = [0, 0, 0] + pos1 = [Point2f(-1, -1), Point2f(-1.0, 0.0), Point2f(0, -1)] + pos2 = [Point2f(1, 1), Point2f(1.0, 0.0), Point2f(0, 1)] + + pos1 = pos1 .- mean(pos1) + pos2 = pos2 .- mean(pos2) + eeg_topoplot!(axis, data, positions=pos1) + eeg_topoplot!(axis, data, positions=pos2) + f +end + +# ╔═╡ Cell order: +# ╠═2fafb0da-f3a9-11ec-0ddf-6725344070fe +# ╠═6cda29dc-7086-4079-83c6-3650204a82ff +# ╠═c4a25915-c7f5-453a-a4f0-4b40ebedea4c +# ╠═e0cc560f-d3e8-415b-b22d-6bca23ef093c +# ╠═59b87673-02d2-4deb-90be-74d923d170eb +# ╠═452a245c-773a-4303-a970-f2592c3e879f +# ╠═f4b81740-d907-42ae-a0df-f46fb2f2cb15 +# ╠═77dc1ba9-9484-485b-a49d-9aa231ef4983 +# ╠═aad784ee-6bb7-4f3c-8444-be050456ddea +# ╠═237e4f4a-cdf2-4bac-8096-de8050251745 +# ╠═42f7755b-80f4-4185-8d21-42e11730e0fc +# ╠═f522329b-3653-4059-9955-8cd05570e923 +# ╠═311f10ff-deb8-4f82-8b12-d5b643656828 +# ╠═a9d2a2e2-6c8c-4cfc-9fed-b5e082cb44af +# ╠═c358633f-8d18-4c5e-80f7-ab972e8860be +# ╠═9fa5c598-3578-4989-9585-29fd32ae1056 +# ╠═c0a2ad2e-ccce-4e80-b52c-75f1428ed182 +# ╠═d7620a42-d54c-4244-a820-d15aecdae626 +# ╠═ec59c704-ae33-4a62-82ce-63acc6b17793 +# ╠═f3d1f3cc-f7c9-4ef4-ba4f-3d32f2509cad +# ╠═872ac6a4-ddaa-4dfb-a40d-9d5ea55bdb3d diff --git a/src/eeg.jl b/src/eeg.jl index 37c953d..c5bcb08 100644 --- a/src/eeg.jl +++ b/src/eeg.jl @@ -8,6 +8,7 @@ default_theme(scene, TopoPlot)..., label_scatter=true, contours=true, + enlarge=1, ) end @@ -22,9 +23,13 @@ Attributes: # Some attributes from topoplot are set to different defaults: * `label_scatter = true` * `contours = true` +* `enlarge = 1`` Otherwise the recipe just uses the [`topoplot`](@ref) defaults and passes through the attributes. +!!! note + The 10-05 channel locations are "perfect" spherical locations based on https://github.com/sappelhoff/eeg_positions/ - the mne-default 10-20 locations are _not_, they were warped to a fsaverage head. Which makes the locations provided here good for visualizations, but not good for source localisation. + !!! note You MUST set `label_text=true` for labels to display. """ @@ -59,9 +64,97 @@ function draw_ear_nose!(parent, circle; kw...) end + +const CHANNELS_10_05 = ["af1","af10","af10h","af1h","af2","af2h","af3","af3h", + "af4","af4h","af5","af5h","af6","af6h","af7","af7h","af8", + "af8h","af9","af9h","aff1","aff10","aff10h","aff1h","aff2", + "aff2h","aff3","aff3h","aff4","aff4h","aff5","aff5h","aff6", + "aff6h","aff7","aff7h","aff8","aff8h","aff9","aff9h","affz", + "afp1","afp10","afp10h","afp1h","afp2","afp2h","afp3","afp3h", + "afp4","afp4h","afp5","afp5h","afp6","afp6h","afp7","afp7h", + "afp8","afp8h","afp9","afp9h","afpz","afz","c1","c1h","c2", + "c2h","c3","c3h","c4","c4h","c5","c5h","c6","c6h","ccp1", + "ccp1h","ccp2","ccp2h","ccp3","ccp3h","ccp4","ccp4h","ccp5", + "ccp5h","ccp6","ccp6h","ccpz","cp1","cp1h","cp2","cp2h","cp3", + "cp3h","cp4","cp4h","cp5","cp5h","cp6","cp6h","cpp1","cpp1h", + "cpp2","cpp2h","cpp3","cpp3h","cpp4","cpp4h","cpp5","cpp5h", + "cpp6","cpp6h","cppz","cpz","cz","f1","f10","f10h","f1h", + "f2","f2h","f3","f3h","f4","f4h","f5","f5h","f6","f6h", + "f7","f7h","f8","f8h","f9","f9h","fc1","fc1h","fc2","fc2h", + "fc3","fc3h","fc4","fc4h","fc5","fc5h","fc6","fc6h","fcc1", + "fcc1h","fcc2","fcc2h","fcc3","fcc3h","fcc4","fcc4h","fcc5", + "fcc5h","fcc6","fcc6h","fccz","fcz","ffc1","ffc1h","ffc2", + "ffc2h","ffc3","ffc3h","ffc4","ffc4h","ffc5","ffc5h","ffc6", + "ffc6h","ffcz","fft10","fft10h","fft7","fft7h","fft8","fft8h", + "fft9","fft9h","ft10","ft10h","ft7","ft7h","ft8","ft8h","ft9", + "ft9h","ftt10","ftt10h","ftt7","ftt7h","ftt8","ftt8h","ftt9", + "ftt9h","fp1","fp1h","fp2","fp2h","fpz","fz","i1","i1h","i2", + "i2h","iz","lpa","n1","n1h","n2","n2h","nas","nfp1","nfp1h", + "nfp2","nfp2h","nfpz","nz","o1","o1h","o2","o2h","oi1","oi1h", + "oi2","oi2h","oiz","oz","p1","p10","p10h","p1h","p2","p2h", + "p3","p3h","p4","p4h","p5","p5h","p6","p6h","p7","p7h","p8", + "p8h","p9","p9h","po1","po10","po10h","po1h","po2","po2h", + "po3","po3h","po4","po4h","po5","po5h","po6","po6h","po7", + "po7h","po8","po8h","po9","po9h","poo1","poo10","poo10h", + "poo1h","poo2","poo2h","poo3","poo3h","poo4","poo4h","poo5", + "poo5h","poo6","poo6h","poo7","poo7h","poo8","poo8h","poo9", + "poo9h","pooz","poz","ppo1","ppo10","ppo10h","ppo1h","ppo2", + "ppo2h","ppo3","ppo3h","ppo4","ppo4h","ppo5","ppo5h","ppo6", + "ppo6h","ppo7","ppo7h","ppo8","ppo8h","ppo9","ppo9h","ppoz", + "pz","rpa","t10","t10h","t7","t7h","t8","t8h","t9","t9h", + "tp10","tp10h","tp7","tp7h","tp8","tp8h","tp9","tp9h","tpp10", + "tpp10h","tpp7","tpp7h","tpp8","tpp8h","tpp9","tpp9h","ttp10", + "ttp10h","ttp7","ttp7h","ttp8","ttp8h","ttp9","ttp9h"] + +""" + CHANNEL_TO_POSITION_10_05 + +Positions for 10/05 channels. + +These are used for all channel locations, including the 10/20 subset. + +!!! warning + The 10/05 channel locations are "perfect" spherical locations based on + https://github.com/sappelhoff/eeg_positions/, while + the default 10/20 locations in MNE-Python are **not**. + The coordinates here are better for visualization, while the MNE provided + coordinates, which were warped to a fsaverage head are better for source + localization. +""" +const CHANNEL_TO_POSITION_10_05 = let + # We load this during precompilation, so that this gets stored as a global + # that's immediately loaded when loading the package + result = Matrix{Float64}(undef, 348, 2) + read!(assetpath("layout_10_05.bin"), result) + positions = Point2f.(result[:, 1], result[:, 2]) + d = Dict{String,Point2f}(zip(CHANNELS_10_05, positions)) + d["t3"] = d["t7"] + d["t4"] = d["t8"] + d["t5"] = d["p7"] + d["t6"] = d["p8"] + d +end + +# even though these are not actively used, sometimes they can be helpful just to plot a default subset of channels. Therefore we havent deleted them yet (because 10_05 is a superset) const CHANNELS_10_20 = ["fp1", "f3", "c3", "p3", "o1", "f7", "t3", "t5", "fz", "cz", "pz", "fp2", "f4", "c4", "p4", "o2", "f8", "t4", "t6"] -const CHANNEL_TO_POSITION_10_20 = begin +""" + CHANNEL_TO_POSITION_10_20 + +Positions for 10/20 channels. + +See also [CHANNEL_TO_POSITION_10_05](@ref) + +!!! warning + This is deprecated and unused! The coordinates here differ slightly from the + newer 10/05 coordinates now used in all plotting functions. + The 10-05 channel locations are "perfect" spherical locations based on + https://github.com/sappelhoff/eeg_positions/, while + the default 10/20 locations in MNE-Python are **not**. + The MNE locations were warped to a fsaverage head, which is better for source + localization but worse for visualization. +""" +const CHANNEL_TO_POSITION_10_20 = let # We load this during precompilation, so that this gets stored as a global # that's immediately loaded when loading the package result = Matrix{Float64}(undef, 19, 2) @@ -72,15 +165,15 @@ end """ labels2positions(labels) -Currently only supports 10/20 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_20`. +Currently supports 10/20 and 10/05 layout, by looking it up in `TopoPlots.CHANNEL_TO_POSITION_10_05`. """ function labels2positions(labels) return map(labels) do label key = lowercase(label) - if haskey(CHANNEL_TO_POSITION_10_20, key) - return CHANNEL_TO_POSITION_10_20[key] + if haskey(CHANNEL_TO_POSITION_10_05, key) + return CHANNEL_TO_POSITION_10_05[key] else - error("Currently only 10/20 is supported. Found label: $(label)") + error("Currently only 10/05 is supported. Found label: $(label)") end end end @@ -95,7 +188,7 @@ function Makie.plot!(plot::EEG_TopoPlot) positions = lift(plot.labels, plot.positions) do labels, positions if positions isa Makie.Automatic - (!isnothing(labels) && labels != Makie.Automatic) || error("Either positions or labels (10/20-lookup) have to be specified") + (!isnothing(labels) && labels != Makie.Automatic) || error("Either positions or labels (10/05-lookup) have to be specified") return labels2positions(labels) else diff --git a/test/runtests.jl b/test/runtests.jl index 2229fe7..09e45bb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -196,3 +196,12 @@ begin f = TopoPlots.eeg_topoplot(1:10; labels=TopoPlots.CHANNELS_10_20[1:10], label_text=true) @test_figure("test-eeg-channel-labels", f) end + +let + pos = TopoPlots.labels2positions(TopoPlots.CHANNELS_10_20) + pos10_05 = TopoPlots.labels2positions(TopoPlots.CHANNELS_10_05) + @test pos[TopoPlots.CHANNELS_10_20 .== "t3"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "t7"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t4"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "t8"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t5"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "p7"] + @test pos[TopoPlots.CHANNELS_10_20 .== "t6"] == pos10_05[TopoPlots.CHANNELS_10_05 .== "p8"] +end