5151from spatialdata_plot .pl .utils import (
5252 _ax_show_and_transform ,
5353 _convert_shapes ,
54+ _datashader_canvas_from_dataframe ,
5455 _decorate_axs ,
5556 _get_collection_shape ,
5657 _get_colors_for_categorical_obs ,
@@ -81,14 +82,15 @@ def _want_decorations(color_vector: Any, na_color: Color) -> bool:
8182 cv = np .asarray (color_vector )
8283 if cv .size == 0 :
8384 return False
84- unique_vals = set (cv .tolist ())
85- if len (unique_vals ) != 1 :
85+ # Fast check: if any value differs from the first, there is variety → show decorations.
86+ first = cv .flat [0 ]
87+ if not (cv == first ).all ():
8688 return True
87- only_val = next ( iter ( unique_vals ))
89+ # All values are the same — suppress decorations when that value is the NA color.
8890 na_hex = na_color .get_hex ()
89- if isinstance (only_val , str ) and only_val .startswith ("#" ) and na_hex .startswith ("#" ):
90- return _hex_no_alpha (only_val ) != _hex_no_alpha (na_hex )
91- return bool (only_val != na_hex )
91+ if isinstance (first , str ) and first .startswith ("#" ) and na_hex .startswith ("#" ):
92+ return _hex_no_alpha (first ) != _hex_no_alpha (na_hex )
93+ return bool (first != na_hex )
9294
9395
9496def _reparse_points (
@@ -782,6 +784,10 @@ def _render_points(
782784 # from the registered points (see above) avoids duplicate-origin ambiguities.
783785 color_table_name = table_name
784786
787+ # When color was already loaded from a table (line 690), pass it directly
788+ # to avoid a redundant get_values() call inside _set_color_source_vec.
789+ _preloaded = points_pd_with_color [col_for_color ] if added_color_from_table and col_for_color is not None else None
790+
785791 color_source_vector , color_vector , _ = _set_color_source_vec (
786792 sdata = sdata_filt ,
787793 element = color_element ,
@@ -795,6 +801,7 @@ def _render_points(
795801 table_name = color_table_name ,
796802 render_type = "points" ,
797803 coordinate_system = coordinate_system ,
804+ preloaded_color_data = _preloaded ,
798805 )
799806
800807 if added_color_from_table and col_for_color is not None :
@@ -846,15 +853,16 @@ def _render_points(
846853 # use dpi/100 as a factor for cases where dpi!=100
847854 px = int (np .round (np .sqrt (render_params .size ) * (fig_params .fig .dpi / 100 )))
848855
849- # apply transformations
856+ # Apply transformations and materialize to pandas immediately so
857+ # datashader aggregates without dask scheduler overhead. See #379.
850858 transformed_element = PointsModel .parse (
851859 trans .transform (sdata_filt .points [element ][["x" , "y" ]]),
852860 annotation = sdata_filt .points [element ][sdata_filt .points [element ].columns .drop (["x" , "y" ])],
853861 transformations = {coordinate_system : Identity ()},
854- )
862+ ). compute ()
855863
856- plot_width , plot_height , x_ext , y_ext , factor = _get_extent_and_range_for_datashader_canvas (
857- transformed_element , coordinate_system , ax , fig_params
864+ plot_width , plot_height , x_ext , y_ext , factor = _datashader_canvas_from_dataframe (
865+ transformed_element , ax , fig_params
858866 )
859867
860868 # use datashader for the visualization of points
@@ -871,7 +879,7 @@ def _render_points(
871879 if isinstance (color_source_vector , pd .Series )
872880 else pd .Series (color_source_vector , index = series_index )
873881 )
874- transformed_element = transformed_element . assign ( col_for_color = source_series )
882+ transformed_element [ col_for_color ] = source_series
875883 else :
876884 if isinstance (color_vector , dd .Series ):
877885 color_vector = color_vector .compute ()
@@ -880,8 +888,7 @@ def _render_points(
880888 if isinstance (color_vector , pd .Series )
881889 else pd .Series (color_vector , index = series_index )
882890 )
883- transformed_element = transformed_element .assign (col_for_color = color_series )
884- transformed_element = transformed_element .rename (columns = {"col_for_color" : col_for_color })
891+ transformed_element [col_for_color ] = color_series
885892
886893 color_dtype = transformed_element [col_for_color ].dtype if col_for_color is not None else None
887894 color_by_categorical = col_for_color is not None and (
@@ -919,7 +926,7 @@ def _render_points(
919926 and isinstance (color_vector [0 ], str )
920927 and color_vector [0 ].startswith ("#" )
921928 ):
922- color_vector = np .asarray ([_hex_no_alpha (x ) for x in color_vector ])
929+ color_vector = np .asarray ([_hex_no_alpha (c ) for c in color_vector ])
923930
924931 nan_shaded = None
925932 if color_by_categorical or col_for_color is None :
0 commit comments