NetCDFMsScan.java

/*
 * (C) Copyright 2015-2016 by MSDK Development Team
 *
 * This software is dual-licensed under either
 *
 * (a) the terms of the GNU Lesser General Public License version 2.1 as published by the Free
 * Software Foundation
 *
 * or (per the licensee's choosing)
 *
 * (b) the terms of the Eclipse Public License v1.0 as published by the Eclipse Foundation.
 */

package io.github.msdk.io.netcdf;

import java.io.IOException;

import io.github.msdk.MSDKRuntimeException;
import io.github.msdk.datamodel.impl.SimpleMsScan;
import io.github.msdk.datamodel.msspectra.MsSpectrumType;
import io.github.msdk.spectra.spectrumtypedetection.SpectrumTypeDetectionAlgorithm;
import ucar.ma2.Array;
import ucar.ma2.Index;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.Variable;

/**
 * <p>
 * NetCDFMsScan class.
 * </p>
 *
 */
public class NetCDFMsScan extends SimpleMsScan {

  private int[] scanStartPositions;
  private float[] scanRetentionTimes;
  private Variable massValueVariable;
  private Variable intensityValueVariable;
  private double massValueScaleFactor;
  private double intensityValueScaleFactor;
  private double[] preLoadedMzValues;
  private float[] preLoadedIntensityValues;
  private Integer numOfDataPoints;
  private MsSpectrumType spectrumType;

  /**
   * <p>
   * Constructor for {@link io.github.msdk.io.netcdf.NetCDFMsScan NetCDFMsScan}
   * </p>
   * 
   * @param scanNumber the Scan Number
   * @param id the Scan ID
   * @param scanStartPositions an int[] containing start positions of all scans, and an extra
   *        element containing the stop position of the last scan
   * @param scanRetentionTimes a float[] containing retention times of all scans
   * @param massValueVariable {@link ucar.nc2.Variable Variable} containing the m/z data of the
   *        scans
   * @param intensityValueVariable {@link ucar.nc2.Variable Variable} containing the intensity data
   *        of the scans
   * @param massValueScaleFactor double value by which the mass values have been scaled by to
   * @param intensityValueScaleFactor double value by which the intensity values have been scaled by
   *        to
   */
  public NetCDFMsScan(Integer scanNumber, int[] scanStartPositions, float[] scanRetentionTimes,
      Variable massValueVariable, Variable intensityValueVariable, double massValueScaleFactor,
      double intensityValueScaleFactor) {
    super(scanNumber);
    this.scanStartPositions = scanStartPositions;
    this.scanRetentionTimes = scanRetentionTimes;
    this.massValueVariable = massValueVariable;
    this.intensityValueVariable = intensityValueVariable;
    this.massValueScaleFactor = massValueScaleFactor;
    this.intensityValueScaleFactor = intensityValueScaleFactor;
    this.preLoadedMzValues = null;
    this.preLoadedIntensityValues = null;
    this.spectrumType = null;
  }

  /** {@inheritDoc} */
  @Override
  public float[] getIntensityValues(float[] intensityValues) {
    if (preLoadedIntensityValues == null) {
      final Integer scanIndex = getScanIndex();
      numOfDataPoints = getNumberOfDataPoints();
      try {
        // int[] which defines the origin
        // Since intensity value is stored in a 1D array, there is only one element
        final int scanStartPosition[] = {scanStartPositions[scanIndex]};
        // int[] which defines the shape
        // shape is the length of each dimension to be considered from the origin
        // So, shape is an int[] containing only one element - 'size'
        final int scanLength[] =
            {scanStartPositions[scanIndex + 1] - scanStartPositions[scanIndex]};
        final Array intensityValueArray =
            intensityValueVariable.read(scanStartPosition, scanLength);
        final Index intensityValuesIndex = intensityValueArray.getIndex();

        if (intensityValues == null || intensityValues.length < numOfDataPoints)
          intensityValues = new float[numOfDataPoints];

        // Load the data points
        for (int i = 0; i < numOfDataPoints; i++) {
          // Change the Index according to i
          final Index intensityIndex0 = intensityValuesIndex.set0(i);
          // get the intensity value after multiplying with the scale factor
          intensityValues[i] =
              (float) (intensityValueArray.getDouble(intensityIndex0) * intensityValueScaleFactor);
        }
      } catch (IOException | InvalidRangeException e) {
        throw new MSDKRuntimeException(e);
      }
    } else {
      if (intensityValues == null || intensityValues.length < numOfDataPoints)
        intensityValues = new float[numOfDataPoints];

      // Copy values to a different array if needed
      for (int i = 0; i < preLoadedIntensityValues.length; i++)
        intensityValues[i] = preLoadedIntensityValues[i];
    }

    return intensityValues;
  }

  /** {@inheritDoc} */
  @Override
  public double[] getMzValues(double[] mzValues) {
    if (preLoadedMzValues == null) {
      final Integer scanIndex = getScanIndex();
      numOfDataPoints = getNumberOfDataPoints();
      try {
        // int[] which defines the origin
        // Since mass value is stored in a 1D array, there is only one element
        final int scanStartPosition[] = {scanStartPositions[scanIndex]};
        // int[] which defines the shape
        // shape is the length of each dimension to be considered from the origin
        // So, shape is an int[] containing only one element - 'size'
        final int scanLength[] =
            {scanStartPositions[scanIndex + 1] - scanStartPositions[scanIndex]};
        final Array massValueArray = massValueVariable.read(scanStartPosition, scanLength);
        final Index massValuesIndex = massValueArray.getIndex();

        if (mzValues == null || mzValues.length < numOfDataPoints)
          mzValues = new double[numOfDataPoints];

        // Load the data points
        for (int i = 0; i < numOfDataPoints; i++) {
          // Change the Index according to i
          final Index massIndex0 = massValuesIndex.set0(i);
          // get the mass value after multiplying with the scale factor
          mzValues[i] = massValueArray.getDouble(massIndex0) * massValueScaleFactor;
        }

      } catch (IOException | InvalidRangeException e) {
        throw new MSDKRuntimeException(e);
      }
    } else {
      if (mzValues == null || mzValues.length < numOfDataPoints)
        mzValues = new double[numOfDataPoints];

      // Copy values to a different array if needed
      for (int i = 0; i < preLoadedMzValues.length; i++)
        mzValues[i] = preLoadedMzValues[i];
    }

    return mzValues;
  }

  /** {@inheritDoc} */
  @Override
  public Integer getNumberOfDataPoints() {
    if (numOfDataPoints == null) {
      final Integer scanIndex = getScanIndex();
      numOfDataPoints = scanStartPositions[scanIndex + 1] - scanStartPositions[scanIndex];
    }

    return numOfDataPoints;
  }

  /** {@inheritDoc} */
  @Override
  public Float getRetentionTime() {
    return scanRetentionTimes[getScanIndex()];
  }

  /** {@inheritDoc} */
  @Override
  public MsSpectrumType getSpectrumType() {
    if (spectrumType == null)
      spectrumType = SpectrumTypeDetectionAlgorithm.detectSpectrumType(getMzValues(),
          getIntensityValues(), getNumberOfDataPoints());

    return spectrumType;
  }

  /**
   * The Scan Index is the inde of the scan in the array
   * 
   * @return the scan index of the scan
   */
  public Integer getScanIndex() {
    return getScanNumber() - 1;
  }

  /**
   * The mass and intensity arrays are loaded once this method is called
   * 
   * @throws IOException
   * @throws InvalidRangeException
   */
  public void parseScan() throws IOException, InvalidRangeException {
    // Load values to this scan instance itself, this method is called only when the scan passes the
    // predicate
    preLoadedMzValues = getMzValues();
    preLoadedIntensityValues = getIntensityValues();
    numOfDataPoints = getNumberOfDataPoints();

    setDataPoints(preLoadedMzValues, preLoadedIntensityValues, numOfDataPoints);
    spectrumType = SpectrumTypeDetectionAlgorithm.detectSpectrumType(preLoadedMzValues,
        preLoadedIntensityValues, numOfDataPoints);
  }
}