Java tutorial
/* * Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.fhcrc.cpl.viewer.quant.gui; import org.fhcrc.cpl.toolbox.gui.chart.*; import org.fhcrc.cpl.toolbox.commandline.CommandLineModuleExecutionException; import org.fhcrc.cpl.toolbox.commandline.CommandLineModuleUtilities; import org.fhcrc.cpl.toolbox.ApplicationContext; import org.fhcrc.cpl.toolbox.datastructure.Pair; import org.fhcrc.cpl.toolbox.filehandler.TempFileManager; import org.fhcrc.cpl.toolbox.proteomics.ModifiedAminoAcid; import org.fhcrc.cpl.toolbox.proteomics.MSRun; import org.fhcrc.cpl.toolbox.proteomics.feature.Spectrum; import org.fhcrc.cpl.toolbox.proteomics.feature.Feature; import org.fhcrc.cpl.toolbox.proteomics.feature.FeatureSet; import org.fhcrc.cpl.toolbox.proteomics.feature.AnalyzeICAT; import org.fhcrc.cpl.toolbox.proteomics.feature.extraInfo.MS2ExtraInfoDef; import org.fhcrc.cpl.toolbox.proteomics.feature.extraInfo.IsotopicLabelExtraInfoDef; import org.fhcrc.cpl.viewer.quant.QuantEvent; import org.fhcrc.cpl.viewer.quant.QuantEventAssessor; import org.fhcrc.cpl.viewer.quant.turk.TurkUtilities; import org.apache.log4j.Logger; import org.jfree.chart.plot.XYPlot; import javax.imageio.ImageIO; import javax.swing.*; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.FileOutputStream; import java.util.*; import java.util.List; import java.awt.*; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; /** * Visualize quantitation events. This class figures out what events to visualize and gathers all * the data from each event, but the actual creation of charts is done in PanelWithSpectrumChart. * * Summaries of the quantitation event information are written in TSV and HTML format. Qurate * can load the TSV version */ public class QuantitationVisualizer { protected static Logger _log = Logger.getLogger(QuantitationVisualizer.class); protected int resolution = PanelWithSpectrumChart.DEFAULT_RESOLUTION; public static final int DEFAULT_IMAGE_HEIGHT_3D = 900; public static final int DEFAULT_IMAGE_WIDTH_3D = 900; public static final int DEFAULT_SINGLE_SCAN_IMAGE_HEIGHT = 100; public static final int DEFAULT_MAX_SINGLE_SCANS_TOTAL_IMAGE_HEIGHT = 4000; public static final int DEFAULT_SPECTRUM_IMAGE_HEIGHT = 700; public static final int DEFAULT_SPECTRUM_IMAGE_WIDTH = 900; public static final float AMINOACID_MODIFICATION_EQUALITY_MASS_TOLERANCE = 0.25f; public static final float AMINOACID_MODIFICATION_EQUALITY_MZ_TOLERANCE = 0.25f; protected Iterator<FeatureSet> featureSetIterator; //a non-displayed button that's used to add listeners who care when we complete each event. //TODO: do this in some less silly way protected JButton dummyProgressButton = new JButton(); //Have to stop somewhere. Chart will extend this number of peaks beyond the heavy monoisotope, plus slop int numHeavyPeaksToPlot = 4; protected File outDir = null; protected File outTsvFile = null; //for Mechanical Turk HITs protected File outTurkFile = null; protected File outHtmlFile = null; //Should we append TSV output to the existing file, if one exists? protected boolean appendTsvOutput = false; protected boolean tsvFileAlreadyExists = false; //Do we actually create the charts, or just create the output files? protected boolean shouldCreateCharts = true; //Should all events that are written be marked Bad? If not, mark Unknown protected boolean markAllEventsBad = false; //should we do an automated assessment of the event? protected boolean shouldAssessEvents = true; //The assumed difference in mass between isotopic peaks protected float peakSeparationMass = PanelWithSpectrumChart.DEFAULT_PEAK_SEPARATION_MASS; //Mass tolerance around each peak, for summary chart protected float peakTolerancePPM = PanelWithSpectrumChart.DEFAULT_PEAK_TOLERANCE_PPM; //height and width of images protected int scanImageHeight = DEFAULT_SINGLE_SCAN_IMAGE_HEIGHT; protected int maxScansImageHeight = DEFAULT_MAX_SINGLE_SCANS_TOTAL_IMAGE_HEIGHT; protected int spectrumImageHeight = DEFAULT_SPECTRUM_IMAGE_HEIGHT; protected int imageWidth = DEFAULT_SPECTRUM_IMAGE_WIDTH; //Directory of mzXML files protected File mzXmlDir; //Single mzXML file protected File mzXmlFile = null; //The amount by which two features can differ and still be combined into the same single representation. //TODO: convert this to log space, parameterize protected float maxCombineFeatureRatioDiff = 0.2f; //Should we write out HTML and tsv text for our events as we build them? protected boolean writeHTMLAndText = true; //PeptideProphet minimum protected float minPeptideProphet = 0; //Scans to display around event protected int numPaddingScans = 3; //m/z padding to display around event protected float mzPadding = 1.5f; //keeps track of the unique peptides of the events plotted protected Set<String> peptidesFound = new HashSet<String>(); //A ghastly structure to hold references to all of the tsv and html files created //In order: protein, peptide, fraction, charge, pairs of <tsv,html> files protected Map<String, Map<String, Map<String, Map<Integer, List<Pair<File, File>>>>>> proteinPeptideFractionChargeFilesMap = new HashMap<String, Map<String, Map<String, Map<Integer, List<Pair<File, File>>>>>>(); //For QuantEvent objects with no associated protein protected static final String DUMMY_PROTEIN_NAME = "DUMMY_PROTEIN"; //should we show 3D plots? protected boolean show3DPlots = true; //Full control of 3D plot parameters protected int rotationAngle3D = PanelWithSpectrumChart.DEFAULT_CONTOUR_PLOT_ROTATION; protected int tiltAngle3D = PanelWithSpectrumChart.DEFAULT_CONTOUR_PLOT_TILT; protected int imageHeight3D = DEFAULT_IMAGE_HEIGHT_3D; protected int imageWidth3D = DEFAULT_IMAGE_WIDTH_3D; protected boolean show3DAxes = true; //Controls whether we write event info directly to the chart images protected boolean writeInfoOnCharts = false; protected boolean writeTheoreticalPeaksOnCharts = false; //If we're adding sidebar information to the images, width of the sidebar protected int sidebarWidth = 180; //a single scan that we want to visualize. This should probably be controlled somewhere else int scan = 0; //Sets of things we want to visualize. This should probably be controlled somewhere else protected Set<String> peptidesToExamine; protected Set<String> proteinsToExamine; protected Set<String> fractionsToExamine; //PrintWriters for the output files protected PrintWriter outHtmlPW; protected PrintWriter outTsvPW; protected PrintWriter outTurkPW; //Unique identifier (per file) for Turk HITs protected int currentTurkID = 0; //URL prefix to add before all image names in Turk output file. Should end with "/" protected String turkImageURLPrefix = ""; protected int turkChartWidth = 690; protected int turkChartHeight = 460; //Should we include the Protein column in the output files? //TODO: get rid of this, always show protein column protected boolean showProteinColumn = true; public QuantitationVisualizer() { } /** * Visualize just the events specified * TODO: fold parts of this in with the no-arg visualizeQuantEvents * @param quantEvents * @throws IOException * @return the list of QuantEvents in the order in which they were written to the file(s) */ public List<QuantEvent> visualizeQuantEvents(List<QuantEvent> quantEvents, boolean saveInProteinDirs) throws IOException { if (outHtmlFile == null) outHtmlFile = new File(outDir, "quantitation.html"); if (outTsvFile == null) outTsvFile = new File(outDir, "quantitation.tsv"); tsvFileAlreadyExists = outTsvFile.exists(); if (writeHTMLAndText) { outHtmlPW = new PrintWriter(outHtmlFile); outTsvPW = new PrintWriter(new FileOutputStream(outTsvFile, appendTsvOutput)); //if we're appending, don't write header. Cheating by writing it to a fake file PrintWriter conditionalTsvPW = outTsvPW; if (appendTsvOutput && tsvFileAlreadyExists) conditionalTsvPW = new PrintWriter( TempFileManager.createTempFile("fake_file", "fake_file_for_quantvisualizer")); QuantEvent.writeHeader(outHtmlPW, conditionalTsvPW, showProteinColumn, show3DPlots); _log.debug("Wrote header to HTML file " + outHtmlFile.getAbsolutePath() + " and tsv file " + outTsvFile.getAbsolutePath()); TempFileManager.deleteTempFiles("fake_file_for_quantvisualizer"); } if (outTurkFile != null) { outTurkPW = new PrintWriter(outTurkFile); outTurkPW.println(TurkUtilities.createTurkHITFileHeaderLine()); outTurkPW.flush(); } //map from fraction name to list of events in that fraction Map<String, List<QuantEvent>> fractionEventMap = new HashMap<String, List<QuantEvent>>(); for (QuantEvent quantEvent : quantEvents) { List<QuantEvent> eventList = fractionEventMap.get(quantEvent.getFraction()); if (eventList == null) { eventList = new ArrayList<QuantEvent>(); fractionEventMap.put(quantEvent.getFraction(), eventList); } eventList.add(quantEvent); } //sort events by scan within fractions, to keep recently-used scans in cache Comparator<QuantEvent> scanAscComp = new QuantEvent.ScanAscComparator(); for (List<QuantEvent> eventList : fractionEventMap.values()) Collections.sort(eventList, scanAscComp); int numEventsProcessed = 0; //listeners that want to be updated when we finish an event ActionListener[] progressListeners = dummyProgressButton.getActionListeners(); List<QuantEvent> resortedEvents = new ArrayList<QuantEvent>(); for (String fraction : fractionEventMap.keySet()) { File mzXmlFile = CommandLineModuleUtilities.findFileWithPrefix(fraction + ".", mzXmlDir, "mzXML"); MSRun run = MSRun.load(mzXmlFile.getAbsolutePath()); for (QuantEvent quantEvent : fractionEventMap.get(fraction)) { resortedEvents.add(quantEvent); File outDirThisEvent = outDir; if (saveInProteinDirs && shouldCreateCharts) { String protein = quantEvent.getProtein(); outDirThisEvent = new File(outDir, protein); outDirThisEvent.mkdir(); } handleEvent(run, outDirThisEvent, quantEvent.getProtein(), fraction, quantEvent); numEventsProcessed++; if (progressListeners != null) { ActionEvent event = new ActionEvent(dummyProgressButton, 0, "" + numEventsProcessed); for (ActionListener listener : progressListeners) listener.actionPerformed(event); } } } if (writeHTMLAndText) { QuantEvent.writeFooterAndClose(outHtmlPW, outTsvPW); ApplicationContext.infoMessage("Saved HTML file " + outHtmlFile.getAbsolutePath()); ApplicationContext.infoMessage("Saved TSV file " + outTsvFile.getAbsolutePath()); } if (outTurkFile != null) { try { outTurkPW.close(); } catch (Exception e) { } } return resortedEvents; } /** * Iterate through all fractions, finding and visualizing the selected events */ public void visualizeQuantEvents() throws IOException { //trying to prevent memory leaks ImageIO.setUseCache(false); if (outHtmlFile == null) outHtmlFile = new File(outDir, "quantitation.html"); if (outTsvFile == null) outTsvFile = new File(outDir, "quantitation.tsv"); _log.debug("visualizeQuantEvents begin, write HTML and text? " + writeHTMLAndText); if (writeHTMLAndText) { outHtmlPW = new PrintWriter(outHtmlFile); outTsvPW = new PrintWriter(new FileOutputStream(outTsvFile, appendTsvOutput)); _log.debug("opened HTML file " + outHtmlFile.getAbsolutePath() + " and tsv file " + outTsvFile.getAbsolutePath() + " for writing."); //if we're appending, don't write header. Cheating by writing it to a fake file PrintWriter conditionalTsvPW = outTsvPW; if (appendTsvOutput && tsvFileAlreadyExists) conditionalTsvPW = new PrintWriter( TempFileManager.createTempFile("fake_file", "fake_file_for_quantvisualizer")); QuantEvent.writeHeader(outHtmlPW, conditionalTsvPW, showProteinColumn, show3DPlots); _log.debug("Wrote HTML and TSV header"); TempFileManager.deleteTempFiles("fake_file_for_quantvisualizer"); } boolean processedAFraction = false; while (featureSetIterator.hasNext()) { FeatureSet fraction = featureSetIterator.next(); ApplicationContext .infoMessage("Evaluating fraction " + MS2ExtraInfoDef.getFeatureSetBaseName(fraction)); if (fractionsToExamine == null || fractionsToExamine.contains(MS2ExtraInfoDef.getFeatureSetBaseName(fraction))) { ApplicationContext .infoMessage("Handling fraction " + MS2ExtraInfoDef.getFeatureSetBaseName(fraction)); handleFraction(fraction); processedAFraction = true; } //trying to prevent memory overflow System.gc(); } if (!processedAFraction) ApplicationContext.infoMessage("WARNING: no fractions processed"); if (writeHTMLAndText) { QuantEvent.writeFooterAndClose(outHtmlPW, outTsvPW); ApplicationContext.infoMessage("Saved HTML file " + outHtmlFile.getAbsolutePath()); ApplicationContext.infoMessage("Saved TSV file " + outTsvFile.getAbsolutePath()); } } /** * Visualize selected events from a fraction * @param featureSet * @throws CommandLineModuleExecutionException */ protected void handleFraction(FeatureSet featureSet) throws IOException { String errorMessage = ""; FeatureSet.FeatureSelector sel = new FeatureSet.FeatureSelector(); sel.setMinPProphet(minPeptideProphet); featureSet = featureSet.filter(sel); Arrays.sort(featureSet.getFeatures(), new Feature.ScanAscComparator()); MSRun run = null; File fileToLoad = mzXmlFile; if (fileToLoad == null) { String fractionName = MS2ExtraInfoDef.getFeatureSetBaseName(featureSet); errorMessage = "Error finding mzXML files"; fileToLoad = CommandLineModuleUtilities.findFileWithPrefix(fractionName, mzXmlDir, "mzXML"); } run = MSRun.load(fileToLoad.getAbsolutePath()); _log.debug("\tLoaded mzXML file " + fileToLoad.getAbsolutePath()); if (proteinsToExamine != null) handleProteinsInRun(featureSet, run, proteinsToExamine); else if (peptidesToExamine != null) handlePeptidesInRun(featureSet, run, peptidesToExamine, DUMMY_PROTEIN_NAME, outDir); else if (scan != 0) { String fractionName = MS2ExtraInfoDef.getFeatureSetBaseName(featureSet); if (fractionsToExamine == null || fractionsToExamine.contains(fractionName)) handleScanInRun(featureSet, fractionName, run, scan); } else { _log.debug("\t processing all scans"); //handle all scans String fractionName = MS2ExtraInfoDef.getFeatureSetBaseName(featureSet); List<QuantEvent> allQuantEvents = new ArrayList<QuantEvent>(); for (Feature feature : featureSet.getFeatures()) { if (IsotopicLabelExtraInfoDef.hasRatio(feature)) allQuantEvents.add(new QuantEvent(feature, fractionName)); } if (allQuantEvents.isEmpty()) ApplicationContext.infoMessage("\tSkipping empty fraction " + fractionName); else { List<QuantEvent> nonOverlappingQuantEvents = findNonOverlappingEvents(allQuantEvents); for (QuantEvent quantEvent : nonOverlappingQuantEvents) { handleEvent(run, outDir, DUMMY_PROTEIN_NAME, fractionName, quantEvent); } ApplicationContext.infoMessage("\tProcessed " + nonOverlappingQuantEvents.size() + " non-overlapping events for " + featureSet.getFeatures().length + " features"); } } } /** * Visualize all charts for a particular scan number. Awfully inefficient, but * it doesn't matter because this is only invoked for single-scan runs * @param featureSet * @param fraction * @param run * @throws CommandLineModuleExecutionException */ protected void handleScanInRun(FeatureSet featureSet, String fraction, MSRun run, int scanToHandle) { for (Feature feature : featureSet.getFeatures()) { if (scanToHandle == feature.getScan()) handleFeature(run, outDir, DUMMY_PROTEIN_NAME, fraction, feature); } } /** * Display all passing events for a particular set of proteins * @param featureSet * @param run * @param proteinsToHandle * @throws CommandLineModuleExecutionException */ protected void handleProteinsInRun(FeatureSet featureSet, MSRun run, Set<String> proteinsToHandle) { for (String protein : proteinsToHandle) { Set<String> peptidesThisProtein = new HashSet<String>(); for (Feature feature : featureSet.getFeatures()) { List<String> proteinsThisFeature = MS2ExtraInfoDef.getProteinList(feature); if (proteinsThisFeature != null && proteinsThisFeature.contains(protein)) { String peptide = MS2ExtraInfoDef.getFirstPeptide(feature); { ApplicationContext.infoMessage("Found peptide " + peptide + ", protein " + protein); peptidesThisProtein.add(peptide); } } } if (!peptidesThisProtein.isEmpty()) { ApplicationContext.infoMessage("Protein " + protein + ": Finding " + peptidesThisProtein.size() + " peptides in run " + MS2ExtraInfoDef.getFeatureSetBaseName(featureSet)); File proteinOutDir = new File(outDir, protein); proteinOutDir.mkdir(); handlePeptidesInRun(featureSet, run, peptidesThisProtein, protein, proteinOutDir); } } } /** * Display all passing events for a particular set of peptides * @param featureSet * @param run * @param peptidesToHandle * @param proteinName * @param outputDir * @throws CommandLineModuleExecutionException */ protected void handlePeptidesInRun(FeatureSet featureSet, MSRun run, Set<String> peptidesToHandle, String proteinName, File outputDir) { String fraction = MS2ExtraInfoDef.getFeatureSetBaseName(featureSet); List<QuantEvent> allQuantEventsAllPeptides = new ArrayList<QuantEvent>(); // String labeledResidue = null; // float labelMassDiff = 0; for (Feature feature : featureSet.getFeatures()) { if (peptidesToHandle.contains(MS2ExtraInfoDef.getFirstPeptide(feature)) && MS2ExtraInfoDef.getPeptideProphet(feature) >= this.minPeptideProphet && IsotopicLabelExtraInfoDef.hasRatio(feature)) { // if (labeledResidue == null) // { // AnalyzeICAT.IsotopicLabel label = IsotopicLabelExtraInfoDef.getLabel(feature); // if (label != null) // { // labeledResidue = "" + label.getResidue(); // labelMassDiff = label.getHeavy() - label.getLight(); // _log.debug("Found label: " + labeledResidue + ", " + labelMassDiff); // } // } allQuantEventsAllPeptides.add(new QuantEvent(feature, fraction)); } } // if (labeledResidue == null) // ApplicationContext.infoMessage("WARNING: unable to determine modification used for quantitation. " + // "Cannot collapse light and heavy states."); List<QuantEvent> nonOverlappingEventsAllPeptides = findNonOverlappingQuantEventsAllPeptides( allQuantEventsAllPeptides); for (QuantEvent quantEvent : nonOverlappingEventsAllPeptides) { int numTotalEvents = 1; if (quantEvent.getOtherEvents() != null) numTotalEvents += quantEvent.getOtherEvents().size(); ApplicationContext.infoMessage("\tHandling peptide " + quantEvent.getPeptide() + ", charge " + quantEvent.getCharge() + " with " + numTotalEvents + " events"); handleEvent(run, outputDir, proteinName, fraction, quantEvent); } } /** * Find overlapping events in a list of events that may come from different peptides, fractions, etc. * Return a list with one member of each of those overlapping event lists * @param quantEvents * @return */ public List<QuantEvent> findNonOverlappingQuantEventsAllPeptides(List<QuantEvent> quantEvents) { List<QuantEvent> result = new ArrayList<QuantEvent>(); Map<String, Map<String, Map<Integer, List<QuantEvent>>>> peptideFractionChargeQuantEventsMap = new HashMap<String, Map<String, Map<Integer, List<QuantEvent>>>>(); _log.debug("findNonOverlappingQuantEventsAllPeptides 1"); for (QuantEvent quantEvent : quantEvents) { String peptide = quantEvent.getPeptide(); Map<String, Map<Integer, List<QuantEvent>>> fractionChargesMap = peptideFractionChargeQuantEventsMap .get(peptide); if (fractionChargesMap == null) { fractionChargesMap = new HashMap<String, Map<Integer, List<QuantEvent>>>(); peptideFractionChargeQuantEventsMap.put(peptide, fractionChargesMap); } String fraction = quantEvent.getFraction(); Map<Integer, List<QuantEvent>> chargeEventsMap = fractionChargesMap.get(fraction); if (chargeEventsMap == null) { chargeEventsMap = new HashMap<Integer, List<QuantEvent>>(); fractionChargesMap.put(fraction, chargeEventsMap); } List<QuantEvent> eventsToEvaluate = chargeEventsMap.get(quantEvent.getCharge()); if (eventsToEvaluate == null) { eventsToEvaluate = new ArrayList<QuantEvent>(); chargeEventsMap.put(quantEvent.getCharge(), eventsToEvaluate); } eventsToEvaluate.add(quantEvent); } _log.debug("Built map with " + peptideFractionChargeQuantEventsMap.size() + " peptides"); //combine modification states that represent light and heavy versions of same species. //To do this, first break up everything by peptide, fraction and charge. Then associate everything //within a charge by light mz and find overlapping events within that list. Don't bother looking at //heavy -- we're not dealing with multiple-label scenarios. for (String peptide : peptideFractionChargeQuantEventsMap.keySet()) { _log.debug("Map peptide " + peptide); Map<String, Map<Integer, List<QuantEvent>>> fractionChargesMap = peptideFractionChargeQuantEventsMap .get(peptide); for (String fraction : fractionChargesMap.keySet()) { _log.debug(" Map fraction " + fraction); Map<Integer, List<QuantEvent>> chargeEventsMap = fractionChargesMap.get(fraction); for (int charge : chargeEventsMap.keySet()) { _log.debug(" Map charge " + charge + ", " + chargeEventsMap.get(charge) + " events"); Map<Float, List<QuantEvent>> lightMzEventsMap = new HashMap<Float, List<QuantEvent>>(); for (QuantEvent event : chargeEventsMap.get(charge)) { boolean foundIt = false; for (Float lightMz : lightMzEventsMap.keySet()) { if (Math.abs( event.getLightMz() - lightMz) < AMINOACID_MODIFICATION_EQUALITY_MZ_TOLERANCE) { lightMzEventsMap.get(lightMz).add(event); foundIt = true; break; } } if (!foundIt) { List<QuantEvent> eventList = new ArrayList<QuantEvent>(); eventList.add(event); lightMzEventsMap.put(event.getLightMz(), eventList); } } _log.debug("Collapsing events from " + lightMzEventsMap.size() + " distinct mzs"); for (List<QuantEvent> eventList : lightMzEventsMap.values()) { List<QuantEvent> nonOverlappingList = findNonOverlappingEvents(eventList); if (nonOverlappingList.size() < eventList.size()) _log.debug(" Reduced " + eventList.size() + " events to " + nonOverlappingList.size() + " non-overlapping events."); result.addAll(nonOverlappingList); } } } } // for (String peptide : peptideFractionChargeModsQuantEventsMap.keySet()) // { // Map<String, Map<Integer, Map<String, List<QuantEvent>>>> fractionChargesMap = peptideFractionChargeModsQuantEventsMap.get(peptide); // _log.debug("processing peptide " + peptide + " with " + fractionChargesMap.size() + " fractions"); // // for (String fraction : fractionChargesMap.keySet()) // { // Map<Integer, Map<String, List<QuantEvent>>> chargeModificationsMap = // fractionChargesMap.get(fraction); // for (int charge : chargeModificationsMap.keySet()) // { // Map<String, List<QuantEvent>> modificationsEventsMap = chargeModificationsMap.get(charge); // for (String modifications : modificationsEventsMap.keySet()) // { // List<QuantEvent> eventList = modificationsEventsMap.get(modifications); // _log.debug("\tCharge " + charge + ", mods " + modifications + ": " + eventList.size() + " events"); // result.addAll(findNonOverlappingEvents(eventList)); // } // } // } // } return result; } /** * Find modification states that are equivalent except for modification masses representing the difference * between light and heavy labels. */ // protected List<Pair<String,String>> pairLightAndHeavyModificationStates(Collection<String> modificationStates, // String labeledResidue, float labelMassDiff) // { // ModResidueMassAscComparator modResidueMassAscComparator = new ModResidueMassAscComparator(); // // List<Pair<String,String>> lightHeavyModStatePairs = new ArrayList<Pair<String,String>>(); // Set<String> alreadyAssignedCompareModStates = new HashSet<String>(); // for (String baseModificationState : modificationStates) // { // _log.debug(" pairLightAndHeavy, checking " + baseModificationState); // Map<Integer, List<ModifiedAminoAcid>> baseModStateMap = // MS2ExtraInfoDef.parsePositionModifiedAminoAcidListMapString(baseModificationState); // for (String compareModificationState : modificationStates) // { // if (baseModificationState.equals(compareModificationState) || // alreadyAssignedCompareModStates.contains(compareModificationState)) // continue; // boolean foundDifference = false; // Map<Integer, List<ModifiedAminoAcid>> compareModStateMap = // MS2ExtraInfoDef.parsePositionModifiedAminoAcidListMapString( // compareModificationState); // if (baseModStateMap.size() != compareModStateMap.size()) // { // continue; // } // for (int position : baseModStateMap.keySet()) // { // if (!compareModStateMap.containsKey(position)) // { // foundDifference = true; // break; // } // List<ModifiedAminoAcid> baseModsThisPosition = baseModStateMap.get(position); // List<ModifiedAminoAcid> compareModsThisPosition = compareModStateMap.get(position); // // if (baseModsThisPosition.size() != compareModsThisPosition.size()) // { // foundDifference = true; // break; // } // Collections.sort(baseModsThisPosition, modResidueMassAscComparator); // Collections.sort(compareModsThisPosition, modResidueMassAscComparator); // // for (int i=0; i<baseModsThisPosition.size(); i++) // { // ModifiedAminoAcid baseMod = baseModsThisPosition.get(i); // ModifiedAminoAcid compareMod = compareModsThisPosition.get(i); // if (!baseMod.getAminoAcidAsString().equals(compareMod.getAminoAcidAsString())) // { // foundDifference = true; // break; // } // float absMassDiff = (float) Math.abs(baseMod.getMass() - compareMod.getMass()); // if (absMassDiff > AMINOACID_MODIFICATION_EQUALITY_MASS_TOLERANCE) // { // if ((!baseMod.getAminoAcidAsString().equals(labeledResidue)) || // (absMassDiff - labelMassDiff > AMINOACID_MODIFICATION_EQUALITY_MASS_TOLERANCE)) // { // foundDifference = true; // break; // } // } // } // } // if (foundDifference) // continue; // alreadyAssignedCompareModStates.add(baseModificationState); // alreadyAssignedCompareModStates.add(compareModificationState); // lightHeavyModStatePairs.add( // new Pair<String,String>(baseModificationState, compareModificationState)); // break; // } // } // return lightHeavyModStatePairs; // } protected class ModResidueMassAscComparator implements Comparator<ModifiedAminoAcid> { public int compare(ModifiedAminoAcid o1, ModifiedAminoAcid o2) { int result = (o1.getAminoAcidAsString().compareTo(o2.getAminoAcidAsString())); if (result == 0) { if (o1.getMass() > o2.getMass()) return 1; if (o1.getMass() < o2.getMass()) return -1; return 0; } return result; } } /** * cover method. Turn the features into events, generate the nonoverlapping events, and * return the feature results * @param features * @return */ public List<Feature> findNonOverlappingFeatures(List<Feature> features) { List<QuantEvent> quantEvents = new ArrayList<QuantEvent>(); for (Feature feature : features) quantEvents.add(new QuantEvent(feature, "")); List<QuantEvent> nonOverlappingEvents = findNonOverlappingEvents(quantEvents); List<Feature> result = new ArrayList<Feature>(); for (QuantEvent quantEvent : nonOverlappingEvents) { Feature representativeFeature = quantEvent.getSourceFeature(); if (quantEvent.getOtherEvents() != null && !quantEvent.getOtherEvents().isEmpty()) { List<Spectrum.Peak> otherFeaturesAsPeaks = new ArrayList<Spectrum.Peak>(); for (QuantEvent otherEvent : quantEvent.getOtherEvents()) otherFeaturesAsPeaks.add(otherEvent.getSourceFeature()); representativeFeature.comprised = otherFeaturesAsPeaks .toArray(new Spectrum.Peak[otherFeaturesAsPeaks.size()]); } result.add(representativeFeature); } return result; } /** * Given a list of features (presumably with the same peptide ID, in the same charge and * modification state), collapse them down to a single feature representing each set of * overlapping events * @param quantEvents * @return */ public List<QuantEvent> findNonOverlappingEvents(List<QuantEvent> quantEvents) { List<QuantEvent> result = new ArrayList<QuantEvent>(); while (!quantEvents.isEmpty()) { List<QuantEvent> eventsOverlappingFirst = findEventsOverlappingFirst(quantEvents); QuantEvent firstRepresentative = eventsOverlappingFirst.get(0); firstRepresentative.setOtherEvents(new ArrayList<QuantEvent>()); for (int i = 1; i < eventsOverlappingFirst.size(); i++) firstRepresentative.getOtherEvents().add(eventsOverlappingFirst.get(i)); result.add(firstRepresentative); } return result; } /** * Find all events overlapping in scans with the first event * SIDE EFFECT: Remove them all from eventsWithinCharge * @param eventsWithinCharge * @return */ public List<QuantEvent> findEventsOverlappingFirst(List<QuantEvent> eventsWithinCharge) { if (eventsWithinCharge.size() == 1) { List<QuantEvent> result = new ArrayList<QuantEvent>(eventsWithinCharge); eventsWithinCharge.remove(0); return result; } QuantEvent firstEvent = eventsWithinCharge.get(0); eventsWithinCharge.remove(firstEvent); List<QuantEvent> eventsOverlappingFirst = new ArrayList<QuantEvent>(); eventsOverlappingFirst.add(firstEvent); eventsOverlappingFirst.addAll(findEventsOverlappingEvent(firstEvent, eventsWithinCharge)); //find the "median" feature by scan (round down if even number) Collections.sort(eventsOverlappingFirst, new QuantEvent.ScanAscComparator()); int numOverlapping = eventsOverlappingFirst.size(); int medianEventIndex = (numOverlapping) / 2; if (numOverlapping % 2 == 1) medianEventIndex = (numOverlapping - 1) / 2; List<QuantEvent> sortedForResult = new ArrayList<QuantEvent>(); sortedForResult.add(eventsOverlappingFirst.get(medianEventIndex)); for (int i = 0; i < eventsOverlappingFirst.size(); i++) if (i != medianEventIndex) sortedForResult.add(eventsOverlappingFirst.get(i)); QuantEvent representativeEvent = sortedForResult.get(0); _log.debug("\t" + representativeEvent.getPeptide() + ", fraction " + representativeEvent.getFraction() + ", charge " + representativeEvent.getCharge() + ", ratio " + representativeEvent.getRatio() + ", scans " + representativeEvent.getFirstLightQuantScan() + "-" + representativeEvent.getLastLightQuantScan() + ", represents " + sortedForResult.size() + " event(s)"); if (sortedForResult.size() > 1) for (int i = 1; i < sortedForResult.size(); i++) _log.debug("\t\tother event " + i + ", ratio=" + sortedForResult.get(i).getRatio()); return sortedForResult; } /** * Find all events overlapping in scans with the base event * SIDE EFFECT: Remove them all from otherEvents * @param baseEvent * @param otherEvents * @return */ public List<QuantEvent> findEventsOverlappingEvent(QuantEvent baseEvent, List<QuantEvent> otherEvents) { List<QuantEvent> eventsOverlappingBaseEvent = new ArrayList<QuantEvent>(); if (otherEvents.size() > 0) { //take a broad interpretation of overlap -- overlapping either light or heavy int firstEventStart = Math.min(baseEvent.getFirstLightQuantScan(), baseEvent.getFirstHeavyQuantScan()); int firstEventEnd = Math.max(baseEvent.getLastLightQuantScan(), baseEvent.getLastHeavyQuantScan()); if (firstEventStart <= 0) firstEventStart = baseEvent.getScan(); if (firstEventEnd <= 0) firstEventEnd = baseEvent.getScan(); double firstFeatureRatio = baseEvent.getRatio(); _log.debug("Looking for events overlapping " + firstEventStart + "-" + firstEventEnd + ", ratio " + firstFeatureRatio + ", out of " + otherEvents.size()); for (QuantEvent compareEvent : otherEvents) { int compareFeatureStart = Math.min(compareEvent.getFirstLightQuantScan(), compareEvent.getFirstHeavyQuantScan()); int compareFeatureEnd = Math.max(compareEvent.getLastLightQuantScan(), compareEvent.getLastHeavyQuantScan()); if (compareFeatureStart <= 0) compareFeatureStart = compareEvent.getScan(); if (compareFeatureEnd <= 0) compareFeatureEnd = compareEvent.getScan(); _log.debug("\tChecking " + compareFeatureStart + "-" + compareFeatureEnd + ", ratio " + compareEvent.getRatio()); //first check ratio similarity, because that's simplest if (Math.abs(compareEvent.getRatio() - firstFeatureRatio) > maxCombineFeatureRatioDiff) continue; if (Math.max(firstEventStart, compareFeatureStart) < Math.min(firstEventEnd, compareFeatureEnd)) { _log.debug("\t\tMatch!"); eventsOverlappingBaseEvent.add(compareEvent); } } otherEvents.removeAll(eventsOverlappingBaseEvent); } // result.comprised = otherFeaturesAsPeaks.toArray(new Spectrum.Peak[otherFeaturesAsPeaks.size()]); return eventsOverlappingBaseEvent; } /** * Call PanelWithSpectrumChart to create all the charts for a quantitative event. Save * all the charts. * Writes a row to the output files * @param run * @param outputDir * @param protein * @param fraction * @param feature * @throws CommandLineModuleExecutionException */ protected void handleFeature(MSRun run, File outputDir, String protein, String fraction, Feature feature) { QuantEvent quantEvent = new QuantEvent(feature, fraction); handleEvent(run, outputDir, protein, fraction, quantEvent); } /** * Given a QuantEvent and a run, create the PanelWithSpectrumChart object that will create all the charts, * and generate them * @param run * @param quantEvent * @return */ public PanelWithSpectrumChart createPanelWithSpectrumChart(MSRun run, QuantEvent quantEvent) { int firstLightQuantScan = quantEvent.getFirstLightQuantScan(); int lastLightQuantScan = quantEvent.getLastLightQuantScan(); int firstHeavyQuantScan = quantEvent.getFirstLightQuantScan(); int lastHeavyQuantScan = quantEvent.getLastHeavyQuantScan(); //Add padding around quant event limits int minScanIndex = Math .max(Math.abs(run.getIndexForScanNum(Math.min(firstLightQuantScan, firstHeavyQuantScan))) - numPaddingScans, 0); int maxScanIndex = Math .min(Math.abs(run.getIndexForScanNum(Math.min(lastLightQuantScan, lastHeavyQuantScan))) + numPaddingScans, run.getScanCount() - 1); int minScan = run.getScanNumForIndex(minScanIndex); int maxScan = run.getScanNumForIndex(maxScanIndex); float minMz = quantEvent.getLightMz() - mzPadding; float maxMz = quantEvent.getHeavyMz() + numHeavyPeaksToPlot / quantEvent.getCharge() + mzPadding; _log.debug("Building chart for event:\n\t" + quantEvent); _log.debug("Scan=" + quantEvent.getScan() + ", ratio=" + quantEvent.getRatio() + ", lightInt=" + quantEvent.getLightIntensity() + ", heavyInt=" + quantEvent.getHeavyIntensity() + ", minScanIndex=" + minScanIndex + ", maxScanIndex=" + maxScanIndex + ", minMz=" + minMz + ", maxMz=" + maxMz); //create the PanelWithSpectrumChart and make it generate everything PanelWithSpectrumChart spectrumPanel = new PanelWithSpectrumChart(run, minScan, maxScan, minMz, maxMz, firstLightQuantScan, lastLightQuantScan, firstHeavyQuantScan, lastHeavyQuantScan, quantEvent.getLightMz(), quantEvent.getHeavyMz(), quantEvent.getCharge()); spectrumPanel.setResolution(resolution); spectrumPanel.setGenerateLineCharts(true); spectrumPanel.setGenerate3DChart(show3DPlots); spectrumPanel.setIdEventScan(quantEvent.getScan()); spectrumPanel.setIdEventMz(quantEvent.getMz()); List<Integer> otherEventScans = new ArrayList<Integer>(); List<Float> otherEventMzs = new ArrayList<Float>(); if (quantEvent.getOtherEvents() != null) for (QuantEvent otherEvent : quantEvent.getOtherEvents()) { otherEventScans.add(otherEvent.getScan()); otherEventMzs.add(otherEvent.getMz()); } spectrumPanel.setOtherEventScans(otherEventScans); spectrumPanel.setOtherEventMZs(otherEventMzs); spectrumPanel.setName("Spectrum"); spectrumPanel.setContourPlotRotationAngle(rotationAngle3D); spectrumPanel.setContourPlotTiltAngle(tiltAngle3D); spectrumPanel.setContourPlotWidth(imageWidth3D); spectrumPanel.setContourPlotHeight(imageHeight3D); spectrumPanel.setContourPlotShowAxes(show3DAxes); spectrumPanel.setSize(new Dimension(imageWidth, spectrumImageHeight)); spectrumPanel.setPeakSeparationMass(peakSeparationMass); spectrumPanel.setPeakTolerancePPM(peakTolerancePPM); spectrumPanel.generateCharts(); spectrumPanel.setVisible(true); spectrumPanel.setMinimumSize(new Dimension(imageWidth, spectrumImageHeight)); return spectrumPanel; } /** * Side effect! Sets QuantEvent.ratioOnePeak, but only if charts are created * @param run * @param outputDir * @param protein * @param fraction * @param quantEvent */ protected void handleEvent(MSRun run, File outputDir, String protein, String fraction, QuantEvent quantEvent) { if (markAllEventsBad) { quantEvent.setQuantCurationStatus(QuantEvent.CURATION_STATUS_BAD); } if (shouldAssessEvents) { //only assess if not already assessed if (quantEvent.getAlgorithmicAssessment() == null) { QuantEventAssessor eventAssessor = new QuantEventAssessor(); // eventAssessor.setLabelType(labelType); eventAssessor.assessQuantEvent(quantEvent, run); //System.err.println("*******ASSESSED!!!!! " + quantEvent.getAlgorithmicAssessment()); } //else System.err.println("NOT ASSESSING!"); } if (shouldCreateCharts || outTurkPW != null) { String filePrefix = quantEvent.getPeptide() + "_" + fraction + "_" + quantEvent.getCharge() + "_" + quantEvent.getScan(); //chart output files if (quantEvent.getSpectrumFile() == null) quantEvent.setSpectrumFile(new File(outputDir, filePrefix + "_spectrum.png")); if (quantEvent.getScansFile() == null) quantEvent.setScansFile(new File(outputDir, filePrefix + "_scans.png")); if (quantEvent.getIntensitySumFile() == null) quantEvent.setIntensitySumFile(new File(outputDir, filePrefix + "_intensitysum.png")); if (quantEvent.getFile3D() == null) quantEvent.setFile3D(new File(outputDir, filePrefix + "_3D.png")); PanelWithSpectrumChart spectrumPanel = createPanelWithSpectrumChart(run, quantEvent); //The single-peak ratio calculated by QuantEventAssessor is better than the slapdash one I calculate //in spectrumPanel, so if we've got that, use it. if (quantEvent.getAlgorithmicAssessment() != null) quantEvent.setRatioOnePeak(quantEvent.getAlgorithmicAssessment().getSinglePeakRatio()); else quantEvent.setRatioOnePeak(spectrumPanel.getRatioOnePeak()); Map<Integer, PanelWithLineChart> scanChartMap = spectrumPanel.getScanLineChartMap(); List<Integer> allScans = new ArrayList<Integer>(scanChartMap.keySet()); Collections.sort(allScans); if (outTurkPW != null) { try { outTurkPW.println(saveTurkImage(quantEvent, spectrumPanel, outDir, currentTurkID)); outTurkPW.flush(); currentTurkID++; } catch (Exception e) { throw new RuntimeException("Failed to save image file", e); } } if (shouldCreateCharts) { //Save the chart images, with sidebar data in case we need it. Clunky. File currentImageFile = new File(""); try { currentImageFile = quantEvent.getSpectrumFile(); saveChartToImageFile(quantEvent, spectrumPanel, currentImageFile, writeInfoOnCharts, 0, 0, false); ApplicationContext.infoMessage( "Wrote spectrum to image " + quantEvent.getSpectrumFile().getAbsolutePath()); currentImageFile = quantEvent.getIntensitySumFile(); saveChartToImageFile(quantEvent, spectrumPanel.getIntensitySumChart(), currentImageFile, writeInfoOnCharts, 0, 0, false); ApplicationContext.infoMessage( "Wrote intensity sum to image " + quantEvent.getIntensitySumFile().getAbsolutePath()); currentImageFile = quantEvent.getScansFile(); spectrumPanel.savePerScanSpectraImage(imageWidth, scanImageHeight, maxScansImageHeight, currentImageFile); ApplicationContext .infoMessage("Wrote scans to image " + quantEvent.getScansFile().getAbsolutePath()); } catch (Exception e) { throw new RuntimeException("Failed to save image file " + currentImageFile.getAbsolutePath(), e); } } if (show3DPlots) { try { saveChartToImageFile(quantEvent, spectrumPanel.getContourPlot(), quantEvent.getFile3D(), writeInfoOnCharts, 0, 0, false); ApplicationContext .infoMessage("Wrote 3D plot to image " + quantEvent.getFile3D().getAbsolutePath()); } catch (Exception e) { throw new RuntimeException("Failed to save 3D image file", e); } } //record event Map<String, Map<String, Map<Integer, List<Pair<File, File>>>>> peptideFractionChargeFilesMap = proteinPeptideFractionChargeFilesMap .get(protein); if (peptideFractionChargeFilesMap == null) { peptideFractionChargeFilesMap = new HashMap<String, Map<String, Map<Integer, List<Pair<File, File>>>>>(); proteinPeptideFractionChargeFilesMap.put(protein, peptideFractionChargeFilesMap); } Map<String, Map<Integer, List<Pair<File, File>>>> fractionChargeFilesMap = peptideFractionChargeFilesMap .get(quantEvent.getPeptide()); if (fractionChargeFilesMap == null) { fractionChargeFilesMap = new HashMap<String, Map<Integer, List<Pair<File, File>>>>(); peptideFractionChargeFilesMap.put(quantEvent.getPeptide(), fractionChargeFilesMap); } Map<Integer, List<Pair<File, File>>> chargeFilesMap = fractionChargeFilesMap.get(fraction); if (chargeFilesMap == null) { chargeFilesMap = new HashMap<Integer, List<Pair<File, File>>>(); fractionChargeFilesMap.put(fraction, chargeFilesMap); } List<Pair<File, File>> filesList = chargeFilesMap.get(quantEvent.getCharge()); if (filesList == null) { filesList = new ArrayList<Pair<File, File>>(); chargeFilesMap.put(quantEvent.getCharge(), filesList); } filesList.add(new Pair<File, File>(quantEvent.getSpectrumFile(), quantEvent.getScansFile())); //Now is a VERY good time to do GC System.gc(); } //end if shouldCreateCharts String outChartsRelativeDirName = ""; if (proteinsToExamine != null) outChartsRelativeDirName = protein + File.separatorChar; if (writeHTMLAndText) { outHtmlPW.println( quantEvent.createOutputRowHtml(outChartsRelativeDirName, showProteinColumn, show3DPlots)); outHtmlPW.flush(); outTsvPW.println(quantEvent.createOutputRowTsv(showProteinColumn, show3DPlots)); outTsvPW.flush(); } } /** * Save the intensity-sum image to a file, appropriately sized for the Mechanical Turk. * Return a String that's appropriate to be one line of a turk HIT file * @param quantEvent * @param spectrumPanel * @param outDir * @param turkId * @return * @throws IOException */ public String saveTurkImage(QuantEvent quantEvent, PanelWithSpectrumChart spectrumPanel, File outDir, int turkId) throws IOException { String imageFileName = TurkUtilities.createTurkImageFileName(turkId); saveChartToImageFile(quantEvent, spectrumPanel.getIntensitySumChart(), new File(outDir, imageFileName), true, turkChartWidth, turkChartHeight, true); return TurkUtilities.createTurkHITFileLine(quantEvent, turkId, turkImageURLPrefix); } public static PanelWithPeakChart buildTheoreticalPeakChart(QuantEvent quantEvent, int chartWidth, int chartHeight) { return buildTheoreticalPeakChart(quantEvent.getLightMz(), quantEvent.getHeavyMz(), quantEvent.getCharge(), quantEvent.getRatio(), chartWidth, chartHeight); } /** * Build a peaks chart showing the theoretical distribution, assuming the ratio is correct * @param lightMz * @param heavyMz * @param charge * @param ratio * @param chartWidth * @param chartHeight * @return */ public static PanelWithPeakChart buildTheoreticalPeakChart(float lightMz, float heavyMz, int charge, float ratio, int chartWidth, int chartHeight) { float lightNeutralMass = (lightMz - Spectrum.HYDROGEN_ION_MASS) * charge; //Hardcoded 6 is number of peakd returned by Poisson() //Can't just use the result of Poisson(), as that's a static array and we're gonna mess with it float[] lightTheoreticalPeaks = new float[6]; System.arraycopy(Spectrum.Poisson(lightNeutralMass), 0, lightTheoreticalPeaks, 0, lightTheoreticalPeaks.length); //System.err.println("***LIGHT, mz=" + lightMz + ", mass=" + lightNeutralMass + ", charge=" + charge); //for (float peak : lightTheoreticalPeaks) // System.err.println("\t" + peak); float[] lightPeakMzs = new float[6]; for (int i = 0; i < 6; i++) lightPeakMzs[i] = lightMz + (Spectrum.HYDROGEN_ION_MASS * i / charge); PanelWithPeakChart theoreticalPeaksChart = new PanelWithPeakChart(lightPeakMzs, lightTheoreticalPeaks, "Theoretical Peaks"); float heavyNeutralMass = (heavyMz - Spectrum.HYDROGEN_ION_MASS) * charge; float[] heavyTheoreticalPeaks = new float[6]; System.arraycopy(Spectrum.Poisson(heavyNeutralMass), 0, heavyTheoreticalPeaks, 0, heavyTheoreticalPeaks.length); //fix 0 ratios at 0.001 to avoid divide by 0 for (int i = 0; i < heavyTheoreticalPeaks.length; i++) heavyTheoreticalPeaks[i] *= 1 / Math.max(ratio, 0.001f); float[] heavyPeakMzs = new float[6]; for (int i = 0; i < 6; i++) heavyPeakMzs[i] = heavyMz + (Spectrum.HYDROGEN_ION_MASS * i / charge); //Adjust heavy peaks if light peaks intrude. Light appears in front of heavy for (int i = 0; i < heavyPeakMzs.length; i++) { for (int j = 0; j < lightPeakMzs.length; j++) if (heavyPeakMzs[i] - lightPeakMzs[j] < 0.1) heavyTheoreticalPeaks[i] += lightTheoreticalPeaks[j]; } //System.err.println("***HEAVY, mz=" + heavyMz + ", mass=" + heavyNeutralMass); //for (float peak : heavyTheoreticalPeaks) // System.err.println("\t" + peak); theoreticalPeaksChart.addData(heavyPeakMzs, heavyTheoreticalPeaks, "heavy"); theoreticalPeaksChart.setPreferredSize(new Dimension(chartWidth, chartHeight)); theoreticalPeaksChart.setSize(new Dimension(chartWidth, chartHeight)); theoreticalPeaksChart.getChart().removeLegend(); //remove axes from chart ((XYPlot) theoreticalPeaksChart.getPlot()).getDomainAxis().setVisible(false); ((XYPlot) theoreticalPeaksChart.getPlot()).getRangeAxis().setVisible(false); return theoreticalPeaksChart; } /** * Cover method, harvests everything from quantEvent. Still silly, because quantEvent is also passed * @param quantEvent * @param chartPanel * @throws IOException */ public void saveChartToImageFile(QuantEvent quantEvent, PanelWithChart chartPanel, File outputFile, boolean writeInfo, int width, int height, boolean overrideSize) throws IOException { saveChartToImageFile(chartPanel, outputFile, sidebarWidth, quantEvent.getCharge(), quantEvent.getLightMz(), quantEvent.getHeavyMz(), quantEvent.getRatio(), writeInfo, width, height, overrideSize); } /** * Save chart to an image file, with or without the sidebar information and/or theoretical peaks * @param chartPanel * @param outFile * @param sidebarWidth * @param charge * @param lightMz * @param heavyMz * @param ratio * @throws IOException */ public void saveChartToImageFile(PanelWithChart chartPanel, File outFile, int sidebarWidth, int charge, float lightMz, float heavyMz, float ratio, boolean writeChartInfo, int width, int height, boolean overrideSize) throws IOException { BufferedImage spectrumImage = null; if (overrideSize) spectrumImage = chartPanel.createImage(width, height); else spectrumImage = chartPanel.createImage(); BufferedImage imageToWrite = spectrumImage; if (writeChartInfo) { int fullImageWidth = spectrumImage.getWidth() + sidebarWidth; imageToWrite = new BufferedImage(fullImageWidth, spectrumImage.getHeight(), BufferedImage.TYPE_INT_RGB); Graphics2D g = imageToWrite.createGraphics(); g.drawImage(spectrumImage, sidebarWidth, 0, null); //write in sidebar int lineHeight = 20; int lineNum = 1; int indent = 5; if (writeChartInfo) { g.setPaint(Color.WHITE); // g.drawString(peptide, indent, lineNum++ * lineHeight); // g.drawString("Charge=" + charge, indent, lineNum++ * lineHeight); // g.drawString("Light mass=" + lightMass, indent, lineNum++ * lineHeight); // g.drawString("Light m/z=" + lightMz, indent, lineNum++ * lineHeight); // g.drawString("Heavy m/z=" + heavyMz, indent, lineNum++ * lineHeight); // g.drawString("Light int=" + lightIntensity, indent, lineNum++ * lineHeight); // g.drawString("Heavy int=" + heavyIntensity, indent, lineNum++ * lineHeight); g.drawString("Ratio=" + ratio, indent, lineNum++ * lineHeight); // g.drawString("MinscanLt=" + lightMinQuantScan, indent, lineNum++ * lineHeight); // g.drawString("MaxscanLt=" + lightMaxQuantScan, indent, lineNum++ * lineHeight); // g.drawString("MinScanHv=" + heavyMinQuantScan, indent, lineNum++ * lineHeight); // g.drawString("MaxScanHv=" + heavyMaxQuantScan, indent, lineNum++ * lineHeight); // g.drawString("ID scan=" + idScan, indent, lineNum++ * lineHeight); // g.drawString("IDscan level=" + idScanLevel, indent, lineNum++ * lineHeight); //theoretical peaks in bottom left int theoreticalPeaksHeight = (int) (sidebarWidth * 2.0 / 3.0); int theoreticalPeaksTop = spectrumImage.getHeight() - theoreticalPeaksHeight - 10; g.drawString("Ideal Peaks", indent, theoreticalPeaksTop - 20); //combined theoretical peak distribution chart PanelWithPeakChart theoreticalPeakChart = buildTheoreticalPeakChart(lightMz, heavyMz, charge, ratio, sidebarWidth, (int) (sidebarWidth * 2.0 / 3.0)); g.drawImage(theoreticalPeakChart.createImage(sidebarWidth, (int) (sidebarWidth * 2.0 / 3.0)), 0, theoreticalPeaksTop, null); } g.dispose(); } ImageIO.write(imageToWrite, "png", outFile); } public int getResolution() { return resolution; } public void setResolution(int resolution) { this.resolution = resolution; } public int getNumHeavyPeaksToPlot() { return numHeavyPeaksToPlot; } public void setNumHeavyPeaksToPlot(int numHeavyPeaksToPlot) { this.numHeavyPeaksToPlot = numHeavyPeaksToPlot; } public File getOutDir() { return outDir; } public void setOutDir(File outDir) { this.outDir = outDir; } public int getScanImageHeight() { return scanImageHeight; } public void setScanImageHeight(int scanImageHeight) { this.scanImageHeight = scanImageHeight; } public int getMaxScansImageHeight() { return maxScansImageHeight; } public void setMaxScansImageHeight(int maxScansImageHeight) { this.maxScansImageHeight = maxScansImageHeight; } public int getSpectrumImageHeight() { return spectrumImageHeight; } public void setSpectrumImageHeight(int spectrumImageHeight) { this.spectrumImageHeight = spectrumImageHeight; } public int getImageWidth() { return imageWidth; } public void setImageWidth(int imageWidth) { this.imageWidth = imageWidth; } public File getMzXmlDir() { return mzXmlDir; } public void setMzXmlDir(File mzXmlDir) { this.mzXmlDir = mzXmlDir; } public float getMaxCombineFeatureRatioDiff() { return maxCombineFeatureRatioDiff; } public void setMaxCombineFeatureRatioDiff(float maxCombineFeatureRatioDiff) { this.maxCombineFeatureRatioDiff = maxCombineFeatureRatioDiff; } public boolean isWriteHTMLAndText() { return writeHTMLAndText; } public void setWriteHTMLAndText(boolean writeHTMLAndText) { this.writeHTMLAndText = writeHTMLAndText; } public float getMinPeptideProphet() { return minPeptideProphet; } public void setMinPeptideProphet(float minPeptideProphet) { this.minPeptideProphet = minPeptideProphet; } public File getMzXmlFile() { return mzXmlFile; } public void setMzXmlFile(File mzXmlFile) { this.mzXmlFile = mzXmlFile; } public int getNumPaddingScans() { return numPaddingScans; } public void setNumPaddingScans(int numPaddingScans) { this.numPaddingScans = numPaddingScans; } public float getMzPadding() { return mzPadding; } public void setMzPadding(float mzPadding) { this.mzPadding = mzPadding; } public Set<String> getPeptidesFound() { return peptidesFound; } public void setPeptidesFound(Set<String> peptidesFound) { this.peptidesFound = peptidesFound; } public int getSidebarWidth() { return sidebarWidth; } public void setSidebarWidth(int sidebarWidth) { this.sidebarWidth = sidebarWidth; } public Map<String, Map<String, Map<String, Map<Integer, List<Pair<File, File>>>>>> getProteinPeptideFractionChargeFilesMap() { return proteinPeptideFractionChargeFilesMap; } public void setProteinPeptideFractionChargeFilesMap( Map<String, Map<String, Map<String, Map<Integer, List<Pair<File, File>>>>>> proteinPeptideFractionChargeFilesMap) { this.proteinPeptideFractionChargeFilesMap = proteinPeptideFractionChargeFilesMap; } public PrintWriter getOutHtmlPW() { return outHtmlPW; } public void setOutHtmlPW(PrintWriter outHtmlPW) { this.outHtmlPW = outHtmlPW; } public PrintWriter getOutTsvPW() { return outTsvPW; } public void setOutTsvPW(PrintWriter outTsvPW) { this.outTsvPW = outTsvPW; } public boolean isShow3DPlots() { return show3DPlots; } public void setShow3DPlots(boolean show3DPlots) { this.show3DPlots = show3DPlots; } public int getRotationAngle3D() { return rotationAngle3D; } public void setRotationAngle3D(int rotationAngle3D) { this.rotationAngle3D = rotationAngle3D; } public int getTiltAngle3D() { return tiltAngle3D; } public void setTiltAngle3D(int tiltAngle3D) { this.tiltAngle3D = tiltAngle3D; } public int getImageHeight3D() { return imageHeight3D; } public void setImageHeight3D(int imageHeight3D) { this.imageHeight3D = imageHeight3D; } public int getImageWidth3D() { return imageWidth3D; } public void setImageWidth3D(int imageWidth3D) { this.imageWidth3D = imageWidth3D; } public boolean isShow3DAxes() { return show3DAxes; } public void setShow3DAxes(boolean show3DAxes) { this.show3DAxes = show3DAxes; } public int getScan() { return scan; } public void setScan(int scan) { this.scan = scan; } public Set<String> getPeptidesToExamine() { return peptidesToExamine; } public void setPeptidesToExamine(Set<String> peptidesToExamine) { this.peptidesToExamine = peptidesToExamine; } public Set<String> getProteinsToExamine() { return proteinsToExamine; } public void setProteinsToExamine(Set<String> proteinsToExamine) { this.proteinsToExamine = proteinsToExamine; } public Set<String> getFractionsToExamine() { return fractionsToExamine; } public void setFractionsToExamine(Set<String> fractionsToExamine) { this.fractionsToExamine = fractionsToExamine; } public Iterator<FeatureSet> getFeatureSetIterator() { return featureSetIterator; } public void setFeatureSetIterator(Iterator<FeatureSet> featureSetIterator) { this.featureSetIterator = featureSetIterator; } public boolean isWriteInfoOnCharts() { return writeInfoOnCharts; } public void setWriteInfoOnCharts(boolean writeInfoOnCharts) { this.writeInfoOnCharts = writeInfoOnCharts; } public boolean isWriteTheoreticalPeaksOnCharts() { return writeTheoreticalPeaksOnCharts; } public void setWriteTheoreticalPeaksOnCharts(boolean writeTheoreticalPeaksOnCharts) { this.writeTheoreticalPeaksOnCharts = writeTheoreticalPeaksOnCharts; } public File getOutHtmlFile() { return outHtmlFile; } public void setOutHtmlFile(File outHtmlFile) { this.outHtmlFile = outHtmlFile; } public File getOutTsvFile() { return outTsvFile; } public File getOutTurkFile() { return outTurkFile; } public void setOutTurkFile(File outTurkFile) { this.outTurkFile = outTurkFile; } public void setOutTsvFile(File outTsvFile) { this.outTsvFile = outTsvFile; } public float getPeakSeparationMass() { return peakSeparationMass; } public void setPeakSeparationMass(float peakSeparationMass) { this.peakSeparationMass = peakSeparationMass; } public float getPeakTolerancePPM() { return peakTolerancePPM; } public void setPeakTolerancePPM(float peakTolerancePPM) { this.peakTolerancePPM = peakTolerancePPM; } public boolean isAppendTsvOutput() { return appendTsvOutput; } public void setAppendTsvOutput(boolean appendTsvOutput) { this.appendTsvOutput = appendTsvOutput; } public void addProgressListener(ActionListener listener) { dummyProgressButton.addActionListener(listener); } public boolean isShouldCreateCharts() { return shouldCreateCharts; } public void setShouldCreateCharts(boolean shouldCreateCharts) { this.shouldCreateCharts = shouldCreateCharts; } public boolean isMarkAllEventsBad() { return markAllEventsBad; } public void setMarkAllEventsBad(boolean markAllEventsBad) { this.markAllEventsBad = markAllEventsBad; } public String getTurkImageURLPrefix() { return turkImageURLPrefix; } public void setTurkImageURLPrefix(String turkImageURLPrefix) { this.turkImageURLPrefix = turkImageURLPrefix; } }