Source code for dna_features_viewer.GraphicRecord.MultilinePlottableMixin

import matplotlib.pyplot as plt
from matplotlib.backends.backend_pdf import PdfPages
import numpy


class MultilinePlottableMixin:
    def plot_on_multiple_lines(
        self,
        n_lines=None,
        nucl_per_line=None,
        plot_sequence=False,
        figure_width="auto",
        **plot_params
    ):
        """Plot the features on different lines (one Matplotlib ax per line)

        Parameters
        ----------

        n_lines
          Number of lines on which the record will be plotted. A number of
          nucleotides per line can be provided instead (see below).
        
        nucl_per_line
          Number of nucleotides to be represented on every line (determines
          the number of lines ``n_lines``).
        
        plot_sequence
          Whether to plot the nucleotide sequence on each line

        figure_width
          Width of the figure in inches. Leave to auto for a width of either 10
          (if not sequence is plotted) or 0.15*nucl_per_line inches
          (if a sequence is plotted).

        **plot_params
          Parameters from ``graphic_record.plot()`` to be used in the plotting
          of the individual lines. This includes ``draw_line``, ``with_ruler``,
          ``annotate_inline``, ``plot_sequence``,
          ``evelate_outline_annotations``, ``strand_in_label_pixel_threshold``
        
        Returns
        -------

        figure, axes
          The matplotlib figure and axes generated.
        """

        if n_lines is None:
            n_lines = int(numpy.ceil(self.sequence_length / nucl_per_line))
        else:
            nucl_per_line = self.sequence_length // n_lines + 1

        if figure_width == "auto":
            if plot_sequence:
                figure_width = 0.15 * nucl_per_line
            else:
                figure_width = 10

        figures_heights = []

        def plot_line(line_index, ax=None):
            first, last = self.first_index, self.last_index
            line_start = first + line_index * nucl_per_line
            line_virtual_end = first + (line_index + 1) * nucl_per_line
            line_end = min(last, line_virtual_end)
            line_record = self.crop((line_start, line_end))
            line_ax, _ = line_record.plot(
                figure_width=figure_width,
                x_lim=(line_start, line_virtual_end),
                ax=ax,
                plot_sequence=plot_sequence,
                **plot_params
            )
            return line_ax

        for line_index in range(n_lines):
            line_ax = plot_line(line_index)
            figures_heights.append(line_ax.figure.get_figheight())
            plt.close(line_ax.figure)
        fig, axes = plt.subplots(
            n_lines,
            1,
            gridspec_kw={"height_ratios": figures_heights},
            figsize=(figure_width, 0.9 * sum(figures_heights)),
        )
        if n_lines == 1:
            axes = [axes]
        for line_index, ax in enumerate(axes):
            plot_line(line_index, ax=ax)
        fig.tight_layout()
        return fig, axes

    def plot_on_multiple_pages(
        self,
        pdf_target,
        n_lines=None,
        nucl_per_line=None,
        lines_per_page=5,
        figure_width="auto",
        **plot_params
    ):
        """Plot the features on different lines on different pages of a PDF.

        This function returns None
        
        Parameters
        ----------

        pdf_target
          Either a path to a PDF, or a file(-like) handle.

        n_lines
          Number of lines on which the record will be plotted. A number of
          nucleotides per line can be provided instead (see below).
        
        nucl_per_line
          Number of nucleotides to be represented on every line (determines
          the number of lines ``n_lines``).
        
        lines_per_page
          Number of lines on each page
        
        plot_sequence
          Whether to plot the nucleotide sequence on each line

        figure_width
          Width of the figure in inches. Leave to auto for a width of either 10
          (if not sequence is plotted) or 0.15*nucl_per_line inches
          (if a sequence is plotted).

        **plot_params
          Parameters from ``graphic_record.plot()`` to be used in the plotting
          of the individual lines. This includes ``draw_line``, ``with_ruler``,
          ``annotate_inline``, ``plot_sequence``,
          ``evelate_outline_annotations``, ``strand_in_label_pixel_threshold``
        """
        nucl_per_page = nucl_per_line * lines_per_page
        number_of_pages = int(numpy.ceil(self.sequence_length / nucl_per_page))
        with PdfPages(pdf_target) as pdf:
            for page_index in range(number_of_pages):
                first, last = self.first_index, self.last_index
                page_start = first + page_index * nucl_per_page
                page_end = first + (page_index + 1) * nucl_per_page
                page_end = min(last, page_end)
                page_record = self.crop((page_start, page_end))
                fig, axes = page_record.plot_on_multiple_lines(
                    nucl_per_line=nucl_per_line,
                    figure_width=figure_width,
                    **plot_params
                )
                pdf.savefig(fig)
                plt.close(fig)