Skip to content

Commit

Permalink
basemod enhancements -- allow selection of specific modifications (#1349
Browse files Browse the repository at this point in the history
)
  • Loading branch information
jrobinso authored Jun 27, 2023
1 parent d885f74 commit 337d8cc
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 33 deletions.
25 changes: 23 additions & 2 deletions src/main/java/org/broad/igv/sam/AlignmentDataManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
import org.broad.igv.feature.genome.Genome;
import org.broad.igv.prefs.IGVPreferences;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.sam.mods.BaseModificationSet;
import org.broad.igv.sam.reader.AlignmentReader;
import org.broad.igv.sam.reader.AlignmentReaderFactory;
import org.broad.igv.track.Track;
Expand All @@ -58,17 +59,18 @@ public class AlignmentDataManager implements IGVEventObserver {
private static Logger log = LogManager.getLogger(AlignmentDataManager.class);
private final AlignmentReader reader;


private AlignmentTrack alignmentTrack;
private CoverageTrack coverageTrack;
private Set<Track> subscribedTracks;

private List<AlignmentInterval> intervalCache;
private ResourceLocator locator;
private HashMap<String, String> chrMappings = new HashMap();
private AlignmentTileLoader loader;
private Map<String, PEStats> peStats;
private SpliceJunctionHelper.LoadOptions loadOptions;

private Set<String> allBaseModifications = new HashSet<>();

private Range currentlyLoading;

public AlignmentDataManager(ResourceLocator locator, Genome genome) throws IOException {
Expand Down Expand Up @@ -193,6 +195,13 @@ public Map<String, PEStats> getPEStats() {
return peStats;
}

/**
* Return all base modfications seen in loaded alignments
*/
public Set<String> getAllBaseModifications() {
return allBaseModifications;
}

public boolean isPairedEnd() {
return getLoader().isPairedEnd();
}
Expand Down Expand Up @@ -393,9 +402,21 @@ AlignmentInterval loadInterval(String chr, int start, int end, AlignmentTrack.Re
downsampleOptions, peStats, bisulfiteContext, renderOptions);
List<Alignment> alignments = t.getAlignments();
List<DownsampledInterval> downsampledIntervals = t.getDownsampledIntervals();
this.updateBaseModfications(alignments);
return new AlignmentInterval(chr, start, end, alignments, t.getCounts(), spliceJunctionHelper, downsampledIntervals);
}

private void updateBaseModfications(List<Alignment> alignments) {
for(Alignment a : alignments) {
List<BaseModificationSet> bmSets = a.getBaseModificationSets();
if(bmSets != null) {
for(BaseModificationSet bms : bmSets) {
allBaseModifications.add(bms.getModification());
}
}
}
}

public AlignmentTrack.ExperimentType inferType() {
ReadStats readStats = new ReadStats();
List<Alignment> sample = AlignmentUtils.firstAlignments(reader, 100);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/broad/igv/sam/AlignmentRenderer.java
Original file line number Diff line number Diff line change
Expand Up @@ -853,7 +853,7 @@ private void drawAlignment(

// Base modification
if (colorOption.isBaseMod()) {
BaseModificationRenderer.drawModifications(alignment, bpStart, locScale, rowRect, context.getGraphics(), colorOption);
BaseModificationRenderer.drawModifications(alignment, bpStart, locScale, rowRect, context.getGraphics(), colorOption, renderOptions.getBasemodFilter());
}

// Kinetic data
Expand Down
34 changes: 25 additions & 9 deletions src/main/java/org/broad/igv/sam/AlignmentTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,6 @@ public boolean isSMRTKinetics() {
}



public enum ShadeAlignmentsOption {
NONE("none"),
MAPPING_QUALITY_HIGH("mapping quality high"),
Expand Down Expand Up @@ -252,9 +251,11 @@ public BisulfiteContext getMatchingBisulfiteContext(final byte[] reference, fina
return (matchesContext) ? this : null;
}

public String getLabel() { return label; }
public String getLabel() {
return label;
}

BisulfiteContext(String label, byte[] preContext, byte[] postContext){
BisulfiteContext(String label, byte[] preContext, byte[] postContext) {
this.label = label;
this.preContext = preContext;
this.postContext = postContext;
Expand Down Expand Up @@ -389,7 +390,7 @@ public void receiveEvent(Object event) {
break;
}

} else if( event instanceof DataLoadedEvent){
} else if (event instanceof DataLoadedEvent) {
final DataLoadedEvent dataLoaded = (DataLoadedEvent) event;
actionToPerformOnFrameLoad.computeIfPresent(dataLoaded.getReferenceFrame(), (k, v) -> {
v.accept(k);
Expand Down Expand Up @@ -747,7 +748,7 @@ public void renderExpandedInsertion(InsertionMarker insertionMarker, RenderConte
boolean leaveMargin = getDisplayMode() != DisplayMode.SQUISHED;

// Insertion interval
if(this.renderOptions.isShowInsertionMarkers()) {
if (this.renderOptions.isShowInsertionMarkers()) {
Graphics2D g = context.getGraphic2DForColor(Color.red);
Rectangle iRect = new Rectangle(inputRect.x, insertionRect.y, inputRect.width, insertionRect.height);
g.fill(iRect);
Expand Down Expand Up @@ -830,14 +831,14 @@ private InsertionInterval getInsertionInterval(ReferenceFrame frame, int x, int
public void sortRows(final SortOption option, final Double location, final String tag, final boolean invertSort, final Set<String> priorityRecords) {
final List<ReferenceFrame> frames = FrameManager.getFrames();
for (ReferenceFrame frame : frames) {
Consumer<ReferenceFrame> sort = (ReferenceFrame f) -> {
Consumer<ReferenceFrame> sort = (ReferenceFrame f) -> {
final AlignmentInterval interval = getDataManager().getLoadedInterval(f);
final double actloc = location != null ? location : f.getCenter();
interval.sortRows(option, actloc, tag, invertSort, priorityRecords);
};
//If the data is loaded sort now, otherwise delay until we get a message that it is loaded.
if(getDataManager().isLoaded(frame)){
sort.accept(frame);
if (getDataManager().isLoaded(frame)) {
sort.accept(frame);
} else {
log.debug("Attempt to sort alignments prior to loading");
actionToPerformOnFrameLoad.put(frame, sort);
Expand Down Expand Up @@ -936,7 +937,6 @@ public String getValueStringAt(String chr, double position, int mouseX, int mous
}



Alignment getAlignmentAt(final TrackClickEvent te) {
MouseEvent e = te.getMouseEvent();
final ReferenceFrame frame = te.getFrame();
Expand Down Expand Up @@ -1331,6 +1331,8 @@ public static class RenderOptions implements Cloneable, Persistable {
private Boolean hideSmallIndels;
private Integer smallIndelThreshold;

private String basemodFilter;

BisulfiteContext bisulfiteContext = BisulfiteContext.CG;
Map<String, PEStats> peStats;

Expand Down Expand Up @@ -1601,6 +1603,13 @@ public int getSmallIndelThreshold() {
return smallIndelThreshold == null ? getPreferences().getAsInt(SAM_SMALL_INDEL_BP_THRESHOLD) : smallIndelThreshold;
}

public String getBasemodFilter() {
return basemodFilter;
}

public void setBasemodFilter(String basemodFilter) {
this.basemodFilter = basemodFilter;
}

@Override
public void marshalXML(Document document, Element element) {
Expand Down Expand Up @@ -1698,6 +1707,9 @@ public void marshalXML(Document document, Element element) {
if (showInsertionMarkers != null) {
element.setAttribute("showInsertionMarkers", showInsertionMarkers.toString());
}
if (basemodFilter != null) {
element.setAttribute("basemodfilter", basemodFilter);
}
}


Expand Down Expand Up @@ -1800,6 +1812,10 @@ public void unmarshalXML(Element element, Integer version) {
if (element.hasAttribute("showInsertionMarkers")) {
showInsertionMarkers = Boolean.parseBoolean(element.getAttribute("showInsertionMarkers"));
}
if (element.hasAttribute("basemodfilter")) {
basemodFilter = element.getAttribute("basemodfilter");
}

}
}

Expand Down
61 changes: 47 additions & 14 deletions src/main/java/org/broad/igv/sam/AlignmentTrackMenu.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import org.broad.igv.logging.LogManager;
import org.broad.igv.logging.Logger;
import org.broad.igv.prefs.PreferencesManager;
import org.broad.igv.sam.mods.BaseModificationUtils;
import org.broad.igv.sashimi.SashimiPlot;
import org.broad.igv.tools.PFMExporter;
import org.broad.igv.track.SequenceTrack;
Expand Down Expand Up @@ -142,7 +143,7 @@ class AlignmentTrackMenu extends IGVPopupMenu {

// Select alignment items
addSeparator();
addSelectByNameItem(alignmentTrack, e);
addSelectByNameItem(alignmentTrack, e);
addClearSelectionsMenuItem();

// Copy items
Expand Down Expand Up @@ -195,7 +196,7 @@ private void addShowChimericRegions(final AlignmentTrack alignmentTrack, final T
add(item);
}

private void addShowDiagram(final TrackClickEvent e, final Alignment clickedAlignment){
private void addShowDiagram(final TrackClickEvent e, final Alignment clickedAlignment) {
JMenuItem item = new JMenuItem("Supplementary Reads Diagram");
if (clickedAlignment != null && clickedAlignment.getAttribute(SAMTag.SA.name()) != null) {
item.setEnabled(true);
Expand Down Expand Up @@ -472,8 +473,8 @@ void addGroupMenuItem(final TrackClickEvent te) {//ReferenceFrame frame) {

private void groupAlignments(AlignmentTrack.GroupOption option, String tag, Range pos) {

if(alignmentTrack.getPreferences().getAsBoolean(SAM_GROUP_ALL)) {
for(AlignmentTrack t : IGV.getInstance().getAlignmentTracks()) {
if (alignmentTrack.getPreferences().getAsBoolean(SAM_GROUP_ALL)) {
for (AlignmentTrack t : IGV.getInstance().getAlignmentTracks()) {
t.groupAlignments(option, tag, pos);
}
} else {
Expand Down Expand Up @@ -563,10 +564,18 @@ public void addFilterMenuItem() {
}

private JRadioButtonMenuItem getColorMenuItem(String label, final AlignmentTrack.ColorOption option) {
return getColorMenuItem(label, option, null);

}

private JRadioButtonMenuItem getColorMenuItem(String label, final AlignmentTrack.ColorOption option, String extra) {
JRadioButtonMenuItem mi = new JRadioButtonMenuItem(label);
mi.setSelected(renderOptions.getColorOption() == option);
mi.addActionListener(aEvt -> {
alignmentTrack.setColorOption(option);
if (option == AlignmentTrack.ColorOption.BASE_MODIFICATION) {
renderOptions.setBasemodFilter(extra);
}
alignmentTrack.repaint();
});

Expand Down Expand Up @@ -632,18 +641,42 @@ void addColorByMenuItem() {
colorMenu.add(getBisulfiteContextMenuItem(group));

// Base modifications
mappings.clear();
mappings.put("base modification", AlignmentTrack.ColorOption.BASE_MODIFICATION);
mappings.put("base modification (5mC)", AlignmentTrack.ColorOption.BASE_MODIFICATION_5MC);
mappings.put("base modification (all C)", AlignmentTrack.ColorOption.BASE_MODIFICATION_C);
mappings.put("base modification (6mA)", AlignmentTrack.ColorOption.BASE_MODIFICATION_6MA);
JRadioButtonMenuItem bmMenuItem;

colorMenu.addSeparator();
for (Map.Entry<String, AlignmentTrack.ColorOption> el : mappings.entrySet()) {
JRadioButtonMenuItem mi = getColorMenuItem(el.getKey(), el.getValue());
colorMenu.add(mi);
group.add(mi);

Set<String> allModifications = dataManager.getAllBaseModifications();

bmMenuItem = getColorMenuItem("CpG modification (5mC)", AlignmentTrack.ColorOption.BASE_MODIFICATION_5MC);
bmMenuItem.setSelected(renderOptions.getColorOption() == AlignmentTrack.ColorOption.BASE_MODIFICATION_5MC);
bmMenuItem.setEnabled(allModifications.contains("m"));
colorMenu.add(bmMenuItem);
group.add(bmMenuItem);

bmMenuItem = getColorMenuItem("CpG modification (all C)", AlignmentTrack.ColorOption.BASE_MODIFICATION_C);
bmMenuItem.setSelected(renderOptions.getColorOption() == AlignmentTrack.ColorOption.BASE_MODIFICATION_C);
bmMenuItem.setEnabled(allModifications.contains("m") || allModifications.contains("h"));
colorMenu.add(bmMenuItem);
group.add(bmMenuItem);

colorMenu.addSeparator();

bmMenuItem = getColorMenuItem("base modification (all)", AlignmentTrack.ColorOption.BASE_MODIFICATION);
bmMenuItem.setSelected(renderOptions.getColorOption() == AlignmentTrack.ColorOption.BASE_MODIFICATION && renderOptions.getBasemodFilter() == null);
bmMenuItem.setEnabled(!allModifications.isEmpty());
colorMenu.add(bmMenuItem);
group.add(bmMenuItem);

for(String m : allModifications) {
String name = BaseModificationUtils.modificationName(m);
bmMenuItem = getColorMenuItem("base modification (" + name + ")", AlignmentTrack.ColorOption.BASE_MODIFICATION, m);
bmMenuItem.setSelected(renderOptions.getColorOption() == AlignmentTrack.ColorOption.BASE_MODIFICATION && m.equals(renderOptions.getBasemodFilter()));
colorMenu.add(bmMenuItem);
group.add(bmMenuItem);
}


// SMRT kinetics
if (alignmentTrack.getPreferences().getAsBoolean(SMRT_KINETICS_SHOW_OPTIONS)) {
// Show additional options to help visualize SMRT kinetics data
mappings.clear();
Expand All @@ -652,7 +685,7 @@ void addColorByMenuItem() {
mappings.put("SMRT CCS fwd-strand aligned IPD", AlignmentTrack.ColorOption.SMRT_CCS_FWD_IPD);
mappings.put("SMRT CCS fwd-strand aligned PW", AlignmentTrack.ColorOption.SMRT_CCS_FWD_PW);
mappings.put("SMRT CCS rev-strand aligned IPD", AlignmentTrack.ColorOption.SMRT_CCS_REV_IPD);
mappings.put("SMRT CCS rev-strand aligned PW",AlignmentTrack.ColorOption.SMRT_CCS_REV_PW);
mappings.put("SMRT CCS rev-strand aligned PW", AlignmentTrack.ColorOption.SMRT_CCS_REV_PW);
colorMenu.addSeparator();
for (Map.Entry<String, AlignmentTrack.ColorOption> el : mappings.entrySet()) {
JRadioButtonMenuItem mi = getColorMenuItem(el.getKey(), el.getValue());
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/broad/igv/sam/CoverageTrack.java
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,8 @@ private void paint(final RenderContext context, final Rectangle rect, final Alig
drawBarBisulfite(context, pX, bottomY, dX, barHeight, totalCount, bc);
}
} else if (colorOption.isBaseMod()) {
BaseModificationCoverageRenderer.drawModifications(context, pX, bottomY, dX, barHeight, pos, alignmentCounts, colorOption);
String basemodFilter = alignmentTrack != null ? alignmentTrack.getRenderOptions().getBasemodFilter() : null;
BaseModificationCoverageRenderer.drawModifications(context, pX, bottomY, dX, barHeight, pos, alignmentCounts, colorOption, basemodFilter);
} else {
if (refBases != null) {
int refIdx = pos - intervalStart;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public static void drawModifications(RenderContext context,
int barHeight,
int pos,
AlignmentCounts alignmentCounts,
ColorOption colorOption) {
ColorOption colorOption,
String basemodFilter) {

switch (colorOption) {
case BASE_MODIFICATION_5MC:
Expand All @@ -31,7 +32,7 @@ public static void drawModifications(RenderContext context,
draw(context, pX, pBottom, dX, barHeight, pos, alignmentCounts, "a");
break;
default:
draw(context, pX, pBottom, dX, barHeight, pos, alignmentCounts, null);
draw(context, pX, pBottom, dX, barHeight, pos, alignmentCounts, basemodFilter);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static void drawModifications(
double locScale,
Rectangle rowRect,
Graphics g,
AlignmentTrack.ColorOption colorOption) {
AlignmentTrack.ColorOption colorOption,
String basemodFilter) {

switch (colorOption) {
case BASE_MODIFICATION_5MC:
Expand All @@ -28,7 +29,7 @@ public static void drawModifications(
draw(alignment, bpStart, locScale, rowRect, g, "a");
break;
default:
draw(alignment, bpStart, locScale, rowRect, g, null);
draw(alignment, bpStart, locScale, rowRect, g, basemodFilter);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,12 @@ public class BaseModificationUtils {

public static String valueString(String modification, byte likelihood) {
int l = (int) (100.0 * Byte.toUnsignedInt(likelihood) / 255);
return "Base modification: " +
((codeValues.containsKey(modification)) ? codeValues.get(modification) : "Uknown") + " (" + l + "%)";
return "Base modification: " + modificationName(modification) + " (" + l + "%)";
}


public static String modificationName(String modification) {
return ((codeValues.containsKey(modification)) ? codeValues.get(modification) : modification);
}


Expand Down

0 comments on commit 337d8cc

Please sign in to comment.