/*****************************************************************************
* Limpet - the Lightweight InforMation ProcEssing Toolkit
* http://limpet.info
*
* (C) 2015-2016, Deep Blue C Technologies Ltd
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html)
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*****************************************************************************/
package info.limpet.data.impl;
import info.limpet.ICommand;
import info.limpet.IQuantityCollection;
import info.limpet.ITemporalQuantityCollection;
import info.limpet.QuantityRange;
import info.limpet.UIProperty;
import info.limpet.data.impl.helpers.QuantityHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.measure.Measurable;
import javax.measure.Measure;
import javax.measure.quantity.Quantity;
import javax.measure.unit.Dimension;
import javax.measure.unit.Unit;
public class TemporalQuantityCollection<T extends Quantity> extends
TemporalObjectCollection<Measurable<T>> implements
ITemporalQuantityCollection<T>, IQuantityCollection<T>
{
private transient QuantityHelper<T> _qHelper;
private QuantityRange<T> _range;
private Unit<T> units;
public TemporalQuantityCollection(String name, ICommand<?> precedent,
Unit<T> units)
{
super(name, precedent);
this.units = units;
_qHelper =
new QuantityHelper<T>((ArrayList<Measurable<T>>) getValues(), units);
}
@Override
public void add(long time, Measurable<T> object)
{
if (object instanceof Measure)
{
Measure<?, ?> oM = (Measure<?, ?>) object;
Unit<?> hisUnits = oM.getUnit();
if (getUnits() != null && !getUnits().equals(hisUnits))
{
throw new RuntimeException("Measurement is in wrong units");
}
}
// double-check the units
super.add(time, object);
}
@Override
public void add(long time, Number value)
{
super.add(time, Measure.valueOf(value.doubleValue(), getUnits()));
}
@Override
public void add(Number value)
{
throw new UnsupportedOperationException(
"Please use add(time, value) for time series datasets");
}
@Override
public Measurable<T> min()
{
initQHelper();
return _qHelper.min();
}
@Override
public Measurable<T> max()
{
initQHelper();
return _qHelper.max();
}
@Override
public Measurable<T> mean()
{
initQHelper();
return _qHelper.mean();
}
@Override
public Measurable<T> variance()
{
initQHelper();
return _qHelper.variance();
}
@Override
public Measurable<T> sd()
{
initQHelper();
return _qHelper.sd();
}
@Override
public boolean isQuantity()
{
return true;
}
@Override
public boolean isTemporal()
{
return true;
}
protected void initQHelper()
{
if (_qHelper == null)
{
_qHelper =
new QuantityHelper<T>((ArrayList<Measurable<T>>) getValues(), units);
}
}
@Override
public Dimension getDimension()
{
initQHelper();
return _qHelper.getDimension();
}
@UIProperty(name = "Units", category = UIProperty.CATEGORY_VALUE)
@Override
public Unit<T> getUnits()
{
initQHelper();
return _qHelper.getUnits();
}
@Override
public void replaceSingleton(Number newValue)
{
initQHelper();
_qHelper.replace(newValue);
}
@Override
public void setRange(QuantityRange<T> range)
{
initQHelper();
_range = range;
_qHelper.setRange(range);
// tell anyone that wants to know
super.fireMetadataChanged();
}
@UIProperty(name = "Range", category = UIProperty.CATEGORY_METADATA,
visibleWhen = "valuesCount == 1")
@Override
public QuantityRange<T> getRange()
{
return _range;
}
@Override
public Measurable<T> interpolateValue(long time, InterpMethod interpMethod)
{
final Measurable<T> res;
switch (interpMethod)
{
case Linear:
res = linearInterp(this.getTimes(), this.getValues(), time);
break;
default:
res = null;
}
return res;
}
private Measurable<T> linearInterp(Collection<Long> times,
List<Measurable<T>> values, long time)
{
final Measurable<T> res;
// ok, find the values either side
Measurable<T> beforeVal, afterVal;
int beforeIndex = -1, afterIndex = -1;
long beforeTime = 0, afterTime = 0;
Iterator<Long> tIter = times.iterator();
int ctr = 0;
while (tIter.hasNext())
{
Long thisT = (Long) tIter.next();
if (thisT <= time)
{
beforeIndex = ctr;
beforeTime = thisT;
}
if (thisT >= time)
{
afterIndex = ctr;
afterTime = thisT;
break;
}
ctr++;
}
if (beforeIndex >= 0 && afterIndex == 0)
{
res = values.get(beforeIndex);
}
else if (beforeIndex >= 0 && afterIndex >= 0)
{
if (beforeIndex == afterIndex)
{
// special case - it falls on one of our values
res = values.get(beforeIndex);
}
else
{
beforeVal = values.get(beforeIndex);
afterVal = values.get(afterIndex);
double y0 = beforeVal.doubleValue(getUnits());
double y1 = afterVal.doubleValue(getUnits());
double x0 = beforeTime;
double x1 = afterTime;
double x = time;
double newRes = y0 + (y1 - y0) * (x - x0) / (x1 - x0);
// ok, we can do the calc
res = Measure.valueOf(newRes, getUnits());
}
}
else
{
res = null;
}
return res;
}
@UIProperty(name = "Value", category = UIProperty.CATEGORY_VALUE,
visibleWhen = "valuesCount == 1")
public Number getSingletonValue()
{
initQHelper();
return _qHelper.getValue();
}
public void setSingletonValue(Number newValue)
{
replaceSingleton(newValue);
fireDataChanged();
}
}