Skip to content

Commit

Permalink
Ultima develop code merge (#1423)
Browse files Browse the repository at this point in the history
Ultima IGV Extensions  -- graphical and text indications of indel quality from Ultima flow sequencing homopolymer data.

---------

Co-authored-by: Ilya Soifer <[email protected]>
  • Loading branch information
dror27 and ilyasoifer committed Dec 11, 2023
1 parent ab6db62 commit 787c5cf
Show file tree
Hide file tree
Showing 15 changed files with 4,577 additions and 49 deletions.
34 changes: 0 additions & 34 deletions .github/workflows/gradle_test.yml

This file was deleted.

1 change: 1 addition & 0 deletions src/main/java/org/broad/igv/prefs/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ private Constants() {
public static final String SAM_ALIGNMENT_SCORE_THRESHOLD = "SAM.ALIGNMENT_SCORE_THRESHOLD";
public static final String SAM_COMPUTE_ISIZES = "SAM.COMPUTE_ISIZES";
public static final String SAM_MAX_INSERT_SIZE_THRESHOLD = "SAM.INSERT_SIZE_THRESHOLD";
public static final String SAM_INSERT_QUAL_COLORING = "SAM.INSERT_QUAL_COLORING";
public static final String SAM_MIN_INSERT_SIZE_THRESHOLD = "SAM.MIN_INSERT_SIZE_THRESHOLD";
public static final String SAM_MAX_INSERT_SIZE_PERCENTILE = "SAM.ISIZE_MAX_PERCENTILE";
public static final String SAM_MIN_INSERT_SIZE_PERCENTILE = "SAM.MIN_ISIZE_MIN_PERCENTILE";
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/broad/igv/sam/AlignmentBlock.java
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,5 @@ default int getPadding() {

char getCigarOperator();

default int getIndexOnRead() { return 0; }
}
7 changes: 7 additions & 0 deletions src/main/java/org/broad/igv/sam/AlignmentBlockImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ public class AlignmentBlockImpl implements AlignmentBlock {
private int pixelEnd;
private int padding = 0;
private char cigarOperator;
private int indexOnRead;

public AlignmentBlockImpl(int start, byte[] bases, byte[] qualities) {
this(start, bases, qualities, 0, bases.length, (char) 0);
Expand All @@ -57,6 +58,8 @@ public AlignmentBlockImpl(int start, byte[] bases, byte[] qualities, int offset,
// qualities are optional in a SAMRecord, we might get null or an array of zero
this.qualities = qualities == null || qualities.length == 0 ? EMPTY_ARRAY : new ByteSubarray(qualities, offset, nBases, (byte) 126);
this.cigarOperator = cigarOperator;

this.indexOnRead = offset;
}

@Override
Expand Down Expand Up @@ -177,4 +180,8 @@ public boolean hasBases() {
public void setPadding(int padding) {
this.padding = padding;
}

public int getIndexOnRead() {
return this.indexOnRead;
}
}
143 changes: 132 additions & 11 deletions src/main/java/org/broad/igv/sam/AlignmentRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.broad.igv.prefs.IGVPreferences;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.renderer.GraphicUtils;
import org.broad.igv.renderer.SequenceRenderer;
import org.broad.igv.sam.AlignmentTrack.ColorOption;
import org.broad.igv.sam.BisulfiteBaseInfo.DisplayStatus;
import org.broad.igv.sam.mods.BaseModificationRenderer;
Expand All @@ -48,6 +49,8 @@
import org.broad.igv.ui.color.GreyscaleColorTable;
import org.broad.igv.ui.color.HSLColorTable;
import org.broad.igv.ui.color.PaletteColorTable;
import org.broad.igv.ultima.render.ColorByTagValueList;
import org.broad.igv.ultima.render.FlowIndelRendering;
import org.broad.igv.util.ChromosomeColors;

import java.awt.*;
Expand Down Expand Up @@ -101,9 +104,9 @@ public class AlignmentRenderer {

// Indel colors
public static Color purple = new Color(118, 24, 220);
private static final Color deletionColor = Color.black;
private static final Color skippedColor = new Color(150, 184, 200);
private static final Color unknownGapColor = new Color(0, 150, 0);
public static Color deletionColor = Color.black;
private static Color skippedColor = new Color(150, 184, 200);
private static Color unknownGapColor = new Color(0, 150, 0);

// Bisulfite colors
private static final Color bisulfiteColorFw1 = new Color(195, 195, 195);
Expand Down Expand Up @@ -132,6 +135,9 @@ public class AlignmentRenderer {
private static ColorTable defaultTagColors;
public static HashMap<Character, Color> nucleotideColors;

final private static ColorByTagValueList colorByTagValueList = new ColorByTagValueList();
final private static FlowIndelRendering flowIndelRendering = new FlowIndelRendering();

private static void initializeTagTypes() {
// pre-seed from orientation colors

Expand Down Expand Up @@ -654,9 +660,16 @@ private void drawAlignment(
y,
h,
gapPxEnd - gapPxStart - 2,
context.translateX,
null,
alignment,
context);
}

// gap extensions
if ( flowIndelRendering.handlesAlignment(alignment) ) {
flowIndelRendering.renderDeletionGap(alignment, gap, y, h, gapPxStart, gapPxEnd - gapPxStart, context, renderOptions);
}
}
}

Expand Down Expand Up @@ -904,15 +917,21 @@ private void drawAlignment(
y,
h,
(int) pxWidthExact,
context.translateX,
aBlock,
context );
alignment,
context);
} else {
int pxWing = (h > 10 ? 2 : (h > 5) ? 1 : 0);
Graphics2D ig = context.getGraphics();
ig.setColor(purple);
ig.fillRect(x, y, 2, h);
ig.fillRect(x - pxWing, y, 2 + 2 * pxWing, 2);
ig.fillRect(x - pxWing, y + h - 2, 2 + 2 * pxWing, 2);
if ( flowIndelRendering.handlesAlignment(alignment) ) {
flowIndelRendering.renderSmallInsertion(alignment, aBlock, context, h, x, y, renderOptions);
} else {
ig.fillRect(x, y, 2, h);
ig.fillRect(x - pxWing, y, 2 + 2 * pxWing, 2);
ig.fillRect(x - pxWing, y + h - 2, 2 + 2 * pxWing, 2);
}

aBlock.setPixelRange(context.translateX + x - pxWing, context.translateX + x + 2 + pxWing);
}
Expand Down Expand Up @@ -1018,7 +1037,7 @@ private static void drawClippedEnds(final Graphics2D g, final int[] xPoly, final
}

private void drawLargeIndelLabel(Graphics2D g, boolean isInsertion, String labelText, int pxCenter,
int pxTop, int pxH, int pxWmax, AlignmentBlock insertionBlock, RenderContext context) {
int pxTop, int pxH, int pxWmax, int translateX, AlignmentBlock insertionBlock, Alignment alignment, RenderContext context) {

final int pxPad = 2; // text padding in the label
final int pxWing = (pxH > 10 ? 2 : 1); // width of the cursor "wing"
Expand All @@ -1043,8 +1062,12 @@ private void drawLargeIndelLabel(Graphics2D g, boolean isInsertion, String label
g.fillRect(pxLeft, pxTop, pxRight - pxLeft, pxH);

if (isInsertion && pxH > 5) {
g.fillRect(pxLeft - pxWing, pxTop, pxRight - pxLeft + 2 * pxWing, 2);
g.fillRect(pxLeft - pxWing, pxTop + pxH - 2, pxRight - pxLeft + 2 * pxWing, 2);
if ( flowIndelRendering.handlesAlignment(alignment) ) {
flowIndelRendering.renderSmallInsertionWings(alignment, insertionBlock, context, pxH, pxTop, pxRight, pxLeft, track.renderOptions);
} else {
g.fillRect(pxLeft - pxWing, pxTop, pxRight - pxLeft + 2 * pxWing, 2);
g.fillRect(pxLeft - pxWing, pxTop + pxH - 2, pxRight - pxLeft + 2 * pxWing, 2);
}
} // draw "wings" For insertions

if (doesTextFit) {
Expand All @@ -1057,6 +1080,104 @@ private void drawLargeIndelLabel(Graphics2D g, boolean isInsertion, String label
}
}

public void renderExpandedInsertion(InsertionMarker i,
List<Alignment> alignments,
RenderContext context,
Rectangle rect,
boolean leaveMargin) {
double origin = context.getOrigin();
double locScale = context.getScale();
if ((alignments != null) && (alignments.size() > 0)) {

Graphics2D g = context.getGraphics2D("INSERTIONS");
double dX = 1 / context.getScale();
int fontSize = (int) Math.min(dX, 12);
if (fontSize >= 8) {
Font f = FontManager.getFont(Font.BOLD, fontSize);
g.setFont(f);
}

for (Alignment alignment : alignments) {
if (alignment.getEnd() < i.position) continue;
if (alignment.getStart() > i.position) break;
AlignmentBlock insertion = alignment.getInsertionAt(i.position);
if (insertion != null) {

// Compute the start and dend of the alignment in pixels
double pixelStart = (insertion.getStart() - origin) / locScale;
double pixelEnd = (insertion.getEnd() - origin) / locScale;
int x = (int) pixelStart;

// If any any part of the feature fits in the track rectangle draw it
if (pixelEnd < rect.x || pixelStart > rect.getMaxX()) {
continue;
}

int bpWidth = insertion.getBasesLength();
double pxWidthExact = ((double) bpWidth) / locScale;
int h = (int) Math.max(1, rect.getHeight() - 2);
int y = (int) (rect.getY() + (rect.getHeight() - h) / 2) - 1;

if (!insertion.hasBases()) {
g.setColor(purple);
g.fillRect(x, y, (int) pxWidthExact, h);

} else {
drawExpandedInsertionBases(x, context, rect, insertion, leaveMargin);
}
}
}
}
}


private void drawExpandedInsertionBases(int pixelPosition,
RenderContext context,
Rectangle rect,
AlignmentBlock block,
boolean leaveMargin) {
Graphics2D g = context.getGraphics2D("INSERTIONS");
ByteSubarray bases = block.getBases();
int padding = block.getPadding();

double locScale = context.getScale();
double origin = context.getOrigin();

// Compute bounds
int pY = (int) rect.getY();
int dY = (int) rect.getHeight();
int dX = (int) Math.max(1, (1.0 / locScale));

final int size = bases.length + padding;
for (int p = 0; p < size; p++) {

char c = p < padding ? '-' : (char) bases.getByte(p - padding);

Color color = SequenceRenderer.nucleotideColors.get(c);
if (color == null) {
color = Color.black;
}

// If there is room for text draw the character, otherwise
// just draw a rectangle to represent the
int pX = (int) (pixelPosition + (p / locScale));

// Don't draw out of clipping rect
if (pX > rect.getMaxX()) {
break;
} else if (pX + dX < rect.getX()) {
continue;
}
BaseRenderer.drawBase(g, color, c, pX, pY, dX, dY - (leaveMargin ? 2 : 0), false, null);
}

int leftX = pixelPosition + context.translateX;
int rightX = leftX + rect.width;
block.setPixelRange(leftX, rightX);

}


private Color getAlignmentColor(Alignment alignment, AlignmentTrack track) {

// Set color used to draw the feature. Highlight features that intersect the
Expand Down Expand Up @@ -1205,7 +1326,7 @@ private Color getAlignmentColor(Alignment alignment, AlignmentTrack track) {
case TAG:
final String tag = renderOptions.getColorByTag();
if (tag != null) {
Object tagValue = alignment.getAttribute(tag);
Object tagValue = !colorByTagValueList.handlesTag(tag) ? alignment.getAttribute(tag) : colorByTagValueList.getValueForColorByTag(alignment, tag);
if (tagValue != null) {

ColorTable ctable;
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/org/broad/igv/sam/AlignmentTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ public static boolean isBisulfiteColorType(ColorOption o) {
private final Genome genome;
private ExperimentType experimentType;
private final AlignmentRenderer renderer;
private RenderOptions renderOptions;
RenderOptions renderOptions;

private boolean removed = false;
private boolean showGroupLine;
Expand Down Expand Up @@ -1211,6 +1211,7 @@ public static class RenderOptions implements Cloneable, Persistable {
private Boolean linkedReads;
private Boolean quickConsensusMode;
private Boolean showMismatches;
private Boolean insertQualColoring;
Boolean computeIsizes;
private Double minInsertSizePercentile;
private Double maxInsertSizePercentile;
Expand Down Expand Up @@ -1278,6 +1279,10 @@ public void setViewPairs(boolean viewPairs) {
this.viewPairs = viewPairs;
}

void setInsertQualColoring(boolean insertQualColoring) {
this.insertQualColoring = insertQualColoring;
}

void setComputeIsizes(boolean computeIsizes) {
this.computeIsizes = computeIsizes;
}
Expand Down Expand Up @@ -1396,6 +1401,9 @@ public boolean isViewPairs() {
return viewPairs;
}

public boolean isInsertQualColoring() {
return insertQualColoring == null ? getPreferences().getAsBoolean(SAM_INSERT_QUAL_COLORING) : insertQualColoring;
}
public boolean isComputeIsizes() {
return computeIsizes == null ? getPreferences().getAsBoolean(SAM_COMPUTE_ISIZES) : computeIsizes;
}
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/org/broad/igv/sam/SAMAlignment.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import org.broad.igv.sam.mods.BaseModificationSet;
import org.broad.igv.sam.smrt.SMRTKinetics;
import org.broad.igv.ui.color.ColorUtilities;
import org.broad.igv.ultima.annotate.FlowBlockAnnotator;

import java.awt.*;
import java.util.*;
Expand Down Expand Up @@ -96,6 +97,8 @@ public class SAMAlignment implements Alignment {

private int flags;

final private static FlowBlockAnnotator flowBlockAnnotator = new FlowBlockAnnotator();

/**
* Picard object upon which this SAMAlignment is based
*/
Expand Down Expand Up @@ -771,6 +774,10 @@ public String getAlignmentValueString(double position, int mouseX, AlignmentTrac
buf.append("Insertion (" + bases.length + " bases): " + new String(bases.copyOfRange(0, 25)) + "..." +
new String(bases.copyOfRange(len - 25, len)) + "<br>");
}

// extended annotation?
if ( flowBlockAnnotator.handlesBlocks(block) )
flowBlockAnnotator.appendBlockQualityAnnotation(this, block, buf);
}
atInsertion = true;
}
Expand Down Expand Up @@ -962,8 +969,10 @@ public String getAlignmentValueString(double position, int mouseX, AlignmentTrac

byte quality = block.getQuality(offset);
buf.append("Location = " + getChr() + ":" + Globals.DECIMAL_FORMAT.format(1 + (long) position) + "<br>");
buf.append("Base = " + (char) base + " @ QV " + Globals.DECIMAL_FORMAT.format(quality) + "<br>");

buf.append("Base = " + (char) base + " @ QV " + Globals.DECIMAL_FORMAT.format(quality));
if ( flowBlockAnnotator.handlesBlocks(block) )
flowBlockAnnotator.appendBlockAttrAnnotation(this, block, offset, buf);
buf.append("<br>");
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,15 @@
import org.broad.igv.feature.genome.load.HubGenomeLoader;
import org.broad.igv.logging.*;
import org.broad.igv.feature.genome.GenomeManager;
import org.broad.igv.util.GoogleUtils;
import org.broad.igv.prefs.Constants;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.session.SessionReader;
import org.broad.igv.ui.IGV;
import org.broad.igv.ui.util.LoadFromURLDialog;
import org.broad.igv.ui.util.MessageUtils;
import org.broad.igv.util.*;
import org.broad.igv.ui.IGVMenuBar;

import javax.swing.*;
import java.awt.*;
Expand Down Expand Up @@ -205,7 +209,6 @@ private void checkAWSAccessbility(String url) {
AmazonUtils.s3ObjectAccessResult res = isObjectAccessible(bucket, key);
if (!res.isObjectAvailable()) {
MessageUtils.showErrorMessage(res.getErrorReason(), null);
return;
}
}
} catch (NullPointerException npe) {
Expand Down
Loading

0 comments on commit 787c5cf

Please sign in to comment.