@@ -333,6 +333,8 @@ def plot_genes(
333
333
x_range : Optional [gplt_params .x_range ] = None ,
334
334
title : Optional [gplt_params .title ] = None ,
335
335
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 ,
336
338
) -> gplt_params .figure :
337
339
debug = self ._log .debug
338
340
@@ -408,6 +410,101 @@ def plot_genes(
408
410
line_width = 0 ,
409
411
)
410
412
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
+
411
508
debug ("tidy up the plot" )
412
509
fig .ygrid .visible = False
413
510
yticks = [0.4 , 1.4 ]
0 commit comments