@@ -636,6 +636,7 @@ def _render_cutout_axes(ax, row, cutout_data, sources,
636636 marker_size , marker_symbol ,
637637 source_marker_size , current_source_marker_size ,
638638 current_source_color ,
639+ source_match_ids = None ,
639640 title = None , subtitle = "" ):
640641 """Render one diaSource cutout onto an existing matplotlib Axes
641642 using preloaded data from `_load_cutout`.
@@ -689,13 +690,28 @@ def _render_cutout_axes(ax, row, cutout_data, sources,
689690 src_xs = src_ys = None
690691 other_src_mask = current_src_mask = None
691692
693+ # Per-diaSource color resolution: map each diaSource to its
694+ # owning diaObject (in this panel's view), then to that
695+ # diaObject's palette color. Falls back to `current_source_color`
696+ # for sources whose owner is not present in `obj_ids`.
697+ if source_match_ids is None :
698+ match_ids_list = sources ["diaObjectId" ].tolist ()
699+ else :
700+ match_ids_list = list (source_match_ids )
701+ src_to_match = dict (zip (sources ["diaSourceId" ].tolist (),
702+ match_ids_list ))
703+ id_to_color = dict (zip (obj_ids .tolist (), obj_colors ))
704+
705+ def _color_for (diaSourceId ):
706+ return id_to_color .get (
707+ src_to_match .get (int (diaSourceId )), current_source_color )
708+
692709 if other_src_mask is not None and other_src_mask .any ():
693- # Share the current-diaSource color so the source positions are
694- # easy to read against the cm.bone background; the marker
695- # symbol (+ vs x) still distinguishes them.
710+ other_indices = np .flatnonzero (other_src_mask )
711+ other_colors = [_color_for (int (id_arr [i ])) for i in other_indices ]
696712 ax .scatter (src_xs [other_src_mask ], src_ys [other_src_mask ],
697713 s = source_marker_size , marker = "+" ,
698- c = current_source_color , linewidths = 1.0 ,
714+ c = other_colors , linewidths = 1.0 ,
699715 label = "other diaSource" )
700716
701717 if len (obj_ids ) > 0 :
@@ -718,7 +734,7 @@ def _render_cutout_axes(ax, row, cutout_data, sources,
718734 if current_src_mask is not None and current_src_mask .any ():
719735 ax .scatter (src_xs [current_src_mask ], src_ys [current_src_mask ],
720736 s = current_source_marker_size , marker = "x" ,
721- c = current_source_color , linewidths = 2.0 ,
737+ c = _color_for ( this_id ) , linewidths = 2.0 ,
722738 label = f"current diaSourceId={ this_id } " )
723739
724740 if title is None :
@@ -926,6 +942,16 @@ def plot_objects_sharing_sources(diaObjectId, sources1, sources2,
926942 left_overlays = _prepare_object_overlays (ro1 , palette )
927943 right_overlays = _prepare_object_overlays (ro2 , palette )
928944
945+ # diaSourceId -> run-2 diaObjectId, so the right panel can color
946+ # each diaSource by its run-2 owner. The left panel uses the
947+ # default (sources["diaObjectId"], the run-1 owner) since
948+ # `sources` is a slice of `sources1`.
949+ src_to_obj2 = dict (zip (
950+ sources2 ["diaSourceId" ].to_numpy (),
951+ sources2 ["diaObjectId" ].to_numpy ()))
952+ right_match_ids = [src_to_obj2 .get (int (sid ))
953+ for sid in sources ["diaSourceId" ]]
954+
929955 n_rows = len (sources )
930956 # One subfigure per row so each row can carry a single shared
931957 # suptitle above both panels; the per-axes title is then just the
@@ -959,6 +985,7 @@ def plot_objects_sharing_sources(diaObjectId, sources1, sources2,
959985 _render_cutout_axes (ax_right , row , cutout_data , sources ,
960986 * right_overlays ,
961987 title = "" , subtitle = column_labels [1 ],
988+ source_match_ids = right_match_ids ,
962989 ** common_kw )
963990
964991 if output_path is not None :
0 commit comments