@@ -333,6 +333,8 @@ def plot_genes(
333333 x_range : Optional [gplt_params .x_range ] = None ,
334334 title : Optional [gplt_params .title ] = None ,
335335 output_backend : gplt_params .output_backend = gplt_params .output_backend_default ,
336+ gene_labels : Optional [gplt_params .gene_labels ] = None ,
337+ gene_labelset : Optional [gplt_params .gene_labelset ] = None ,
336338 ) -> gplt_params .figure :
337339 debug = self ._log .debug
338340
@@ -408,6 +410,101 @@ def plot_genes(
408410 line_width = 0 ,
409411 )
410412
413+ if gene_labels :
414+ debug ("determine new figure height and range to accommodate gene labels" )
415+
416+ # Increase the figure height by a certain factor, to accommodate labels.
417+ height_increase_factor = 1.3
418+ fig .height = int (fig .height * height_increase_factor )
419+
420+ # Get the original y_range.
421+ # Note: fig.y_range is not subscriptable.
422+ orig_y_range = fig .y_range .start , fig .y_range .end
423+
424+ # Determine the midpoint of the original range, to rescale outward from there.
425+ orig_mid_y_range = (orig_y_range [0 ] + orig_y_range [1 ]) / 2
426+ orig_y_range_extent = orig_y_range [1 ] - orig_y_range [0 ]
427+
428+ # Determine the new start and end points of the extended range.
429+ new_y_range_extent = orig_y_range_extent * height_increase_factor
430+ new_y_range_extent_half = new_y_range_extent / 2
431+ new_y_start = orig_mid_y_range - new_y_range_extent_half
432+ new_y_end = orig_mid_y_range + new_y_range_extent_half
433+
434+ # Set the new y_range.
435+ fig .y_range = bokeh .models .Range1d (new_y_start , new_y_end )
436+
437+ debug ("determine midpoint of each gene rectangle" )
438+ data ["mid_x" ] = (data ["start" ] + data ["end" ]) / 2
439+
440+ debug ("make gene labels and pointers" )
441+
442+ # Put gene_labels into a new column, where the gene_id matches.
443+ # Fill unmapped genes with empty strings, otherwise "NaN" would be displayed.
444+ data ["gene_label" ] = data ["ID" ].map (gene_labels ).fillna ("" )
445+
446+ # Put gene pointers (▲ or ▼) in a new column, depending on the strand.
447+ # Except if the gene_label is null or an empty string, which should not be shown.
448+ data ["gene_pointer" ] = data .apply (
449+ lambda row : ("▼" if row ["strand" ] == "+" else "▲" )
450+ if row ["gene_label" ]
451+ else "" ,
452+ axis = 1 ,
453+ )
454+
455+ # Put the pointer above or below the gene rectangle, depending on + or - strand.
456+ neg_strand_pointer_y = orig_mid_y_range - 1.1
457+ pos_strand_pointer_y = orig_mid_y_range + 1.1
458+ data ["pointer_y" ] = data ["strand" ].apply (
459+ lambda strand : pos_strand_pointer_y
460+ if strand == "+"
461+ else neg_strand_pointer_y
462+ )
463+
464+ # Put the label above or below the gene rectangle, depending on + or - strand.
465+ neg_strand_label_y = orig_mid_y_range - 1.25
466+ pos_strand_label_y = orig_mid_y_range + 1.3
467+ data ["label_y" ] = data ["strand" ].apply (
468+ lambda strand : pos_strand_label_y
469+ if strand == "+"
470+ else neg_strand_label_y
471+ )
472+
473+ # Get the data as a ColumnDataSource.
474+ data_as_cds = bokeh .models .ColumnDataSource (data )
475+
476+ # Create a LabelSet for the gene pointers.
477+ gene_pointers_ls = bokeh .models .LabelSet (
478+ source = data_as_cds ,
479+ x = "mid_x" ,
480+ y = "pointer_y" ,
481+ text = "gene_pointer" ,
482+ text_align = "center" ,
483+ text_baseline = "middle" ,
484+ text_font_size = "9pt" ,
485+ text_color = "#444444" ,
486+ )
487+
488+ # Create a LabelSet for the gene labels.
489+ gene_labels_ls = bokeh .models .LabelSet (
490+ source = data_as_cds ,
491+ x = "mid_x" ,
492+ y = "label_y" ,
493+ text = "gene_label" ,
494+ text_align = "left" ,
495+ text_baseline = "middle" ,
496+ text_font_size = "9pt" ,
497+ text_color = "#444444" ,
498+ x_offset = 8 ,
499+ )
500+
501+ # Add the markers and labels to the figure.
502+ fig .add_layout (gene_pointers_ls )
503+ fig .add_layout (gene_labels_ls )
504+
505+ if gene_labelset :
506+ fig .add_layout (gene_labelset )
507+
411508 debug ("tidy up the plot" )
412509 fig .ygrid .visible = False
413510 yticks = [0.4 , 1.4 ]
0 commit comments