Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

basemod enhancements -- allow selection of specific modifications #1349

Merged
merged 1 commit into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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