package org.autoplot.tca; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import org.das2.datum.EnumerationUnits; import org.das2.datum.Units; import org.das2.util.monitor.NullProgressMonitor; import org.das2.util.monitor.ProgressMonitor; import org.das2.qds.AbstractQFunction; import org.das2.qds.BundleDataSet; import org.das2.qds.DDataSet; import org.das2.qds.DataSetUtil; import org.das2.qds.MutablePropertyDataSet; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.autoplot.datasource.DataSetURI; import org.autoplot.datasource.DataSource; import org.autoplot.datasource.capability.TimeSeriesBrowse; import org.das2.datum.UnitsUtil; import org.das2.qds.ops.Ops; /** * Allow Autoplot URIs to supply data to label plots. * * class:org.autoplot.tca.AutoplotTCASource:vap+file:/tmp/foo.txt?rank2=field1-field4&depend0=field0 * class:org.autoplot.tca.AutoplotTCASource:vap+dat:file:/home/jbf/project/autoplot/data/dat/rockets/21139_E_field.txt?skipLines=1&depend0=field0&rank2=field3-field4 * @author jbf */ public class UriTcaSource extends AbstractQFunction { TimeSeriesBrowse tsb; boolean needToRead; QDataSet ds; QDataSet tlim; QDataSet bundleDs; DataSource dss; QDataSet error; QDataSet errorNoDs; QDataSet nonValueDs; //QDataSet nonMonoDs; QDataSet initialError; static final Logger logger= org.das2.util.LoggerManager.getLogger( "autoplot.tca.uritcasource" ); // cache the example input so we only attempt read once. private MutablePropertyDataSet exampleInput=null; public UriTcaSource( String uri ) throws Exception { logger.log(Level.FINE, "new tca source: {0}", uri); if ( uri.startsWith("class:org.autoplot.tca.UriTcaSource:") ) { throw new IllegalArgumentException("pass a URI to this, not class:org.autoplot.tca.UriTcaSource"); } EnumerationUnits eu= new EnumerationUnits("UriTcaSource"); error= DataSetUtil.asDataSet( eu.createDatum("Error") ); errorNoDs= DataSetUtil.asDataSet( eu.createDatum("No Data") ); nonValueDs= DataSetUtil.asDataSet( eu.createDatum(" ") ); //nonMonoDs= DataSetUtil.asDataSet( eu.createDatum("Non Mono") ); DataSource dss1; try { dss1= DataSetURI.getDataSource(uri); initialError= null; this.tsb= dss1.getCapability( TimeSeriesBrowse.class ); this.dss= dss1; this.needToRead= true; } catch ( Exception lex ) { logger.log( Level.WARNING, lex.getMessage(), lex ); initialError= DataSetUtil.asDataSet( eu.createDatum(lex.toString()) ); } } private void doRead( ) throws Exception { ProgressMonitor mon= new NullProgressMonitor(); // DasProgressPanel.createFramed("loading data"); if ( this.tsb!=null ) { logger.log(Level.FINE, "reading TCAs from TSB {0}", this.tsb.getURI()); } else { logger.log(Level.FINE, "reading TCAs from {0}", dss); } needToRead= false; // clear the flag in case there is an exception. ds= dss.getDataSet( mon ); if ( ds==null ) { logger.log(Level.FINE, "doRead getDataSet got null "); } else { logger.log(Level.FINE, "doRead got: {0}", ds); prepBundle(); } if ( this.tsb==null ) { // jython scripts can get a TimeSeriesBrowse after the first read. tsb= dss.getCapability( TimeSeriesBrowse.class ); } } /** * given the dataset loaded, ds, inspect the data to create the bundle * descriptor, bundleDs. * Preconditions: * Postconditions */ private void prepBundle() { QDataSet dep0= SemanticOps.xtagsDataSet(ds); if ( !DataSetUtil.isMonotonicAndIncreasing(dep0) ) { logger.warning("TCA contains data which is not monotonically increasing"); if ( dep0.value(0)>dep0.value(dep0.length()-1) ) { ds= Ops.copy( Ops.reverse(ds) ); dep0= SemanticOps.xtagsDataSet(ds); if ( !DataSetUtil.isMonotonicAndIncreasing(dep0) ) { logger.warning("reversed TCA dataset still contains non-monotonic tags"); ds= Ops.ensureMonotonicAndIncreasingWithFill(ds); } else { logger.info("reversing TCA dataset makes tags monotonically increasing."); } } else { logger.warning("removing non-monotonically increasing tags of TCA dataset."); ds= Ops.ensureMonotonicAndIncreasingWithFill(ds); } } tlim= DataSetUtil.guessCadenceNew( SemanticOps.xtagsDataSet(ds), ds ); if ( this.tsb!=null ) { DatumRange dr= this.tsb.getTimeRange(); QDataSet ext= Ops.extent( SemanticOps.xtagsDataSet(ds), null ); double d0= DatumRangeUtil.normalize( dr, DataSetUtil.asDatum( ext.slice(0) ) ); double d1= DatumRangeUtil.normalize( dr, DataSetUtil.asDatum( ext.slice(1) ) ); logger.log(Level.FINE, "normalized after load: {0}-{1}", new Object[]{d0, d1}); } bundleDs= (QDataSet)ds.property(QDataSet.BUNDLE_1); if ( bundleDs==null ) { if ( ds.rank()==1 ) { // just a single param, go ahead and support this. DDataSet bds1= DDataSet.createRank2(1,0); String name= (String) ds.property(QDataSet.NAME); String label= (String) ds.property(QDataSet.LABEL); bds1.putProperty( QDataSet.NAME, 0, name==null ? "ds0" : name ); bds1.putProperty( QDataSet.LABEL, 0, label==null ? ( name==null ? "" : name ) : label ); if ( ds.property(QDataSet.VALID_MIN)!=null ) bds1.putProperty( QDataSet.VALID_MIN, 0, ds.property(QDataSet.VALID_MIN) ); if ( ds.property(QDataSet.VALID_MAX)!=null ) bds1.putProperty( QDataSet.VALID_MAX, 0, ds.property(QDataSet.VALID_MAX) ); if ( ds.property(QDataSet.FILL_VALUE)!=null ) bds1.putProperty( QDataSet.FILL_VALUE, 0, ds.property(QDataSet.FILL_VALUE) ); bundleDs= bds1; } else { DDataSet bds1= DDataSet.createRank2(ds.length(0),0); QDataSet dep1= (QDataSet) ds.property(QDataSet.DEPEND_1); Units u= dep1==null ? Units.dimensionless : SemanticOps.getUnits(dep1); for ( int i=0; i0; } else { boolean valid= true; for ( int i=0; i0; } return valid; } } /** * This will set the focus range for the TimeSeriesBrowse, if available, * and then call each tick individually. * @param parms * @return */ @Override public synchronized QDataSet values( QDataSet parms ) { if ( initialError!=null ) { if ( ds==null ) { return new BundleDataSet( error ); } } QDataSet tt= Ops.copy( Ops.unbundle(parms, 0 ) ); QDataSet dtt= Ops.diff( tt ); QDataSet gcd; try { gcd= DataSetUtil.gcd( dtt, Ops.divide( dtt.slice(0),100 ) ); } catch ( IllegalArgumentException ex ) { ex.printStackTrace(); gcd= Ops.reduceMin( dtt, 0 ); } Datum d; DatumRange dr= null; // calculate the bounding DatumRange for all params. for ( int i=0; i200 ) { System.err.println("check suppressed bad read..."); context=null; } } if ( context!=null ) dr= DatumRangeUtil.union( dr, DataSetUtil.asDatumRange(context,true) ); tsb.setTimeRange(dr); } } } try { if ( read ) { doRead(); logger.log( Level.FINER, "loaded dataset: {0} {1} ", new Object[]{ tsb!=null ? tsb.getTimeRange() : "", ds } ); } if ( ds==null ) { BundleDataSet result= new BundleDataSet( errorNoDs ); ((MutablePropertyDataSet)result).putProperty( QDataSet.UNITS, errorNoDs.property(QDataSet.UNITS) ); return result; } QDataSet dep0= SemanticOps.xtagsDataSet(ds); QDataSet d0= parm.slice(0); QDataSet findex; if ( dep0.length()==1 ) { findex= Ops.dataset(0); } else { findex= Ops.findex( dep0, d0 ); if ( Math.abs( findex.value() % 1.0 ) > 0.1 ) { logger.log(Level.FINE, "interpolating to calculate tick for {0}", d); } } QDataSet result; if ( findex.value()>=-0.5 && findex.value()=dep0.length() ) imax= dep0.length()-1; int irad= Math.max( ii-imin, imax-ii ); for ( int iiii= 1; iiii= imin ) { result= ds.slice(ii-iiii); if ( isValid(result) ) { break; } } if ( ii+iiii <= imax ) { result= ds.slice(ii+iiii); if ( isValid(result) ) { break; } } } } else { logger.log( Level.FINER, "findex={0} for {1} {2}", new Object[]{findex, d0, result}); if ( deltaPlus!=null ) { QDataSet delta= Ops.magnitude( Ops.subtract( d0, dep0.slice(ii) ) ); if ( Ops.gt( delta, tlim ).value()==1 ) { BundleDataSet result1= new BundleDataSet( nonValueDs ); for ( int i=1; idep0.length()-1 && ( Ops.ge( Ops.add( dep0.slice(dep0.length()-1), deltaMinus ), d0 ).value()==1 ) ) { result= ds.slice(dep0.length()-1); } else if ( findex.value()<0 && ( Ops.le( Ops.subtract( dep0.slice(0), deltaPlus ), d0 ).value()==1 ) ) { result= ds.slice(0); } else { if ( tsb==null ) { BundleDataSet result1= new BundleDataSet( nonValueDs ); for ( int i=1; i