All Downloads are FREE. Search and download functionalities are using the official Maven repository.

decodes.tsdb.algo.SubSample Maven / Gradle / Ivy

Go to download

A collection of software for aggregatting and processing environmental data such as from NOAA GOES satellites.

The newest version!
/**
 * $Id$
 * 
 * $Log$
 * Revision 1.2  2016/02/29 22:19:22  mmaloney
 * CWMS-7764 Sampling Time Offset Fix.
 *
 * Revision 1.1.1.1  2014/05/19 15:28:59  mmaloney
 * OPENDCS 6.0 Initial Checkin
 *
 * Revision 1.13  2011/11/08 22:23:24  mmaloney
 * dev
 *
 * Revision 1.12  2011/11/08 22:01:39  mmaloney
 * *** empty log message ***
 *
 * Revision 1.11  2011/11/08 21:53:39  mmaloney
 * dev
 *
 * Revision 1.10  2011/11/08 21:45:38  mmaloney
 * dev
 *
 * Revision 1.9  2011/11/08 21:12:42  mmaloney
 * dev
 *
 * Revision 1.8  2011/05/03 19:06:26  mmaloney
 * dev
 *
 * Revision 1.7  2011/05/03 18:44:57  mmaloney
 * dev
 *
 * Revision 1.6  2011/05/03 18:19:25  mmaloney
 * dev
 *
 * Revision 1.5  2011/05/03 17:23:38  mmaloney
 * Convert to high-performance time-slice algorithm. No fetching.
 *
 * Revision 1.4  2010/12/21 19:20:35  mmaloney
 * group computations
 *
 */
package decodes.tsdb.algo;

import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

import ilex.var.NamedVariableList;
import ilex.var.NamedVariable;
import decodes.tsdb.DbAlgorithmExecutive;
import decodes.tsdb.DbCompException;
import decodes.tsdb.DbIoException;
import decodes.tsdb.IntervalCodes;
import decodes.tsdb.IntervalIncrement;
import decodes.tsdb.NoSuchObjectException;
import decodes.tsdb.ParmRef;
import decodes.tsdb.VarFlags;
import decodes.util.PropertySpec;

//AW:IMPORTS
// Place an import statements you need here.
//AW:IMPORTS_END

//AW:JAVADOC
/**
Convert a short interval to a longer interval by taking the first value equal-to or after the longer-period timestamp.
Example: Convert 10min data to 30min data by taking data on the hour and half-hour

 */
//AW:JAVADOC_END
public class SubSample
	extends decodes.tsdb.algo.AW_AlgorithmBase
{
//AW:INPUTS
	public double inputShortInterval;	//AW:TYPECODE=i
	String _inputNames[] = { "inputShortInterval" };
//AW:INPUTS_END

//AW:LOCALVARS
	private IntervalIncrement outputIncr = null;
	private GregorianCalendar outputCal = null;
	private PropertySpec subsampPropertySpecs[] = 
	{
		new PropertySpec("samplingTimeOffset", PropertySpec.STRING,
			"(optional) E.g. for a daily subsample: '6 Hours' to grab the 6AM value.")
	};
	
	@Override
	protected PropertySpec[] getAlgoPropertySpecs()
	{
		return subsampPropertySpecs;
	}
//AW:LOCALVARS_END

//AW:OUTPUTS
	public NamedVariable outputLongInterval = new NamedVariable("outputLongInterval", 0);
	String _outputNames[] = { "outputLongInterval" };
//AW:OUTPUTS_END

//AW:PROPERTIES
//	public boolean aggLowerBoundClosed = true;
//	public boolean aggUpperBoundClosed = false;
//	String _propertyNames[] = { "aggLowerBoundClosed", "aggUpperBoundClosed" };
	public String samplingTimeOffset = "";
	String _propertyNames[] = { "samplingTimeOffset" };
//AW:PROPERTIES_END

	// Allow javac to generate a no-args constructor.

	/**
	 * Algorithm-specific initialization provided by the subclass.
	 */
	protected void initAWAlgorithm( )
		throws DbCompException
	{
//AW:INIT
		_awAlgoType = AWAlgoType.TIME_SLICE;
//AW:INIT_END

//AW:USERINIT
		// Code here will be run once, after the algorithm object is created.
//AW:USERINIT_END
	}
	
	/**
	 * This method is called once before iterating all time slices.
	 */
	protected void beforeTimeSlices()
		throws DbCompException
	{
//AW:BEFORE_TIMESLICES
		
		// Note aggTZ may be set either globally of specifically for this algo or comp.
		outputCal = new GregorianCalendar(aggCal.getTimeZone());
		
		// We will use outputCal to keep track of the next output time.
		// Initialize it to the first time >= the first input time.
		Date firstInputT = baseTimes.first();
		outputCal.setTime(firstInputT);
		ParmRef outputParmRef = getParmRef("outputLongInterval");
		
		outputIncr = IntervalCodes.getIntervalCalIncr(
			outputParmRef.compParm.getInterval());
		if (outputIncr == null || outputIncr.getCount() == 0)
			throw new DbCompException("SubSample requires regular interval output!");

		debug3("beforeTimeSlices firstInputT=" + debugSdf.format(firstInputT) + " outputIncr = " + outputIncr
			+ (samplingTimeOffset!= null ? (", samplingTimeOffset=" + samplingTimeOffset) : ""));

		// Always get rid of seconds and msecs.
		outputCal.set(Calendar.MILLISECOND, 0);
		outputCal.set(Calendar.SECOND, 0);

		// MJM Added samplingTimeOffset processing for OpenDCS 6.2 RC03
		IntervalIncrement offsetIncr[] = null;
		if (samplingTimeOffset != null && samplingTimeOffset.trim().length() > 0)
		{
			try
			{
				offsetIncr = IntervalIncrement.parseMult(samplingTimeOffset);
				debug1("Honoring sampling time offset '" + samplingTimeOffset + "'");
			}
			catch (NoSuchObjectException ex)
			{
				warning("Invalid samplingTimeOffset property '" + samplingTimeOffset + "': " + ex
					+ " -- ignored.");
				offsetIncr = null;
			}
		}
		
		if (outputIncr.getCalConstant() == Calendar.MINUTE)
		{
			// output interval is in # of minutes
			int min = outputCal.get(Calendar.MINUTE);
			min = (min / outputIncr.getCount()) * outputIncr.getCount();
			outputCal.set(Calendar.MINUTE, min);
		}
		else if (outputIncr.getCalConstant() == Calendar.HOUR_OF_DAY)
		{
			outputCal.set(Calendar.MINUTE, 0);
			int hr = outputCal.get(Calendar.HOUR_OF_DAY);
			hr = (hr / outputIncr.getCount()) * outputIncr.getCount();
			outputCal.set(Calendar.HOUR_OF_DAY, hr);
		}
		else if (outputIncr.getCalConstant() == Calendar.DAY_OF_MONTH)
		{
			outputCal.set(Calendar.MINUTE, 0);
			outputCal.set(Calendar.HOUR_OF_DAY, 0);
			int day = outputCal.get(Calendar.DAY_OF_MONTH);
			day = (day / outputIncr.getCount()) * outputIncr.getCount();
			outputCal.set(Calendar.DAY_OF_MONTH, day);
		}
		else if (outputIncr.getCalConstant() == Calendar.MONTH)
		{
			// Note count should be 1, 2, 3, 4, or 6. Anything else will give weird results.
			outputCal.set(Calendar.MINUTE, 0);
			outputCal.set(Calendar.HOUR_OF_DAY, 0);
			outputCal.set(Calendar.DAY_OF_MONTH, 1);
			int month = outputCal.get(Calendar.MONTH);
			month = (month / outputIncr.getCount()) * outputIncr.getCount();
			outputCal.set(Calendar.MONTH, month);
		}
		else
		{
			throw new DbCompException("Invalid output interval: " + 
				outputParmRef.compParm.getInterval());
		}
		
		// If an offset was supplied, add it. Note: it's up to the user to make sure
		// it makes sense. Good: output interval = 1Day, offsetIncr = 6 hours.
		// Bad: output interval = 1 hour, offsetIncr = 1 week.
		if (offsetIncr != null)
			for(IntervalIncrement ii : offsetIncr)
			{
				outputCal.set(ii.getCalConstant(), ii.getCount());
			}		
		
		// Because of the added increment, I could end up with an outputCal time that
		// is before the first input time.
		// Example Daily average at 6 AM from hourly inputs, and the first value I'm given is 7 AM.
		// The above code will set outputCal to 6AM. I need to add the output increment so that
		// the outputCal is always >= the first input time.
		while(outputCal.getTime().before(firstInputT))
		{
			debug3("beforeTimeSlices firstInputT=" + debugSdf.format(firstInputT)
				+ ", outputCal=" + debugSdf.format(outputCal.getTime())
				+ ", incr=" + outputIncr);
// should the following be -outputIncr.getCount()
			outputCal.add(outputIncr.getCalConstant(), outputIncr.getCount());
		}
			
		// Normally for copy, output units will be the same as input.
		String inUnits = getInputUnitsAbbr("inputShortInterval");
		if (inUnits != null && inUnits.length() > 0)
			setOutputUnitsAbbr("outputLongInterval", inUnits);
		
		debug1("first input=" + debugSdf.format(firstInputT)
			+ ", first output=" + debugSdf.format(outputCal.getTime())
			+ " outputIncr=" + outputIncr.toString());
//AW:BEFORE_TIMESLICES_END
	}

	/**
	 * Do the algorithm for a single time slice.
	 * AW will fill in user-supplied code here.
	 * Base class will set inputs prior to calling this method.
	 * User code should call one of the setOutput methods for a time-slice
	 * output variable.
	 *
	 * @throws DbCompException (or subclass thereof) if execution of this
	 *        algorithm is to be aborted.
	 */
	protected void doAWTimeSlice()
		throws DbCompException
	{
//AW:TIMESLICE
		Date nextOutputT = outputCal.getTime();
		long deltaSec = (_timeSliceBaseTime.getTime() - nextOutputT.getTime()) / 1000L;
		if (deltaSec <= roundSec && deltaSec >= -roundSec)
		{
			debug1("Outputting value at " + debugSdf.format(nextOutputT)
				+ ", deltaSec=" + deltaSec + ", timeSlice=" 
				+ debugSdf.format(_timeSliceBaseTime));
			setOutput(outputLongInterval, inputShortInterval, nextOutputT);
		}
		
		// Regardless of whether te above produced an output, the
		// next output time should always be > current time slice
		while (!outputCal.getTime().after(_timeSliceBaseTime))
		{
			outputCal.add(outputIncr.getCalConstant(), outputIncr.getCount());
		}
		debug1("Advanced nextOutput to be at " + debugSdf.format(outputCal.getTime()));
//AW:TIMESLICE_END
	}

	/**
	 * This method is called once after iterating all time slices.
	 */
	protected void afterTimeSlices()
		throws DbCompException
	{
//AW:AFTER_TIMESLICES
//AW:AFTER_TIMESLICES_END
	}

	/**
	 * Required method returns a list of all input time series names.
	 */
	public String[] getInputNames()
	{
		return _inputNames;
	}

	/**
	 * Required method returns a list of all output time series names.
	 */
	public String[] getOutputNames()
	{
		return _outputNames;
	}

	/**
	 * Required method returns a list of properties that have meaning to
	 * this algorithm.
	 */
	public String[] getPropertyNames()
	{
		return _propertyNames;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy