/*- * Copyright 2016 Diamond Light Source Ltd. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ package uk.ac.diamond.scisoft.analysis.processing.operations.oned; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.eclipse.dawnsci.analysis.api.processing.Atomic; import org.eclipse.dawnsci.analysis.api.processing.OperationData; import org.eclipse.dawnsci.analysis.api.processing.OperationException; import org.eclipse.dawnsci.analysis.api.processing.OperationRank; import org.eclipse.dawnsci.analysis.dataset.operations.AbstractOperation; import org.eclipse.january.DatasetException; import org.eclipse.january.IMonitor; import org.eclipse.january.dataset.BooleanDataset; import org.eclipse.january.dataset.Comparisons; import org.eclipse.january.dataset.Dataset; import org.eclipse.january.dataset.DatasetFactory; import org.eclipse.january.dataset.DatasetUtils; import org.eclipse.january.dataset.DoubleDataset; import org.eclipse.january.dataset.IDataset; import org.eclipse.january.dataset.ILazyDataset; import org.eclipse.january.dataset.IndexIterator; @Atomic public class InterpolateMissingDataOperation extends AbstractOperation<InterpolateMissingDataModel, OperationData> { @Override public String getId() { return "uk.ac.diamond.scisoft.analysis.processing.operations.oned.InterpolateMissingDataOperation"; } @Override public OperationRank getInputRank() { return OperationRank.ONE; } @Override public OperationRank getOutputRank() { return OperationRank.ONE; } @Override protected OperationData process(IDataset input, IMonitor monitor) throws OperationException { Dataset inputData = DatasetUtils.copy(DoubleDataset.class, input); copyMetadata(input, inputData); Double mdi = model.getMdi(); return new OperationData(interpolateMissingData(inputData, mdi)); } public static Dataset interpolateMissingData(Dataset inputData, Double mdi) { BooleanDataset isMissing = Comparisons.logicalNot(Comparisons.isFinite(inputData)); // Also add missing data values, if defined if (mdi != null) { isMissing = Comparisons.logicalOr(isMissing, Comparisons.equalTo(inputData, mdi)); } // RLE the missing data blocks. Stored in a map of first element to length of block Map<Integer, Integer> missingDataBlocks = new TreeMap<Integer, Integer>(); IndexIterator iter = isMissing.getIterator(); while (iter.hasNext()) { if (isMissing.getAbs(iter.index)) { int missingBlockStart = iter.index; int missingBlockLength = 1; while(iter.hasNext()) { if (isMissing.getAbs(iter.index)) missingBlockLength++; else break; } missingDataBlocks.put(missingBlockStart, missingBlockLength); } } // Get the abscissae of the data, or build one ILazyDataset[] abscissae = getFirstAxes(inputData); Dataset abscissa = null; if (abscissae != null && abscissae[0] != null) { try { abscissa = DatasetUtils.sliceAndConvertLazyDataset(abscissae[0]); } catch (DatasetException e) { // do nothing } } if (abscissa == null) { abscissa = DatasetFactory.createRange(inputData.getSize()); } // To interpolate the missing data, we can assume an ordered grid, // since missing data can be just left out of a collection of points for (Entry<Integer, Integer> entry : missingDataBlocks.entrySet()) { int nextFiniteIndex = entry.getKey()+entry.getValue(); int lastFiniteIndex = entry.getKey()-1; // Deal with end points if (entry.getKey() == 0) { inputData.setSlice(inputData.getObjectAbs(nextFiniteIndex), new int[]{0}, new int[]{nextFiniteIndex}, new int[]{1}); } else if (entry.getKey() + entry.getValue() == inputData.getSize()) { inputData.setSlice(inputData.getObjectAbs(lastFiniteIndex), new int[]{lastFiniteIndex+1}, new int[]{inputData.getSize()}, new int[]{1}); } else { // Construct the parameters of the interpolation y(x) = a·x + b double a = (inputData.getElementDoubleAbs(nextFiniteIndex) - inputData.getElementDoubleAbs(lastFiniteIndex))/(abscissa.getElementLongAbs(nextFiniteIndex) - abscissa.getElementDoubleAbs(lastFiniteIndex)); double b = inputData.getElementDoubleAbs(lastFiniteIndex) - a * abscissa.getElementDoubleAbs(lastFiniteIndex); for (int index = entry.getKey(); index < entry.getKey() + entry.getValue(); index++) { inputData.setObjectAbs(index, a*abscissa.getElementDoubleAbs(index)+b); } } } return inputData; } }