1+ from contextlib import contextmanager
12from functools import cached_property
23from pathlib import Path
34
1920
2021# Mapping of region names to their geographic extent and projection
2122# extent [lon_min, lon_max, lat_min, lat_max] in PlateCarree coordinates
22- REGIONS = {
23+ DOMAINS = {
2324 "globe" : {
2425 "extent" : None , # full globe view
2526 "projection" : _PROJECTIONS ["orthographic" ],
4041
4142
4243class StatePlotter :
43- """A class to plot state fields on various REGIONS ."""
44+ """A class to plot state fields on various DOMAINS ."""
4445
4546 def __init__ (
4647 self ,
@@ -168,7 +169,6 @@ def plot_field(
168169 field = field [mask ]
169170 field = field [- 1 ] if field .ndim == 2 else field .squeeze ()
170171 finite = np .isfinite (field )
171-
172172 # TODO: clip data to domain would make plotting faster (especially tripcolor)
173173 # tried using Map.domain.extract() but too memory heavy (probably uses
174174 # meshgrid in the background), implement clipping with e.g.
@@ -183,24 +183,72 @@ def plot_field(
183183 # subplot.tripcolor( # also works but is slower
184184 # have to overwrite _plot_kwargs to avoid earthkit-plots trying to pass transform
185185 # PlateCarree based on NumpySource
186- subplot ._plot_kwargs = lambda source : {}
187- subplot .tricontourf (
188- x = x [finite ],
189- y = y [finite ],
190- z = field [finite ],
191- style = style ,
192- transform = proj ,
193- ** kwargs , # for earthkit.plots to work properly cmap and norm are needed here
194- )
186+
187+ # Normalize style and color-related kwargs
188+ style_to_use , plot_kwargs = self ._prepare_plot_kwargs (style , kwargs )
189+
190+ # Temporarily suppress earthkit-plots internal source-based kwargs
191+ with self ._temporary_plot_kwargs_override (subplot ):
192+ subplot .tricontourf (
193+ x = x [finite ],
194+ y = y [finite ],
195+ z = field [finite ],
196+ style = style_to_use ,
197+ transform = proj ,
198+ ** plot_kwargs ,
199+ ) # for earthkit.plots to work properly cmap and norm are needed here
195200 # TODO: gridlines etc would be nicer to have in the init, but I didn't get
196201 # them to overlay the plot layer
202+
197203 subplot .standard_layers ()
198204
199205 if colorbar :
200206 subplot .legend ()
201207 if title :
202208 subplot .title (title )
203209
210+ def _prepare_plot_kwargs (
211+ self ,
212+ style : ekp .styles .Style | None ,
213+ kwargs : dict ,
214+ ) -> tuple [ekp .styles .Style | None , dict ]:
215+ """Return a cleaned style and plot kwargs without mutating the input."""
216+ plot_kwargs = dict (kwargs )
217+
218+ # Discrete colors mode: if explicit 'colors' provided, drop cmap
219+ colors = plot_kwargs .get ("colors" , None )
220+ if colors is not None :
221+ plot_kwargs .pop ("cmap" , None )
222+ plot_kwargs .setdefault (
223+ "no_style" , True
224+ ) # avoid interpolation being performed by earthkit-plots resulting in an error
225+ return style , plot_kwargs
226+
227+ # Continuous mode: remove None entries to avoid matplotlib errors
228+ if plot_kwargs .get ("colors" , None ) is None :
229+ plot_kwargs .pop ("colors" , None )
230+ if plot_kwargs .get ("levels" , None ) is None :
231+ plot_kwargs .pop ("levels" , None )
232+
233+ return style , plot_kwargs
234+
235+ @contextmanager
236+ def _temporary_plot_kwargs_override (self , subplot : ekp .Map ):
237+ """Temporarily override internal _plot_kwargs to avoid transform issues."""
238+ has_attr = hasattr (subplot , "_plot_kwargs" )
239+ old = getattr (subplot , "_plot_kwargs" , None )
240+ subplot ._plot_kwargs = lambda source : {}
241+ try :
242+ yield
243+ finally :
244+ if has_attr :
245+ subplot ._plot_kwargs = old
246+ else :
247+ try :
248+ delattr (subplot , "_plot_kwargs" )
249+ except Exception :
250+ pass
251+
204252 @cached_property
205253 def _orthographic_tri (self ) -> Triangulation :
206254 """Compute the triangulation for the orthographic projection."""
0 commit comments