@@ -1040,90 +1040,90 @@ def _check_sphere(sphere, info=None, sphere_units="m"):
1040
1040
sphere = "auto"
1041
1041
1042
1042
if isinstance (sphere , str ):
1043
- if sphere not in ("auto" , "eeglab" ):
1044
- raise ValueError (
1045
- f'sphere, if str, must be "auto" or "eeglab", got { sphere } '
1046
- )
1043
+ _check_option (
1044
+ "sphere" , sphere , ("auto" , "eeglab" , "extra" , "eeg" , "cardinal" , "hpi" )
1045
+ )
1046
+
1047
+ if isinstance (sphere , str ) and sphere == "eeglab" :
1047
1048
assert info is not None
1048
1049
1049
- if sphere == "auto" :
1050
- R , r0 , _ = fit_sphere_to_headshape (
1051
- info , verbose = _verbose_safe_false (), units = "m"
1050
+ # We need coordinates for the 2D plane formed by
1051
+ # Fpz<->Oz and T7<->T8, as this plane will be the horizon (i.e. it
1052
+ # will determine the location of the head circle).
1053
+ #
1054
+ # We implement some special-handling in case Fpz is missing, as this seems to be
1055
+ # a quite common situation in numerous EEG labs.
1056
+ montage = info .get_montage ()
1057
+ if montage is None :
1058
+ raise ValueError (
1059
+ 'No montage was set on your data, but sphere="eeglab" can only work if '
1060
+ "digitization points for the EEG channels are available. Consider "
1061
+ "calling set_montage() to apply a montage."
1052
1062
)
1053
- sphere = tuple (r0 ) + (R ,)
1054
- sphere_units = "m"
1055
- elif sphere == "eeglab" :
1056
- # We need coordinates for the 2D plane formed by
1057
- # Fpz<->Oz and T7<->T8, as this plane will be the horizon (i.e. it
1058
- # will determine the location of the head circle).
1059
- #
1060
- # We implement some special-handling in case Fpz is missing, as
1061
- # this seems to be a quite common situation in numerous EEG labs.
1062
- montage = info .get_montage ()
1063
- if montage is None :
1064
- raise ValueError (
1065
- 'No montage was set on your data, but sphere="eeglab" '
1066
- "can only work if digitization points for the EEG "
1067
- "channels are available. Consider calling set_montage() "
1068
- "to apply a montage."
1069
- )
1070
- ch_pos = montage .get_positions ()["ch_pos" ]
1071
- horizon_ch_names = ("Fpz" , "Oz" , "T7" , "T8" )
1072
-
1073
- if "FPz" in ch_pos : # "fix" naming
1074
- ch_pos ["Fpz" ] = ch_pos ["FPz" ]
1075
- del ch_pos ["FPz" ]
1076
- elif "Fpz" not in ch_pos and "Oz" in ch_pos :
1077
- logger .info (
1078
- "Approximating Fpz location by mirroring Oz along the X and Y axes."
1063
+ ch_pos = montage .get_positions ()["ch_pos" ]
1064
+ horizon_ch_names = ("Fpz" , "Oz" , "T7" , "T8" )
1065
+
1066
+ if "FPz" in ch_pos : # "fix" naming
1067
+ ch_pos ["Fpz" ] = ch_pos ["FPz" ]
1068
+ del ch_pos ["FPz" ]
1069
+ elif "Fpz" not in ch_pos and "Oz" in ch_pos :
1070
+ logger .info (
1071
+ "Approximating Fpz location by mirroring Oz along the X and Y axes."
1072
+ )
1073
+ # This assumes Fpz and Oz have the same Z coordinate
1074
+ ch_pos ["Fpz" ] = ch_pos ["Oz" ] * [- 1 , - 1 , 1 ]
1075
+
1076
+ for ch_name in horizon_ch_names :
1077
+ if ch_name not in ch_pos :
1078
+ msg = (
1079
+ f'sphere="eeglab" requires digitization points of the following '
1080
+ f"electrode locations in the data: { ', ' .join (horizon_ch_names )} , "
1081
+ f"but could not find: { ch_name } "
1079
1082
)
1080
- # This assumes Fpz and Oz have the same Z coordinate
1081
- ch_pos ["Fpz" ] = ch_pos ["Oz" ] * [- 1 , - 1 , 1 ]
1082
-
1083
- for ch_name in horizon_ch_names :
1084
- if ch_name not in ch_pos :
1085
- msg = (
1086
- f'sphere="eeglab" requires digitization points of '
1087
- f"the following electrode locations in the data: "
1088
- f"{ ', ' .join (horizon_ch_names )} , but could not find: "
1089
- f"{ ch_name } "
1090
- )
1091
- if ch_name == "Fpz" :
1092
- msg += ", and was unable to approximate its location from Oz"
1093
- raise ValueError (msg )
1094
-
1095
- # Calculate the radius from: T7<->T8, Fpz<->Oz
1096
- radius = np .abs (
1083
+ if ch_name == "Fpz" :
1084
+ msg += ", and was unable to approximate its location from Oz"
1085
+ raise ValueError (msg )
1086
+
1087
+ # Calculate the radius from: T7<->T8, Fpz<->Oz
1088
+ radius = np .abs (
1089
+ [
1090
+ ch_pos ["T7" ][0 ], # X axis
1091
+ ch_pos ["T8" ][0 ], # X axis
1092
+ ch_pos ["Fpz" ][1 ], # Y axis
1093
+ ch_pos ["Oz" ][1 ], # Y axis
1094
+ ]
1095
+ ).mean ()
1096
+
1097
+ # Calculate the center of the head sphere.
1098
+ # Use 4 digpoints for each of the 3 axes to hopefully get a better approximation
1099
+ # than when using just 2 digpoints.
1100
+ sphere_locs = dict ()
1101
+ for idx , axis in enumerate (("X" , "Y" , "Z" )):
1102
+ sphere_locs [axis ] = np .mean (
1097
1103
[
1098
- ch_pos ["T7" ][0 ], # X axis
1099
- ch_pos ["T8" ][0 ], # X axis
1100
- ch_pos ["Fpz" ][1 ], # Y axis
1101
- ch_pos ["Oz" ][1 ], # Y axis
1104
+ ch_pos ["T7" ][idx ],
1105
+ ch_pos ["T8" ][idx ],
1106
+ ch_pos ["Fpz" ][idx ],
1107
+ ch_pos ["Oz" ][idx ],
1102
1108
]
1103
- ).mean ()
1104
-
1105
- # Calculate the center of the head sphere
1106
- # Use 4 digpoints for each of the 3 axes to hopefully get a better
1107
- # approximation than when using just 2 digpoints.
1108
- sphere_locs = dict ()
1109
- for idx , axis in enumerate (("X" , "Y" , "Z" )):
1110
- sphere_locs [axis ] = np .mean (
1111
- [
1112
- ch_pos ["T7" ][idx ],
1113
- ch_pos ["T8" ][idx ],
1114
- ch_pos ["Fpz" ][idx ],
1115
- ch_pos ["Oz" ][idx ],
1116
- ]
1117
- )
1118
- sphere = (sphere_locs ["X" ], sphere_locs ["Y" ], sphere_locs ["Z" ], radius )
1119
- sphere_units = "m"
1120
- del sphere_locs , radius , montage , ch_pos
1109
+ )
1110
+ sphere = (sphere_locs ["X" ], sphere_locs ["Y" ], sphere_locs ["Z" ], radius )
1111
+ sphere_units = "m"
1112
+ del sphere_locs , radius , montage , ch_pos
1113
+ elif isinstance (sphere , str ) or (
1114
+ isinstance (sphere , list ) and all (isinstance (s , str ) for s in sphere )
1115
+ ):
1116
+ # Fit a sphere to the head points.
1117
+ R , r0 , _ = fit_sphere_to_headshape (
1118
+ info , dig_kinds = sphere , verbose = _verbose_safe_false (), units = "m"
1119
+ )
1120
+ sphere = tuple (r0 ) + (R ,)
1121
+ sphere_units = "m"
1121
1122
elif isinstance (sphere , ConductorModel ):
1122
1123
if not sphere ["is_sphere" ] or len (sphere ["layers" ]) == 0 :
1123
1124
raise ValueError (
1124
- "sphere, if a ConductorModel, must be spherical "
1125
- "with multiple layers, not a BEM or single-layer "
1126
- f"sphere (got { sphere } )"
1125
+ "sphere, if a ConductorModel, must be spherical with multiple layers, "
1126
+ f"not a BEM or single-layer sphere (got { sphere } )"
1127
1127
)
1128
1128
sphere = tuple (sphere ["r0" ]) + (sphere ["layers" ][0 ]["rad" ],)
1129
1129
sphere_units = "m"
@@ -1132,8 +1132,8 @@ def _check_sphere(sphere, info=None, sphere_units="m"):
1132
1132
sphere = np .concatenate ([[0.0 ] * 3 , [sphere ]])
1133
1133
if sphere .shape != (4 ,):
1134
1134
raise ValueError (
1135
- "sphere must be float or 1D array of shape (4,), got "
1136
- f"array-like of shape { sphere .shape } "
1135
+ "sphere must be float or 1D array of shape (4,), "
1136
+ f"got array-like of shape { sphere .shape } "
1137
1137
)
1138
1138
_check_option ("sphere_units" , sphere_units , ("m" , "mm" ))
1139
1139
if sphere_units == "mm" :
0 commit comments