Package org.bioviz.protannot

Source Code of org.bioviz.protannot.GenomeView

package org.bioviz.protannot;

import com.affymetrix.genometryImpl.BioSeq;
import com.affymetrix.genometryImpl.SeqSpan;
import com.affymetrix.genometryImpl.span.SimpleMutableSeqSpan;
import com.affymetrix.genometryImpl.span.SimpleSeqSpan;
import com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry;
import com.affymetrix.genometryImpl.symmetry.SeqSymmetry;
import com.affymetrix.genometryImpl.symmetry.SimpleMutableSeqSymmetry;
import com.affymetrix.genometryImpl.symmetry.SymWithProps;
import com.affymetrix.genometryImpl.util.SeqUtils;

import com.affymetrix.genoviz.awt.AdjustableJSlider;
import com.affymetrix.genoviz.bioviews.GlyphI;
import com.affymetrix.genoviz.bioviews.Scene;
import com.affymetrix.genoviz.event.NeoMouseEvent;
import com.affymetrix.genoviz.glyph.FillRectGlyph;
import com.affymetrix.genoviz.glyph.LabelledRectGlyph;
import com.affymetrix.genoviz.glyph.LineContainerGlyph;
import com.affymetrix.genoviz.glyph.OutlineRectGlyph;
import com.affymetrix.genoviz.glyph.SequenceGlyph;
import com.affymetrix.genoviz.util.NeoConstants;
import com.affymetrix.genoviz.widget.NeoMap;
import com.affymetrix.genoviz.widget.NeoAbstractWidget;
import com.affymetrix.genoviz.widget.Shadow;
import com.affymetrix.genoviz.widget.TieredNeoMap;
import com.affymetrix.genoviz.widget.VisibleRange;
import com.affymetrix.genoviz.widget.tieredmap.ExpandedTierPacker;
import com.affymetrix.genoviz.widget.tieredmap.MapTierGlyph;

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
import java.util.*;
import java.util.Map.Entry;

/**
* This class displays the main view of transcripts, the conserved
* motifs (protein annotations) they encode, and an exon summary
* that shows how the transcript structures vary.
*/

final public class GenomeView extends JPanel implements MouseListener, ComponentListener{

  // We allow users to change the colors of transcripts, protein
  // annotations, etc
  // The default colors (see below) were chosen to accommodate
  // people with red/green color blindness and also to make it
  // possible to distinguish the frame colors when printed in
  // black and white.
    static enum COLORS
    {
        BACKGROUND("background", Color.white),
        FRAME0("frame0", new Color(0,100,145)),
        FRAME1("frame1", new Color(0,100,255)),
        FRAME2("frame2", new Color(192,192,114)),
        TRANSCRIPT("transcript", Color.black),
        DOMAIN("domain", new Color(84,168,132)),
        EXONSUMMARY("exonsummary", Color.blue),
    AMINOACID("amino_acid",Color.black);

        private final String name;
        private final Color color;

        COLORS(String nm, Color col)
        {
            this.name = nm;
            this.color = col;
        }

        @Override
        public String toString()
        {
            return name;
        }

        private Color defaultColor()
        {
            return color;
        }

        int getRGB()
        {
            return color.getRGB();
        }

        static Map<String,Color> defaultColorList()
        {
            Map<String,Color> defaults = new HashMap<String,Color>();

            for(COLORS C : values()) {
        defaults.put(C.toString(), C.defaultColor());
      }

            return defaults;
        }
    };

    JPopupMenu popup;

    private static final boolean DEBUG_GENOMIC_ANNOTS = false;
    private static final boolean DEBUG_TRANSCRIPT_ANNOTS = false;
    private static final boolean DEBUG_PROTEIN_ANNOTS = false;
  // the map that displays the transcripts and protein annotations
    private final TieredNeoMap seqmap;
  // the map that shows the sequence and axis
    private final NeoMap axismap;
    private final NeoMap[] maps;
    private final ModPropertySheet table_view;
    private final AdjustableJSlider xzoomer;
    private final AdjustableJSlider yzoomer;
    private BioSeq gseq;
    private BioSeq vseq;
    private List<GlyphI> exonGlyphs = null;
    private List<SeqSymmetry> exonList = new ArrayList<SeqSymmetry>();
    private Map<String,Color> prefs_hash;
  private boolean showhairline = true;
  private boolean showhairlineLabel = true;
  private Shadow hairline, axishairline;
  private final JSplitPane split_pane;
 
    private static Color col_bg = COLORS.BACKGROUND.defaultColor();
    private static Color col_frame0 = COLORS.FRAME0.defaultColor();
    private static Color col_frame1 = COLORS.FRAME1.defaultColor();
    private static Color col_frame2 = COLORS.FRAME2.defaultColor();
    private static Color col_ts = COLORS.TRANSCRIPT.defaultColor();
    private static Color col_domain = COLORS.DOMAIN.defaultColor();
    private static Color col_exon_summary = COLORS.EXONSUMMARY.defaultColor();
  private static Color col_amino_acid = COLORS.AMINOACID.defaultColor();
    private static Color col_sequence = Color.black;
    private static Color col_axis_bg = Color.lightGray;

    private List<GlyphI> selected = new ArrayList<GlyphI>();
    private List<GlyphI> storeSelected;
    private VisibleRange zoomPoint = new VisibleRange();

    // size constants - these are needed to control the layout
  // of different elements within each map.
    private static final int axis_pixel_height = 20;
    private static final int seq_pixel_height = 10;
    private static final int upper_white_space = 5;
    private static final int middle_white_space = 2;
    private static final int lower_white_space = 2;
    private static final int divider_size = 8;
    private static final int table_height = 100;
    private static final int seqmap_pixel_height = 500;
  private static final double zoomRatio = 30.0;

    /**
     * Removes currently loaded data by clearing maps.
     */
    void no_data() {
        seqmap.clearWidget();
        axismap.clearWidget();
        seqmap.updateWidget();
        axismap.updateWidget();
//        table_view.showProperties(new Properties[0]);
    }

    /**
     * Sets up the layout for the maps and the other elements that are
   * part of the application.
     * @param   phash   Color perferences stored in hashtable to setup the layout.
     */
    GenomeView(Map<String,Color> phash) {

        initPrefs(phash);
        popup = new JPopupMenu();
        seqmap = new TieredNeoMap(true, false);
    seqmap.enableDragScrolling(true);
        seqmap.setReshapeBehavior(NeoAbstractWidget.X, NeoAbstractWidget.FITWIDGET);
        seqmap.setReshapeBehavior(NeoAbstractWidget.Y, NeoAbstractWidget.FITWIDGET);
        seqmap.setMapOffset(0, seqmap_pixel_height);
        axismap = new NeoMap(false, false);
        axismap.setMapColor(col_axis_bg);
        axismap.setMapOffset(0, axis_pixel_height + seq_pixel_height
                + upper_white_space + middle_white_space
                + lower_white_space);
        JScrollBar y_scroller = new JScrollBar(JScrollBar.VERTICAL);
        seqmap.setOffsetScroller(y_scroller);

        xzoomer = new AdjustableJSlider(Adjustable.HORIZONTAL);
        xzoomer.setBackground(Color.white);
        yzoomer = new AdjustableJSlider(Adjustable.VERTICAL);
        yzoomer.setBackground(Color.white);

        seqmap.setZoomer(NeoMap.X, xzoomer);
        seqmap.setZoomer(NeoMap.Y, yzoomer);

        axismap.setZoomer(NeoMap.X, seqmap.getZoomer(TieredNeoMap.X));


        seqmap.getScroller(NeoMap.X).addAdjustmentListener(new AdjustmentListener() {

            public void adjustmentValueChanged(AdjustmentEvent e) {
                axismap.getScroller(NeoMap.X).setValue(seqmap.getScroller(NeoMap.X).getValue());
            }
        });

        seqmap.getZoomer(NeoMap.X).addAdjustmentListener(new AdjustmentListener() {

            public void adjustmentValueChanged(AdjustmentEvent e) {
                axismap.getScroller(NeoMap.X).setValue(seqmap.getScroller(NeoMap.X).getValue());
            }
        });

        this.setLayout(new BorderLayout());

        JPanel map_panel = new JPanel();

        map_panel.setLayout(new BorderLayout());
        map_panel.add("North", axismap);
        seqmap.setPreferredSize(new Dimension(100, seqmap_pixel_height));
        seqmap.setBackground(col_bg);
        map_panel.add("Center", seqmap);
        JPanel right = new JPanel();
        right.setLayout(new GridLayout(1, 2));
        right.add(y_scroller);
        right.add(yzoomer);
        int maps_height = axis_pixel_height + seq_pixel_height
                + upper_white_space + middle_white_space + lower_white_space
                + divider_size + seqmap_pixel_height;

        JPanel p = new JPanel();
    p.addComponentListener(this);
        p.setPreferredSize(new Dimension(seqmap.getWidth(), maps_height));
        p.setLayout(new BorderLayout());
        p.add("Center", map_panel);
        p.add("East", right);
        map_panel.add("South", xzoomer);
        table_view = new ModPropertySheet();
        table_view.setPreferredSize(new Dimension(seqmap.getWidth(), table_height));

    split_pane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,p,table_view);
    this.add("Center",split_pane);
        //this.add("Center", p);
        //this.add("South", table_view);
        seqmap.addMouseListener(this);
        seqmap.setSelectionEvent(TieredNeoMap.NO_SELECTION);

        seqmap.setSelectionAppearance(Scene.SELECT_OUTLINE);
        axismap.addMouseListener(this);
        axismap.setSelectionEvent(NeoMap.NO_SELECTION);
        axismap.setSize(seqmap.getSize().width, upper_white_space
                + middle_white_space + lower_white_space
                + axis_pixel_height + seq_pixel_height);
        maps = new NeoMap[2];
        maps[0] = seqmap;
        maps[1] = axismap;

    zoomPoint = new VisibleRange();
        hairline = new Shadow(this.seqmap);
    axishairline = new Shadow( this.axismap);
    zoomPoint.addListener(hairline);
    zoomPoint.removeListener(axishairline);

    hairline.setUseXOR(true);
    hairline.setLabeled(showhairlineLabel);
    }

    /**
     * Initialized GenomeView colors with preferences provided in the parameter phash
     * @param   phash   Map providing color preferences for GenomeView
     */
    private void initPrefs(Map<String,Color> phash) {
        tempColorPrefs(phash);
        prefs_hash = phash;
    }

    /**
     * Changes color preferences
     * @param phash     Map<String,Color>
     */
    private static void tempColorPrefs(Map<String,Color> phash)
    {
        if (phash == null) {
            return;
        }

        if (phash.containsKey(COLORS.BACKGROUND.toString())) {
            col_bg = phash.get(COLORS.BACKGROUND.toString());
        }
        if (phash.containsKey(COLORS.FRAME0.toString())) {
            col_frame0 = phash.get(COLORS.FRAME0.toString());
        }
        if (phash.containsKey(COLORS.FRAME1.toString())) {
            col_frame1 = phash.get(COLORS.FRAME1.toString());
        }
        if (phash.containsKey(COLORS.FRAME2.toString())) {
            col_frame2 = phash.get(COLORS.FRAME2.toString());
        }
        if (phash.containsKey(COLORS.TRANSCRIPT.toString())) {
            col_ts = phash.get(COLORS.TRANSCRIPT.toString());
        }
        if (phash.containsKey(COLORS.DOMAIN.toString())) {
            col_domain = phash.get(COLORS.DOMAIN.toString());
        }
        if (phash.containsKey(COLORS.EXONSUMMARY.toString())) {
            col_exon_summary = phash.get(COLORS.EXONSUMMARY.toString());
        }
    if (phash.containsKey(COLORS.AMINOACID.toString())) {
            col_amino_acid = phash.get(COLORS.AMINOACID.toString());
        }
    }

    /**
     * Add mouse listener to maps so that the application can detect
   * user interactions with the display.
     * @param   listener    Listener that is to be added to maps.
     */
    public void addMapListener(MouseListener listener) {
        seqmap.addMouseListener(listener);
        axismap.addMouseListener(listener);
    }

    /**
     * Set the data model - the BioSeq object - that the application
   * will display.
   *
     * @param   gseq the data model representing the transcripts, their
   * annotations, and their meta-data properties, such as their ids
   * in external databases.
   * @param is_new whether or not this BioSeq object has not been displayed
   * previously. This allows ProtAnnot to redraw the Glyphs using a new
   * color scheme without changing the zoom level.
     * @see     com.affymetrix.genometryImpl.BioSeq
     * @see     com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genometryImpl.util.SeqUtils
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genoviz.widget.Shadow
     * @see     com.affymetrix.genoviz.widget.tieredmap.ExpandedTierPacker
     * @see     com.affymetrix.genoviz.widget.tieredmap.MapTierGlyph
     */
    void setBioSeq(BioSeq gseq, boolean is_new) {
        this.gseq = gseq;
        seqmap.clearWidget();
        seqmap.setMapRange(gseq.getMin(), gseq.getMax());
        axismap.clearWidget();
    axismap.setMapRange(gseq.getMin(), gseq.getMax());
        seqmap.setBackground(col_bg);
        seqmap.setMaxZoom(NeoMap.Y, seqmap.getHeight()/zoomRatio);

        exonGlyphs = new ArrayList<GlyphI>();
        exonList = new ArrayList<SeqSymmetry>();

    hairline.reset(this.seqmap, NeoConstants.HORIZONTAL, Color.lightGray);
    axishairline.reset(this.axismap, NeoConstants.HORIZONTAL, Color.lightGray);
    hairline.setLabeled(showhairlineLabel);

        int acount = gseq.getAnnotationCount();

        SeqSymmetry[] path2view = new SeqSymmetry[1];
        MutableSeqSymmetry viewSym = new SimpleMutableSeqSymmetry();
        viewSym.addSpan(new SimpleSeqSpan(gseq.getMin(), gseq.getMax(), gseq));
        vseq = new BioSeq("view seq", null, gseq.getLength());
        vseq.setBounds(gseq.getMin(), gseq.getMax());

        viewSym.addSpan(new SimpleSeqSpan(vseq.getMin(), vseq.getMax(), vseq));

        path2view[0] = viewSym;

        for (int i = 0; i < acount; i++) {
            SeqSymmetry asym = gseq.getAnnotation(i);
            if (DEBUG_GENOMIC_ANNOTS) {
                SeqUtils.printSymmetry(asym);
            }
            glyphifyMRNA(asym, path2view);
        }

        MapTierGlyph sumTier = new MapTierGlyph();
        sumTier.setCoords(gseq.getMin(), seqmap_pixel_height - 20, gseq.getLength(), 20);
        sumTier.setState(MapTierGlyph.EXPANDED);

        ExpandedTierPacker epack = (ExpandedTierPacker) sumTier.getExpandedPacker();
        epack.setMoveType(ExpandedTierPacker.DOWN);
        GlyphSummarizer summer = new GlyphSummarizer(col_exon_summary);
        if (exonGlyphs.size() > 0) {
            GlyphI gl = summer.getSummaryGlyph(exonGlyphs);
            sumTier.addChild(gl);
        }
        seqmap.addTier(sumTier);
        seqmap.repack();

//        table_view.showProperties(new Properties[0]);
        setupAxisMap();

    axismap.stretchToFit(is_new, false);
    seqmap.stretchToFit(is_new, true);

    seqmap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD,
                ((gseq.getMin()+gseq.getMax())/2.0));
        axismap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD,
                ((gseq.getMin()+gseq.getMax())/2.0));

        seqmap.updateWidget();
        axismap.updateWidget();
    }
 
  /**
   * Toggle hairline on/off.
   * @return
   */
  public boolean toggleHairline() {
    showhairline = !showhairline;

    hairline.setShowHairline(showhairline);
    axishairline.setShowHairline(showhairline);

    updateWidget();

    return showhairline;
  }

  /**
   * Toggle hairline label on/off.
   * @return
   */
  public boolean toggleHairlineLabel() {
    showhairlineLabel = !showhairlineLabel;

    hairline.setLabeled(showhairlineLabel);

    updateWidget();

    return showhairlineLabel;
  }

  public void stretchToFit(boolean x, boolean y){
    seqmap.stretchToFit(x, y);
    axismap.stretchToFit(x, y);
  }
 
  public void updateWidget(){
    seqmap.updateWidget();
    axismap.updateWidget();
  }
    /**
     * Sets the title of the frame provided by the parameter title.
     * @param   title  Title of the frame. Usually the name of the file on display.
     */
    void setTitle(String title) {
        table_view.setTitle(title);
    }

    /**
     * Make a Glyph to represent the given mRNA object.
     * @param   mrna2genome  a data model representing an mRNA, a set of exons
     * @param   path2view  "view seq" symmetry enclosed in an array
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genometryImpl.BioSeq
     * @see     com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry
     * @see     com.affymetrix.genometryImpl.SeqSpan
     * @see     com.affymetrix.genometryImpl.util.SeqUtils
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genoviz.widget.tieredmap.MapTierGlyph
     */
    private void glyphifyMRNA(SeqSymmetry mrna2genome, SeqSymmetry[] path2view) {
        int childcount = mrna2genome.getChildCount();
        MapTierGlyph tier = new MapTierGlyph();
        tier.setCoords(gseq.getMin(), 0, gseq.getLength(), 80);
        tier.setState(MapTierGlyph.EXPANDED);
        ExpandedTierPacker epack = (ExpandedTierPacker) tier.getExpandedPacker();
        epack.setMoveType(ExpandedTierPacker.DOWN);
        seqmap.addTier(tier);

        MutableSeqSymmetry annot2genome = new SimpleMutableSeqSymmetry();
        copyToMutable(mrna2genome, annot2genome);

        SeqUtils.transformSymmetry(annot2genome, path2view);
    GlyphI tGlyph = glyphifyExons(mrna2genome, annot2genome, childcount);
        tier.addChild(tGlyph);
    BioSeq mrna = SeqUtils.getOtherSpan(mrna2genome, mrna2genome.getSpan(gseq)).getBioSeq();
    displayAssociatedmRNAforTranscript(mrna, path2view, mrna2genome, tier, tGlyph);
    }

  /**
   * Create glyphs for exons.
   * @param mrna2genome
   * @param annot2genome
   * @param childcount - exon count
   * @return
   */
  private GlyphI glyphifyExons(
      SeqSymmetry mrna2genome, MutableSeqSymmetry annot2genome, int childcount) {
    GlyphI tGlyph = new LineContainerGlyph();
    seqmap.setDataModel(tGlyph, mrna2genome);
    SeqSpan tSpan = annot2genome.getSpan(vseq);
    tGlyph.setCoords(tSpan.getMin(), 0, tSpan.getLength(), 20);
    tGlyph.setColor(col_ts);
    for (int i = 0; i < childcount; i++) {
      SeqSymmetry exon2genome = annot2genome.getChild(i);
      SeqSpan gSpan = exon2genome.getSpan(vseq);
      GlyphI cglyph = new OutlineRectGlyph();
      seqmap.setDataModel(cglyph, exon2genome);
      // can't give this a type and therefore signal
      // to the selection logic that this is first class selectable
      // object
      // so let's put it in a list
      exonList.add(exon2genome);
      cglyph.setColor(col_ts);
      cglyph.setCoords(gSpan.getMin(), 0, gSpan.getLength(), 20);
      exonGlyphs.add(cglyph);
      tGlyph.addChild(cglyph);
      //  testing display of "exon segments" for transcripts that have
      //     base inserts relative to the genomic sequence
      //  haven't dealt with display of base deletions in transcript relative to genomic yet
      //  if exon is segmented by inserts, then it will have children
      //     that specify this segmentation
      for (int seg_index = 0; seg_index < exon2genome.getChildCount(); seg_index++) {
        SeqSymmetry eseg2genome = exon2genome.getChild(seg_index);
        SeqSpan seg_gspan = eseg2genome.getSpan(vseq);
        if (seg_gspan.getLength() == 0) {
          // only mark the inserts (those whose genomic extent is zero
          GlyphI segGlyph = new OutlineRectGlyph();
          segGlyph.setColor(col_bg);
          segGlyph.setCoords(seg_gspan.getMin(), 0, seg_gspan.getLength(), 25);
          tGlyph.addChild(segGlyph);
        }
      }
    }
    return tGlyph;
  }


  // now follow symmetry link to annotated mrna seqs, map those annotations to genomic
  //    coords, and display
  private void displayAssociatedmRNAforTranscript(
      BioSeq mrna, SeqSymmetry[] path2view, SeqSymmetry mrna2genome, MapTierGlyph tier, GlyphI tGlyph) {
    if (mrna != null) {
      if (DEBUG_TRANSCRIPT_ANNOTS) {
        System.out.println(mrna.getID() + ",  " + mrna);
      }
      SeqSymmetry[] new_path2view = new SeqSymmetry[path2view.length + 1];
      System.arraycopy(path2view, 0, new_path2view, 1, path2view.length);
      new_path2view[0] = mrna2genome;
      int acount = mrna.getAnnotationCount();
      for (int i = 0; i < acount; i++) {
        SeqSymmetry annot2mrna = mrna.getAnnotation(i);
        if (annot2mrna != mrna2genome) {
          glyphifyTranscriptAnnots(mrna, annot2mrna, new_path2view, tier, tGlyph);
        }
      }
    }
  }


  // now follow symmetry link to annotated mrna seqs, map those annotations to genomic
  //    coords, and display
  // consider merging with displayAssociatedmRNAforTranscript
  private void displayAssociatedmRNAforProtein(
      BioSeq protein, SeqSymmetry[] path2view, SeqSymmetry annot2mrna, MapTierGlyph tier) {
    if (protein != null) {
      if (DEBUG_PROTEIN_ANNOTS) {
        System.out.println(protein.getID() + ",  " + protein);
      }
      //   new path info is added to _beginning_ of path
      SeqSymmetry[] new_path2view = new SeqSymmetry[path2view.length + 1];
      System.arraycopy(path2view, 0, new_path2view, 1, path2view.length);
      new_path2view[0] = annot2mrna;
      int acount = protein.getAnnotationCount();
      for (int i = 0; i < acount; i++) {
        SeqSymmetry annot2protein = protein.getAnnotation(i);
        if (annot2protein != annot2mrna) {
          glyphifyProteinAnnots(annot2protein, new_path2view, tier);
        }
      }
    }
  }


    /**
     * Create glyphs that represent each transcript.
     * @param   mrna
     * @param   annot2mrna
     * @param   path2view - "view seq" symmetry enclosed in an array appended with mrna2genome
     * @param   tier - tier where glyphs will be added
     * @param   trans_parent - parent glyph
     * @see     com.affymetrix.genometryImpl.BioSeq
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genoviz.widget.tieredmap.MapTierGlyph
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry
     * @see     com.affymetrix.genometryImpl.SeqSpan
     * @see     com.affymetrix.genometryImpl.util.SeqUtils
     */
    private void glyphifyTranscriptAnnots(BioSeq mrna,
            SeqSymmetry annot2mrna, SeqSymmetry[] path2view,
            MapTierGlyph tier, GlyphI trans_parent) {
        if (DEBUG_TRANSCRIPT_ANNOTS) {
            SeqUtils.printSymmetry(annot2mrna);
        }
        SeqSpan mrna_span = annot2mrna.getSpan(mrna);
        MutableSeqSymmetry annot2genome = new SimpleMutableSeqSymmetry();
        copyToMutable(annot2mrna, annot2genome);

        SeqUtils.transformSymmetry(annot2genome, path2view);
        if (DEBUG_TRANSCRIPT_ANNOTS) {
            SeqUtils.printSymmetry(annot2genome);
        }

        SeqSpan pSpan = SeqUtils.getOtherSpan(annot2mrna, mrna_span);
        BioSeq protein = pSpan.getBioSeq();
    String amino_acid = null;

    try{
      amino_acid = protein.getResidues(0, protein.getLength());
    }catch(Exception ex){
      System.out.println("*** Warning: No amino acid found ");
    }

        GlyphI aGlyph = new LineContainerGlyph();
        SeqSpan aSpan = annot2genome.getSpan(vseq);
        aGlyph.setCoords(aSpan.getMin(), 0, aSpan.getLength(), 20);
        aGlyph.setColor(col_ts);
        seqmap.setDataModel(aGlyph, annot2mrna);
    glyphifyCDSs(annot2genome, protein, aGlyph, amino_acid, vseq);
        trans_parent.addChild(aGlyph);
    displayAssociatedmRNAforProtein(protein, path2view, annot2mrna, tier);
    }


  /**
   * Add glyphs for CDS regions.
   * @param annot2genome
   * @param protein
   * @param aGlyph parent glyph
   * @param amino_acid String representing amino acids; visible when zoomed in
   * @param vseq
   */
  private static void glyphifyCDSs(
      MutableSeqSymmetry annot2genome, BioSeq protein, GlyphI aGlyph, String amino_acid, BioSeq vseq) {
    int cdsCount = annot2genome.getChildCount();
    int prev_amino_end = 0;
    int prev_add = 0;
    for (int j = 0; j < cdsCount; j++) {
      SeqSymmetry cds2genome = annot2genome.getChild(j);
      SeqSpan gSpan = cds2genome.getSpan(vseq);
      GlyphI cglyph = new FillRectGlyph();
      //SeqSpan protSpan = cds2genome.getSpan(protein);
      // coloring based on frame
      colorByFrame(cglyph, gSpan.getMin() + prev_add);
      prev_add += 3 - (gSpan.getLength() % 3)//Keep a track of no of complete amino acid added.
      cglyph.setCoords(gSpan.getMin(), 0, gSpan.getLength(), 20);
      aGlyph.addChild(cglyph);
      if (amino_acid != null) {
        SequenceGlyph sg = new ColoredResiduesGlyph(false);
        int start = prev_amino_end;
        int end = start + gSpan.getLength();
        String sub_amino_acid = amino_acid.substring(start, end);
        prev_amino_end += gSpan.getLength();
        sg.setResidues(sub_amino_acid);
        sg.setCoords(gSpan.getMin(), 0, gSpan.getLength(), 20);
        sg.setForegroundColor(col_amino_acid);
        sg.setBackgroundColor(cglyph.getBackgroundColor());
        aGlyph.addChild(sg);
      }
    }
  }

  /**
   * Colors by exon frame relative to genomic coordinates
   * @param gl
   * @param genome_codon_start  First position of complete amino acid.
   */
   private static void colorByFrame(GlyphI gl, int genome_codon_start) {

        genome_codon_start %= 3;
        if (genome_codon_start == 0) {
            gl.setColor(col_frame0);
        } else if (genome_codon_start == 1) {
            gl.setColor(col_frame1);
        } else {
            gl.setColor(col_frame2);
        // genome_codon_start = 2
    }


    /**
     * Colors by exon frame relative to genomic coordinates
     * @param   gl
     * @param   protSpan  represents a protein annotation, an annotation on the
   * transcript's translated sequence
     * @param   genSpan
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genometryImpl.SeqSpan
     */
    private static void colorByFrame(GlyphI gl, SeqSpan protSpan, SeqSpan genSpan) {
        double pstart = protSpan.getStartDouble();
        double fraction = Math.abs(pstart - (int) pstart);
        int genome_codon_start = genSpan.getStart();
        int exon_codon_start;
        if (fraction < 0.3) {
            exon_codon_start = 0;
        } else if (fraction < 0.6) {
            exon_codon_start = 2;
        } else {
            exon_codon_start = 1;
        }
        genome_codon_start += exon_codon_start;
        genome_codon_start %= 3;
        if (genome_codon_start == 0) {
            gl.setColor(col_frame0);
        } else if (genome_codon_start == 1) {
            gl.setColor(col_frame1);
        } else {
            gl.setColor(col_frame2);
        // genome_codon_start = 2
    }


    /**
     *
     * @param   annot2protein
     * @param   path2view - "view seq" symmetry enclosed in an array appended with annot2mrna
     * @param   tier
     * @see     com.affymetrix.genometryImpl.BioSeq
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genoviz.widget.tieredmap.MapTierGlyph
     * @see     com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry
     * @see     com.affymetrix.genometryImpl.SeqSpan
     * @see     com.affymetrix.genometryImpl.symmetry.SymWithProps
     * @see     com.affymetrix.genometryImpl.util.SeqUtils
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     */
    private void glyphifyProteinAnnots(
            SeqSymmetry annot2protein,
            SeqSymmetry[] path2view,
            MapTierGlyph tier) {

        MutableSeqSymmetry annot2genome = new SimpleMutableSeqSymmetry();
        copyToMutable(annot2protein, annot2genome);

        if (DEBUG_PROTEIN_ANNOTS) {
            SeqUtils.printSymmetry(annot2genome);
        }
        SeqUtils.transformSymmetry(annot2genome, path2view);
        if (DEBUG_PROTEIN_ANNOTS) {
            SeqUtils.printSymmetry(annot2genome);
        }

        GlyphI aGlyph = new LineContainerGlyph();
        seqmap.setDataModel(aGlyph, annot2protein);

        SeqSpan aSpan = annot2genome.getSpan(vseq);
        aGlyph.setCoords(aSpan.getMin(), 0, aSpan.getLength(), 20);
        // will return a color from the prefs for the protein annotation
        // span -- or else the default - col_domain
        Color color = pick_color_for_domain(aSpan, prefs_hash);
        aGlyph.setColor(color);

        // for now, need to descend two levels because that is depth of path --
        //    eventually will use some sort of flattening method (probably
        //    first set up as part of SeqUtils)
        int count1 = annot2genome.getChildCount();
        for (int i = 0; i < count1; i++) {

            SeqSymmetry child = annot2genome.getChild(i);
            int count2 = child.getChildCount();

            // reach "back" and get actual symmetry (rather than transformed symmetry)
            //   really need some sort of tracking in transform mechanism to associate calculated
            //   symmetries with original symmetries that they map back to...
            SymWithProps original_child = (SymWithProps) annot2protein.getChild(i);

            for (int j = 0; j < count2; j++) {
                SeqSymmetry grandchild = child.getChild(j);
                SeqSpan gSpan = grandchild.getSpan(vseq);
                LabelledRectGlyph cglyph = new LabelledRectGlyph();
        if(i%2 == 0) {
          cglyph.setColor(color);
        }
        else {
          cglyph.setColor(color.darker());
        }

        String spanno = "Span " + String.valueOf(i+1) + " of ";
        String interpro = (String) ((SymWithProps)annot2protein).getProperty("InterPro Name");
        if(interpro != null){
          spanno += interpro;
        }
        cglyph.setText(spanno);
                cglyph.setCoords(gSpan.getMin(), 0, gSpan.getLength(), 20);
                aGlyph.addChild(cglyph);
                seqmap.setDataModel(cglyph, original_child);
                original_child.setProperty("type", "protspan");
            }
        }
        tier.addChild(aGlyph);
    }

    /**
     * Returns color for given propertied object
     * @param   propertied
     * @return  Color
     * @see     com.affymetrix.genometryImpl.symmetry.SymWithProps
     */
    private static Color pick_color_for_domain(Object propertied, Map<String,Color> prefs_hash) {
        Color to_return = col_domain;
        if (propertied instanceof SymWithProps) {
            Object property = ((SymWithProps) propertied).getProperty("method");
            if (property != null) {
                to_return = prefs_hash.get((String)property);
            }
        }
        return to_return;
    }

    /**
     * Sets the axis map. Sets range,background and foreground color.
     * @see     com.affymetrix.genoviz.glyph.SequenceGlyph
     */
    private void setupAxisMap() {
        /* Implementing it in this way because in above method synchronization is lost when
         zoomtoselected feature is used. So to correct it below used method is used */

        axismap.addAxis(upper_white_space+axis_pixel_height);
        //String residues = gseq.getResidues();
        ColoredResiduesGlyph sg = new ColoredResiduesGlyph(true);
        sg.setResiduesProvider(gseq, gseq.getLength());
        sg.setCoords(gseq.getMin(), upper_white_space + axis_pixel_height
                + middle_white_space, gseq.getLength() , seq_pixel_height);
        sg.setForegroundColor(col_sequence);
        sg.setBackgroundColor(col_axis_bg);
        axismap.getScene().addGlyph(sg);
    }

  public void componentResized(ComponentEvent e) {
    split_pane.repaint();
    stretchToFit(false,true);
  }

  public void componentMoved(ComponentEvent e) {}

  public void componentShown(ComponentEvent e) {}

  public void componentHidden(ComponentEvent e) {}

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseReleased(MouseEvent e) {
    if (e.isPopupTrigger()) {
      popup.show(this, e.getX(), e.getY());
      return;
    }

    if(e.getClickCount() == 2){
      zoomToSelection();
    }
  }

    /**
     * Sets zoom focus and selects any Glyph underlying the location of the
   * click.
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genoviz.event.NeoMouseEvent
     * @see     com.affymetrix.genoviz.widget.NeoMap
     */
    public void mousePressed(MouseEvent e) {

        if (!(e instanceof NeoMouseEvent)) {
            return;
        }
        NeoMouseEvent nme = (NeoMouseEvent) e;
        Object coord_source = nme.getSource();

        seqmap.setZoomBehavior(NeoMap.Y, NeoMap.CONSTRAIN_COORD,
                nme.getCoordY());
        seqmap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD,
                nme.getCoordX());
        axismap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD,
                nme.getCoordX());
        // if alt is down or shift is down, add to previous selection,
        //    otherwise replace previous selection
        boolean multiselect = false;
        if (nme.isAltDown() || nme.isShiftDown()) {
            multiselect = true;
        }

        if (coord_source == axismap) {
            if (!multiselect) {
                seqmap.clearSelected();
            }
        } else
        {

            List<GlyphI> hitGlyphs = seqmap.getItems(nme.getCoordX(),
                    nme.getCoordY());
            if (!multiselect) {
                seqmap.clearSelected();
            }
            selected = seqmap.getSelected();
            List<GlyphI> to_select = getGlyphsToSelect(hitGlyphs, selected,
                    multiselect);
            if (to_select == null) {
                selected = new ArrayList<GlyphI>();
            } else {
                if (to_select.size() > 0) {
                    seqmap.select(to_select);
                    selected = to_select;
                }
            }
        }
    zoomPoint.setSpot(seqmap.getZoomCoord(NeoMap.X));

        axismap.updateWidget();
        seqmap.updateWidget();

        showProperties();

    if(hairline != null) {
      hairline.setRange((int)nme.getCoordX(), (int)nme.getCoordX() + 1);
    }

        if (e.isPopupTrigger()) {
            popup.show(this, e.getX(), e.getY());
        }
    }

  public JSplitPane getSplitPane(){
    return split_pane;
  }
 
  public JPanel getTablePanel(){
    return table_view;
  }

    /**
     * Shows properties (meta-data about selected items) in the property table
   * at the bottom of the display.
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genometryImpl.symmetry.SymWithProps
     * @see     com.affymetrix.genometryImpl.symmetry.SimpleMutableSeqSymmetry
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     */
    private void showProperties() {
        Set<Properties> propvec = new HashSet<Properties>();
        Properties props = null;
        for (GlyphI gl : selected) {
            SymWithProps info = null;
            Object candidate = gl.getInfo();
            if (candidate instanceof SymWithProps) {
                info = (SymWithProps) candidate;
                candidate = gl.getParent().getInfo();
        addParentInfo(candidate, info);
            } else {
                if (candidate instanceof SimpleMutableSeqSymmetry && exonList.contains((SeqSymmetry) candidate)) {
                    props = new Properties();
                    props.setProperty("start", String.valueOf((int) gl.getCoordBox().x));
                    props.setProperty("end", String.valueOf((int) (gl.getCoordBox().x + gl.getCoordBox().width)));
                    props.setProperty("length", String.valueOf((int) gl.getCoordBox().width));
                    props.setProperty("type", "exon");
                    candidate = gl.getParent().getInfo();
                    if (candidate instanceof SymWithProps) {
                        info = (SymWithProps) candidate;
                        for(Entry<Object,Object> E: props.entrySet()) {
              info.setProperty( (String) E.getKey(),E.getValue());
            }
                    }
                }
            }
            if (info != null) {
                if (info.getProperties() != null) {
                    propvec.add(convertPropsToProperties(info.getProperties()));
                }
            }
        }
    Properties[] prop_array = propvec.toArray(new Properties[propvec.size()]);
        table_view.showProperties(prop_array);
    }

  private void addParentInfo(Object candidate, SymWithProps info) {
    if (candidate instanceof SymWithProps) {
      SymWithProps parent = (SymWithProps) candidate;
      for (Entry<String, Object> E : parent.getProperties().entrySet()) {
        if (!Xml2GenometryParser.TYPESTR.equals(E.getKey()) &&
            !Xml2GenometryParser.AA_START.equals(E.getKey()) &&
            !Xml2GenometryParser.AA_END.equals(E.getKey()) &&
            !Xml2GenometryParser.AA_LENGTH.equals(E.getKey()) ) {
          info.setProperty(E.getKey(), E.getValue());
        }
      }
    }
  }

    /**
     * Converts Map to Properties to for display in property table.
     * @param   prop  a Map containing meta-data name/value pairs for an item
     * @return  Properties the meta-data values transformed to Property objects
     */
    private static Properties convertPropsToProperties(Map<String, Object> prop) {
        Properties retval = new Properties();
        for (Entry<String, Object> ent : prop.entrySet()) {
            retval.put(ent.getKey(), ent.getValue());
        }
        return retval;
    }

    /**
     * Decides which glyphs to choose when mouse is clicked.
     * @param   clicked_glyphs
     * @param   prev_glyphs
     * @param   multiselect
     * @return  List<GlyphI>
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     */
    private List<GlyphI> getGlyphsToSelect(List<GlyphI> clicked_glyphs,
            List<GlyphI> prev_glyphs,
            boolean multiselect) {
        List<GlyphI> candidates = new ArrayList<GlyphI>();

        if (multiselect) {
            filterGlyphs(candidates, prev_glyphs);
        }
    filterGlyphs(candidates, clicked_glyphs);

        List<GlyphI> to_return = new ArrayList<GlyphI>();
        GlyphI champion = null;
        Rectangle2D candidate_box;
        double champion_end = 0;
        double champion_start = 0;
        double candidate_end = 0;
        double candidate_start = 0;
        for (GlyphI candidate : candidates) {
            // we want everything
            if (multiselect) {
                to_return.add(candidate);
            } // we just want the topmost GlyphI
            // to figure out what Glyph is on top we have to think about geometry
            else {
                candidate_box = candidate.getCoordBox();
                candidate_start = candidate_box.getX();
                candidate_end = candidate_box.getX() + candidate_box.getWidth();
                // note: if champion is null, we're on the first Glyph - so let the
                // candidate be the champion for now
                if (champion == null
                        || (candidate_end < champion_end && candidate_start >= champion_start)
                        || (candidate_end <= champion_end && candidate_start > champion_start)
                        || // we leave the most computationally intensive test for last
                        (champion.getChildren() != null && champion.getChildren().contains(candidate))) {
                    champion = candidate;
                    champion_start = candidate_start;
                    champion_end = candidate_end;
                }
            }
        }
        if (champion != null) {
            to_return.add(champion);
        }
        return to_return;
    }

    /**
     * Filters out glyphs if no information is present or if it is not a instance of SymWithProps
     * @param   gList
     * @param   glyphs
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     * @see     com.affymetrix.genometryImpl.symmetry.SymWithProps
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     */
    private void filterGlyphs(List<GlyphI> gList, List<GlyphI> glyphs) {
        for (GlyphI g : glyphs) {
            Object info = g.getInfo();
            if (info != null) {
                if ((info instanceof SymWithProps
                        && ((SymWithProps) info).getProperty("type") != null)
                        || exonList.contains((SeqSymmetry)info)) {
                    gList.add(g);
                }
            }
        }
    }

    /**
     * Returns selected Glyphs
     * @return  Returns list of selected Glyphs
     * @see     com.affymetrix.genoviz.bioviews.GlyphI
     */
    public List<GlyphI> getSelected() {
        return selected;
    }

    /**
     * Return the Properties for current selection.
     */
    public Properties[] getProperties() {
        return table_view.getProperties();
    }

    /**
     * Makes everything visible by zooming out completely.
     */
    void unzoom() {
        seqmap.stretchToFit(true, true);
        seqmap.updateWidget();
        axismap.stretchToFit(true, true);
        axismap.updateWidget();
    }

    /**
     * Zoom to the selected glyphs.
     */
    public void zoomToSelection() {
        List<GlyphI> selections = getSelected();
        if (selections.isEmpty()) {
            return;
        }

        double min_x = -1f;
        double max_x = -1f;

        for (GlyphI glyph : selections) {
            Rectangle2D glyphbox = glyph.getCoordBox();
            if (min_x == -1 || min_x > glyphbox.getX()) {
                min_x = glyphbox.getX();
            }
            if (max_x == -1 || max_x < glyphbox.getX() + glyphbox.getWidth()) {
                max_x = glyphbox.getX() + glyphbox.getWidth();
            }
        }

        double zoom_focus = (min_x + max_x) / 2f;
        double width = max_x - min_x;
        double pixels_per_coord =
                Math.min(seqmap.getView().getPixelBox().width / (width * 1.1f),
                seqmap.getMaxZoom(NeoAbstractWidget.X));


        if (pixels_per_coord < seqmap.getMinZoom(NeoAbstractWidget.X)) {
            unzoom();
            seqmap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD, zoom_focus);
            axismap.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD, zoom_focus);
            return;
        }
        for (NeoMap map : maps) {
            map.zoom(NeoAbstractWidget.X, pixels_per_coord);
            double screen_width = map.getVisibleRange()[1] - map.getVisibleRange()[0];
            double scroll_to = min_x + (width * 0.5) - (screen_width * 0.5);
            map.scroll(NeoAbstractWidget.X, scroll_to);
            map.setZoomBehavior(NeoMap.X, NeoMap.CONSTRAIN_COORD, zoom_focus);
            map.updateWidget(true);
        }

    seqmap.adjustScroller(NeoAbstractWidget.X);
    seqmap.adjustZoomer(NeoAbstractWidget.X);
    }

    /**
     * Copies a SeqSymmetry.
     * Note that this clears all previous data from the MutableSeqSymmetry.
     * @param   sym Source parameter to copy from.
     * @param   mut Target parameter to copy to.
     * @see     com.affymetrix.genometryImpl.symmetry.SeqSymmetry
     * @see     com.affymetrix.genometryImpl.symmetry.MutableSeqSymmetry
     * @see     com.affymetrix.genometryImpl.SeqSpan
     * @see     com.affymetrix.genometryImpl.span.SimpleMutableSeqSpan
     */
    private static void copyToMutable(SeqSymmetry sym, MutableSeqSymmetry mut) {
        mut.clear();
        int spanCount = sym.getSpanCount();
        for (int i = 0; i < spanCount; i++) {
            SeqSpan span = sym.getSpan(i);
            SeqSpan newspan = new SimpleMutableSeqSpan(span);
            mut.addSpan(newspan);
        }
        int childCount = sym.getChildCount();
        for (int i = 0; i < childCount; i++) {
            SeqSymmetry child = sym.getChild(i);
            MutableSeqSymmetry newchild = new SimpleMutableSeqSymmetry();
            copyToMutable(child, newchild);
            mut.addChild(newchild);
        }
    }

  /**
    * Store old values.
    */
    private void storeCurrentSelection() {
        storeSelected = getSelected();
    }

    /**
     * Restores old values.
     */
    private void restorePreviousSelection() {

        seqmap.select(storeSelected);
        selected = storeSelected;

        zoomPoint.setSpot(seqmap.getZoomCoord(NeoMap.X));
        axismap.updateWidget();
        seqmap.updateWidget();
        showProperties();

    }

    /**
     * Action to be performed when user saves color changes.
     * @param   colorhash   Map<String,Color> new color preferences
     */
    void changePreference(Map<String,Color> colorhash)
    {
        tempChangePreference(colorhash);
        initPrefs(colorhash);
    }

    /**
     * Action to be performed when user attempts to apply color changes.
     * @param   colorhash   Map<String,Color> new color preferences
     */
    void tempChangePreference(Map<String,Color> colorhash)
    {
        tempColorPrefs(colorhash);
        if(gseq != null)
        {
            storeCurrentSelection();
            setBioSeq(gseq,false);
            restorePreviousSelection();
        }
    }

    /**
     * Action to be performed when user cancel color changes. So revert back to old color preferences.
     *
     */
    void cancelChangePrefernce()
    {
        tempColorPrefs(prefs_hash);
        if(gseq != null)
        {
            storeCurrentSelection();
            setBioSeq(gseq,false);
            restorePreviousSelection();
        }
    }

    /**
     * Returns color preferences.
     * @return  Map<String,Color>     Returns color preferences.
     */
    Map<String,Color> getColorPrefs()
    {
        return prefs_hash;
    }
}

TOP

Related Classes of org.bioviz.protannot.GenomeView

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.