wiki:CreatingAnnotion

Steps to Create a GRITS Project Archive

The GRITS plugin "org.grits.toolbox.importer.ms.annotation.glycan.simiansearch" executes a tool title GELATO, which annotates MS spectra with candidate glycans from a specified database. The GELATO plugin is "org.grits.toolbox.ms.annotation". This plugin creates an archive that is readable by the GRITS GUI component (plugins "org.grits.toolbox.entry.ms.annotation" and "org.grits.toolbox.entry.ms.annotation.glycan"). While GELATO's algorithms perform the matching of peaks in MS spectra to glycans, the archive that is created is an instance of the GRITS MS object model (plugin "org.grits.toolbox.ms.om"). In order to extend GRITS to other forms of MS data, it will be helpful to understand the requirements for creation of an archive file that can be opened in GRITS. This document is meant to serve that purpose.

Minimal steps to create a GRITS MS Glycan Annotation archive

Step one: populating the data structures with MS data. The default data structure for GELATO is a HashMap<Integer, Scan> where the Integer is the scan number and the Scan object is an instance of org.grits.toolbox.ms.om.data.Scan. GRITS supports a variety of MS types: Direct Infusion (DI), Total Ion Mapping (TIM), LC-MS/MS, and MS Profile. For DI, TIM, and LC-MS/MS, it is assumed that you will have at least 1 MS1 scan and 1 MS/MS scan. For MS Profile, you will only need an MS1 scan.

Assuming a Direct Infusion experiment, the minimum data for GELATO is:

Populating the Scan Data

1) Adding MS Data

a) Create an MS1 scan (example code)

   Scan ms1scan = new Scan();  // create new Scan object
   ms1scan.setScanNo(1);  // set the scan number to 1
   ms1scan.setMsLevel(1); // set the MS level to 1
   List<Peak> peakList = new ArrayList<>();  // create a new peak list for the MS1 scan
   Peak precursorPeak = new Peak();  // create a new peak that will be a precursor peak (generates an MS/MS event)
   precursorPeak.setMz(528.2);  // set the m/z value of the peak (could be anything)
   precursorPeak.setIntensity(10000.0);  // set the intensity to something > 0
   precursorPeak.setIsPrecursor(true);  // identify the peak as a precursor peak
   precursorPeak.setId(1);  // the peak MUST have a unique ID
   precursorPeak.setPrecursorMz(528.18);  // when the ion in the MS1 is trapped for fragmentation, this is the m/z of the ions trapped...could slightly differ from the peak in the full MS1 scan
   peakList.add(precursorPeak);  // add the precursor peak to the peak list
   ms1scan.setPeaklist(peakList);  // set the peak list of the full MS1 scan to the newly created peak list

b) Create an MS2 scan (example code)

   Scan ms2scan = new Scan();  // create new Scan object
   ms2scan.setScanNo(2);  // set the scan number to 2
   ms2scan.setMsLevel(2);  // set the MS level to 2
   ms2scan.setPrecursor(precursorPeak);  // set the precursor variable to the precursorPeak created above. This ties the MS2 scan to the precursor peak in the parent MS1 scan
   List<Peak> peakList = new ArrayList<>();  // if you want to id fragment peaks, create a new peak list for the MS2 scan
   Peak fragPeak = new Peak(); // create a fragment peak
   fragPeak.setMz(940.2);
   fragPeak.setIntensity(5000.0);
   fragPeak.setIsPrecursor(false);
   fragPeak.setId(1);
   peakList.add(fragPeak);
   ms2scan.setPeaklist(peakList);
   ms2scan.setParentScan(1);   // set the parent scan of this MS/MS scan to the full MS scan

c) Populate the MS1 object with subscans

   List<Integer> subScans = new ArrayList<>();   // create a list of integers to contain all scan numbers that were subscans of the full MS1 scan
   subScans.add(2);  // scan number 2 is the only subscan in this example, so add the number 2
   ms1scan.setSubScans(subScans);  // add the subscan list to the full MS1 scan

d) Add the scans to the Hashmap datastructure

   HashMap<Integer, Scan> testData = new HashMap<>(); // create the data structure to be used by Gelato
   testData.put(1, ms1scan); // put the ms1 scan in the Hashmap, key is scan number 1
   testData.put(2, ms2scan); // put the ms2 scan in the Hashmap, key is scan number 2

Initialize Glycan Database

2) Creating Glycan Objects

a) * You must first initialize the EuroCarbDB GlycanBuilder BuilderWorkspace even though you never use the instance

BuilderWorkspace bw = new BuilderWorkspace(new GlycanRendererAWT());

b) Create a test GlycanStructure object and a corresponding Glycan object. I'm using a static GlycoWorkbench (GWB) formatted sequence

   private static String GLYCAN_SEQUENCE1 = "freeEnd--??1D-GlcNAc,p--??1D-GlcNAc,p--??1D-Man,p(--??1D-Man,p--??1D-Man,p--??1D-Man,p--??1D-Glc,p--??1D-Glc,p)--??1D-Man,p(--??1D-Man,p--??1D-Man,p)--??1D-Man,p--??1D-Man,p$MONO,perMe,0,0,freeEnd";
   private static String GLYCAN_ID1 = "TEST_GOG1";
   
   private void createTestGlycanObjects() {
      testGlycanStructure1 = new GlycanStructure();   // create the GlycanStructure object
      testGlycanStructure1.setGWBSequence(GLYCAN_SEQUENCE1); // set the sequence
      testGlycanStructure1.setId(GLYCAN_ID1);  // set its ID

      testGlycan1 = Glycan.fromString(testGlycanStructure1.getGWBSequence());  // create the Glycan object using the "Glycan.fromString( <GWB Seq> )" method
      testGlycanStructure1.setSequence( testGlycan1.toGlycoCTCondensed() );   // set the alternative sequence (using GlycoCT Condensed)
      testGlycanStructure1.setSequenceFormat(GlycanAnnotation.SEQ_FORMAT_GLYCOCT_CONDENSED);  // set the type of sequence
   }

Storing the User-specified Parameters

3) Create Method object and populate with applicable settings

   private void createTestMethod() {
      Method testMethod = new Method();

      // Set the MS Method type. Options: 
      /* (from the Method.java class)
        public static final String MS_TYPE_INFUSION = "Direct Infusion";
        public static final String MS_TYPE_LC = "LC-MS/MS";
        public static final String MS_TYPE_TIM = "Total Ion Mapping (TIM)";
        public static final String MS_TYPE_MSPROFILE = "MS Profile";
      */ 
      testMethod.setMsType(Method.MS_TYPE_INFUSION);

      // Right now, we only really support generic "glycan"
      testMethod.setAnnotationType( Method.ANNOTATION_TYPE_GLYCAN );

      // set accuracy information
      testMethod.setAccuracy( 1.0 );
      testMethod.setAccuracyPpm(false); // false means daltons

      testMethod.setFragAccuracy( 500.0 ); 
      testMethod.setFragAccuracyPpm( true ); // true means PPM

      testMethod.setShift(0.0); // not sure what this is for but you have to set it

      setMethod( testMethod ); // store the new method object in your class
   }

Data objects

4) Create Data and DataHeader objects

   private void createTestData() {
      Data testData = new Data();	// Data is part of GRITS object model. It is top-level object for linking MS data to annotation information	
      DataHeader dataHeader = new DataHeader(); // DataHeader, also part of object Model, tracks meta-data, in particular the CustomExtraData that are associated with a project
      dataHeader.setMethod( getMethod() );  // the testMethod was set in step 3. Store it in the DataHeader
      testData.setDataHeader(dataHeader);  // store the DataHeader in the Data object.
      setData( testData ); // keep track of new data object in your class
   }

Create Parameter Sets for Use in Calculating Candidate Glycans/Fragments for Matching to MS Peaks

5) Create AnalyteSettings object

   private void createTestAnalyteSettings() {
     AnalyteSettings testAnalyteSettings = new AnalyteSettings();
     GlycanSettings glycanSettings = new GlycanSettings(); 
     testAnalyteSettings.setGlycanSettings(glycanSettings);
     setAnalyteSettings( testAnalyteSettings );
   }

Determine Adduct Options

6) create list of adducts to consider

a) * Note that class GlycanPreDefinedOptions has static, pre-defined glycan-specific options (see comments)

   private void createTestAdducts() {
      List<IonSettings> testAdductsToAnalyze = new ArrayList<>();

      /*
        public class GlycanPreDefinedOptions {
	  public static Ion ION_ADDUCT_HYDROGEN = new Ion("H", new Double(1.007825032), "Hydrogen", Integer.valueOf(1), Boolean.TRUE);
	  public static Ion ION_ADDUCT_SODIUM = new Ion("Na", new Double(22.989769670), "Sodium", Integer.valueOf(1), Boolean.TRUE);
	  public static Ion ION_ADDUCT_POTASSIUM = new Ion("K", new Double(38.963706900), "Potassium", Integer.valueOf(1), Boolean.TRUE);
	  public static Ion ION_ADDUCT_CHLORINE = new Ion("Cl", new Double(34.968852710), "Chlorine", Integer.valueOf(1), Boolean.FALSE);
	  public static Ion ION_ADDUCT_LITHIUM = new Ion("Li", new Double(7.016004000), "Lithium", Integer.valueOf(1), Boolean.TRUE);
	  public static Ion ION_ADDUCT_ELECTRON = new Ion("e", new Double(0.0005486), "electron", Integer.valueOf(1), Boolean.TRUE);
	  public static Ion ION_ADDUCT_NEGHYDROGEN = new Ion("-H", new Double(-1.007825032), "Negative mode Hydrogen", Integer.valueOf(1), Boolean.FALSE);
	  public static Ion ION_ADDUCT_CALCIUM = new Ion("Ca", new Double(39.9625906), "Calcium", Integer.valueOf(2), Boolean.TRUE);
          ......
        }
      */
      testAdductsToAnalyze.add( GlycanPreDefinedOptions.ION_ADDUCT_SODIUM );  // add pre-defined sodium adduct
      
      IonSettings myAdduct = new IonSettings("TestAdduct", 1.0, "Test Glycan Adduct", 2, Boolean.TRUE);	// create your own adduct
      testAdductsToAnalyze.add( myAdduct );

      List<Integer> testAdductsToAnalyzeCnts = new ArrayList<>(); // the software must know how many adducts (and thus charges) to consider when matching
      testAdductsToAnalyzeCnts.add( Integer.valueOf(4) ); // assumes up to 4 sodiums since sodium is first element of testAdductsToAnalyze list
      testAdductsToAnalyzeCnts.add( Integer.valueOf(1) ); // assumes up to 1 myAdduct since sodium is second element of testAdductsToAnalyze list

      setAdducts( testAdductsToAnalyze ); // store the list of adducts in your class
      setAdductCounts( testAdductsToAnalyzeCnts); // store the list of adduct counts in your class
   }

Set Project Path and File Names

7) Set the final name of the archive

  • Setting the archive name / paths is an important consideration. For LC-MS/MS data, it is different (needs an overview file)!
       protected void setArchiveFilePaths() {
          this.sArchiveName = AnnotationWriter.getArchiveFilePath(getProjectName());  // the ArchiveWriter controls the file extension, etc, so there is a method to create final name
       }
    

Match Glycan Features to MS Peaks

8) Process data to annotate spectra

a) This is a sample method to annotate a single MS/MS spectra to a specified glycan structure

   /* Method declaration. 
      Params:  iParentScan == MS1 scan,  iSubScanNum == MS2 scan
      Assumption:  iParentScan is parent of iSubScan. Scan iSubScan is in iParentScan's subscan list
   private boolean processStructure( int iParentScan, int iSubScanNum ) {

     GlycanScansAnnotation glycanScanAnnotation = new GlycanScansAnnotation(); // Instantiate GlycanScansAnnotation object
     GlycanAnnotation glycanAnnotation = new GlycanAnnotation(); // Instantiate GlycanAnnotation object

     // note that the GlycanScanAnnotation and GlycanAnnotation objects have the same id!
     glycanScanAnnotation.setAnnotationId(getCurrentAnnotationId()); // Set new annotation id. This can be incrementing integer valuer
     glycanAnnotation.setId(getCurrentAnnotationId()); // Set the Id of the glycanAnnotation 		
     setCurrentAnnotationId(getCurrentAnnotationId() + 1);  // now increment the id so the next time it is a new value

     // initialize values in glycanScanAnnotation object
     glycanScanAnnotation.setGlycanId(testGlycanStructure1.getId()); // set the glycan id of the structure to match
     getGlycanIDs().add(testGlycanStructure1.getId()); // 

     // initialize values in glycanAnnotation object
     glycanAnnotation.setGlycanId(testGlycanStructure1.getId());
     glycanAnnotation.setSequenceGWB(testGlycanStructure1.getGWBSequence());
     glycanAnnotation.setSequence(testGlycanStructure1.getSequence());

     // Set other glycan-specific options, if desired. Not required to create functioning archive
     //  glycanAnnotation.setPerDerivatisationType( "....." ); 
     //  glycanAnnotation.setReducingEnd( "....." );

		int iCurrentFeatureId = getTestData().getFeatureIndex();				
		//		boolean bRes = matchGlycanStructures(glycanScanAnnotation, glycanAnnotation);

		boolean bRes = matchSingleSubScan(iParentScan, iSubScanNum, iStrucNum, glycanScanAnnotation, glycanAnnotation);
		if( bRes ) {
			if(iCurrentFeatureId != getTestData().getFeatureIndex()) {//means there is new annotations added using the given glycan structure
				getTestData().getAnnotation().add(glycanAnnotation);
			}
			if( ! glycanScanAnnotation.getScanAnnotations().isEmpty() ) {
				try {
					writer.writeAnnotationsPerGlycan(glycanScanAnnotation, getTempOutputPath());  // creates temp files that will be used to populate the archive in the next step
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			return true;
		}
		return false;
	}

Write the GRITS Project Files

9) Create the archive that will be read by GRITS

a) Populate the Scan Feature objects

	protected void populateScanFeatureData(String glycanFilesPath) {
		//define objects to gather the MS1 annotation while processing MS2
		AnnotationReader reader = new AnnotationReader(); // instantiate the reader, which will read the temp files created in Step 8
		GlycanScansAnnotation glycanAnnotation = new GlycanScansAnnotation();
		ScanFeatures scanFeatures = new ScanFeatures();  
		for(Integer scanId : getTestData().getScans().keySet()){	
			if( isCanceled() )
				return;
			scanFeatures = new ScanFeatures(); // Create new ScanFeatures object to add features to the scan if it was annotated
			scanFeatures.setScanId(scanId);
			scanFeatures.setScanPeaks(new HashSet<Peak>(getTestData().getScans().get(scanId).getPeaklist())); // set the list of peaks to the ScanFeature that are in this Scan
			getTestData().getScanFeatures().put(scanId, scanFeatures); // add this new ScanFeatures object to list of ScanFeatures in the Data object (associated with this scan id)
			if(getTestData().getAnnotatedScan().get(scanId) != null) { // this scan was annotated
				for(String glycanId : getTestData().getAnnotatedScan().get(scanId)){
					glycanAnnotation = reader.readglycanAnnotation(glycanFilesPath, glycanId); // read the temp file for this particular glycan
					if(glycanAnnotation != null && glycanAnnotation.getScanAnnotations().get(scanId) != null) {
						for( GlycanFeature f : glycanAnnotation.getScanAnnotations().get(scanId) ) {
							if( ! scanFeatures.getFeatures().contains(f) ) {
								scanFeatures.getFeatures().add(f); // add each unique GlycanFeature (from ScanAnnotations) to the ScanFeatures list of features
							}
						}
					}
				}
			}
		}
	}

b) Write the archive file(s)

	protected void populateScanFeatureData(String glycanFilesPath, Data data) {
             AnnotationWriter writer = new AnnotationWriter();
             writer.generateScansAnnotationFiles(glycanFilesPath, data)
        }

1) create ZipOutputStream

	FileOutputStream ms_final = new FileOutputStream(zipPath);
	ZipOutputStream compressedFile = new ZipOutputStream(ms_final);

2) Write DataHeader to compressed file

	public void writeDataHeaderToZip( DataHeader dHeader, ZipOutputStream compressedFile ) {
		JAXBContext jaxbDataHeaderContext = null;
		Marshaller jaxbDataHeaderMarshaller = null;
		try {
			List<Class> contextList = new ArrayList<>(Arrays.asList(FilterUtils.filterClassContext));
			contextList.add(FilterSetting.class);
			contextList.add(DataHeader.class);
			jaxbDataHeaderContext = JAXBContext.newInstance(contextList.toArray(new Class[contextList.size()]));
			jaxbDataHeaderMarshaller = jaxbDataHeaderContext.createMarshaller();
		} catch (JAXBException e1) {
			logger.error(e1.getMessage(), e1);
			return;
		} catch ( Exception e2 ) {
			logger.error(e2.getMessage(), e2);
			return;
		}
		try {
			jaxbDataHeaderMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			compressedFile.putNextEntry(new ZipEntry(AnnotationWriter.DATA_HEADER));
			jaxbDataHeaderMarshaller.marshal(dHeader, compressedFile);
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch (JAXBException e2) {
			logger.error(e2.getMessage(), e2);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}
		try {
			compressedFile.closeEntry();
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}
	}

3) Write Method object to compressed file

	public void writeMethodToZip( Method method, ZipOutputStream compressedFile ) {
		JAXBContext jaxbMethodContext = null;
		Marshaller jaxbMethodMarshaller = null;
		try {
			List<Class> contextList = new ArrayList<>(Arrays.asList(FilterUtils.filterClassContext));
			contextList.add(FilterSetting.class);
			contextList.add(Method.class);
			jaxbMethodContext = JAXBContext.newInstance(contextList.toArray(new Class[contextList.size()]));
			jaxbMethodMarshaller = jaxbMethodContext.createMarshaller();
		} catch (JAXBException e1) {
			logger.error(e1.getMessage(), e1);
			return;
		} catch ( Exception e2 ) {
			logger.error(e2.getMessage(), e2);
			return;
		}
		try {
			jaxbMethodMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			compressedFile.putNextEntry(new ZipEntry(AnnotationWriter.SETTINGS_FILE));
			jaxbMethodMarshaller.marshal(method, compressedFile);
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch (JAXBException e2) {
			logger.error(e2.getMessage(), e2);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}
		try {
			compressedFile.closeEntry();
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}		
	}

4) Write Data object WITHOUT ScanFeatures objects to compressed file

	public void writeDataToZip(Data data, ZipOutputStream compressedFile ) {
		JAXBContext jaxbDataContext = null;
		Marshaller jaxbDataMarshaller = null;
		try {
			List<Class> contextList = new ArrayList<>(Arrays.asList(FilterUtils.filterClassContext));
			contextList.add(FilterSetting.class);
			contextList.add(Data.class);
			jaxbDataContext = JAXBContext.newInstance(contextList.toArray(new Class[contextList.size()]));
			jaxbDataMarshaller = jaxbDataContext.createMarshaller();
		} catch (JAXBException e1) {
			logger.error(e1.getMessage(), e1);
			return;
		} catch ( Exception e2 ) {
			logger.error(e2.getMessage(), e2);
			return;
		}
		try {
			jaxbDataMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
			compressedFile.putNextEntry(new ZipEntry(AnnotationWriter.DATA_FILE));
			// testing not serializing the scanfeatures in the data object.
			HashMap<Integer, ScanFeatures> hm = data.getScanFeatures();
			data.setScanFeatures(null); // set scan features to null before writing data!
			jaxbDataMarshaller.marshal(data, compressedFile);
			data.setScanFeatures(hm);
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch (JAXBException e2) {
			logger.error(e2.getMessage(), e2);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}
		try {
			compressedFile.closeEntry();
		} catch (IOException e1) {
			logger.error(e1.getMessage(), e1);
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}		
	}

5) Write ScanFeatures objects to compressed file

	public void writeScanFeaturesToZip(Data data, ZipOutputStream compressedFile, String glycanFilesPath ) throws Exception {
		JAXBContext jaxbContextAnn = null;
		Marshaller jaxbMarshallerAnn = null;
		try {
			jaxbContextAnn = JAXBContext.newInstance(ScanFeatures.class);
			jaxbMarshallerAnn = jaxbContextAnn.createMarshaller();
		} catch (JAXBException e1) {
			logger.error(e1.getMessage(), e1);
			return;
		} catch ( Exception e2 ) {
			logger.error(e2.getMessage(), e2);
			return;
		}
		HashMap<String,String> gogCheckerForMS1 = new HashMap<String,String>();
		try {
			if( data.getScans() != null ) {
				for(Integer scanId : data.getScans().keySet()){
					try {
						compressedFile.putNextEntry(new ZipEntry(scanId+".xml"));
						jaxbMarshallerAnn.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
						jaxbMarshallerAnn.marshal(data.getScanFeatures().get(scanId),compressedFile);
					} catch (IOException e1) {
						logger.error(e1.getMessage(), e1);
					} catch (JAXBException e2) {
						logger.error(e2.getMessage(), e2);
					} catch ( Exception ex ) {
						logger.error(ex.getMessage(), ex);
					}
					try {
						compressedFile.closeEntry();
						if(data.getAnnotatedScan().get(scanId) != null){
							for(String gog : data.getAnnotatedScan().get(scanId)){
								gogCheckerForMS1.put(gog, "seen");
							}
						}
					} catch (IOException e1) {
						logger.error(e1.getMessage(), e1);
					} catch ( Exception ex ) {
						logger.error(ex.getMessage(), ex);
					}
				}
			}
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
			return;
		}

		try {
			deleteAnnotationsPerGlycan(glycanFilesPath,gogCheckerForMS1);  // clean up temporary folder of data
		} catch ( Exception ex ) {
			logger.error(ex.getMessage(), ex);
		}
	}

6) Close compressed file

		try {
			compressedFile.close();
		}catch(Exception ex){
			logger.error("Error closing compressed file", ex);
		}
Last modified 2 years ago Last modified on 06/26/2018 04:51:01 PM