package org.das2.qds.examples; import java.text.ParseException; import java.util.logging.Level; import java.util.logging.Logger; import org.das2.datum.EnumerationUnits; import org.das2.datum.Units; import org.das2.datum.UnitsUtil; import org.das2.qds.ArrayDataSet; import org.das2.qds.DDataSet; import org.das2.qds.DataSetUtil; import org.das2.qds.JoinDataSet; import org.das2.qds.MutablePropertyDataSet; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.das2.qds.SparseDataSetBuilder; import org.das2.qds.WritableDataSet; import org.das2.qds.ops.Ops; import static org.das2.qds.ops.Ops.PI; import static org.das2.qds.ops.Ops.linspace; import static org.das2.qds.ops.Ops.ripples; /** * For the various QDataSet schemes, show examples and documentation for * each. This was motivated when trying to describe the output of * org.das2.graph.ContoursRenderer.doAutorange() * * Note all QDataSets are "duck-typed," meaning if they happen to meet the * requirements of an interface then they are an instance of the interface. * * @author jbf */ public class Schemes { private static Logger logger= Logger.getLogger("qdataset.schemes"); /** * return a bounding box for the data. This is a rank 2 dataset where * ds[0,:] shows the bounds for the first dimension and ds[1,:] shows the * bounds for the second dimension. Therefor ds[0,0] is the minumum extent * of the first dimension, and ds[0,1] is the maximum. * Note this can be extended to any number * of dimensions (cube or hypercube). * * Note, *
     *from org.das2.qds.examples import Schemes
     *ds= Schemes.boundingBox()
     *print asDatumRange(ds.slice(0))
     *
* * @return a bounding box for the data. * @see org.das2.qds.DataSetUtil#asDatumRange(org.das2.qds.QDataSet) */ public static QDataSet boundingBox( ) { try { QDataSet xx= Ops.timegen( "2015-02-20T00:30", "60 s", 1440 ); QDataSet yy= Ops.linspace( 14., 16., 1440 ); JoinDataSet bds= new JoinDataSet(2); bds.join( Ops.extent( xx ) ); bds.join( Ops.extent( yy ) ); bds.makeImmutable(); return bds; } catch (ParseException ex) { throw new RuntimeException(ex); } } /** * return true if the data is a boundingBox. * @param ds a dataset * @return true if the dataset is a bounding box. */ public static boolean isBoundingBox( QDataSet ds ) { return ds.rank()==2 && ds.length(0)==2 && ds.length()>0; } /** * return a rank 2 waveform, where the waveform is stored in packets. * DEPEND_0 is the time for each packet, and DEPEND_1 is the difference in * time for each measurement to the packet time. Note the additional requirement * that the offsets be uniform, e.g.: *
     *from org.das2.qds.examples import Schemes
     *ds= Schemes.rank2Waveform()
     *deltaT= ds.property( QDataSet.DEPEND_1 )
     *ddeltaT= diffs(dep1)
     *print ddeltaT[0], ddeltT[-1] # should be the same
     *
* * @return rank 2 waveform. */ public static QDataSet rank2Waveform( ) { return Ops.ripplesWaveformTimeSeries(20); } /** * return true if the data is a rank 2 waveform. * @param ds a dataset * @return true if the data is a rank 2 waveform. */ public static boolean isRank2Waveform( QDataSet ds ) { if ( ds.rank()!=2 ) return false; QDataSet dep0= (QDataSet)ds.property(QDataSet.DEPEND_0); if ( dep0==null ) return false; Units u0= SemanticOps.getUnits(dep0); if ( u0==Units.dimensionless ) { return false; } else { QDataSet dep1= (QDataSet)ds.property(QDataSet.DEPEND_1); if ( dep1==null ) return false; if ( dep1.length()
     *from org.das2.qds.examples import Schemes
     *ds= Schemes.vectorTimeSeries()
     *plot( magnitude( ds ) )
     *plot( unbundle( ds, 0 ) )
     *
* dataset→rank2bundle→vectorTimeSeries. * @return rank 2 vector time series. */ public static QDataSet vectorTimeSeries( ) { return Ops.ripplesVectorTimeSeries(1440); } /** * return true if the data is a vector time series. * @param ds a dataset * @return true if the data is a vector time series. */ public static boolean isVectorTimeSeries( QDataSet ds ) { return ds.rank()==2 && ( Ops.isLegacyBundle(ds) || Ops.isBundle(ds) ) && isTimeSeries(ds); } /** * return a rank 2 simple spectrogram, which has two indeces. * @return rank 2 simple spectrogram */ public static QDataSet simpleSpectrogram() { return Ops.ripples(40,30); } /** * return true if the data is a simple spectrogram, which is * rank 2, and not a small bundle. * @param ds a dataset * @return true if the data is a simple spectrogram. */ public static boolean isSimpleSpectrogram( QDataSet ds ) { if ( ds.rank()==2 ) { return !(ds.length(0)<4 && ( Ops.isBundle(ds) || Ops.isLegacyBundle(ds) )); } else { return false; } } /** * return a rank 1 scalar time series. * @return a rank 1 scalar time series. */ public static QDataSet scalarTimeSeries() { try { QDataSet density= Ops.add( Ops.ripples(20), Ops.randomn(0,20) ); density= Ops.putProperty( density, QDataSet.UNITS, Units.pcm3 ); QDataSet t = Ops.timegen("2011-10-24", "20 sec", 20 ); return Ops.link( t, density ); } catch (ParseException ex) { throw new RuntimeException(ex); } } /** * return true if the data is a simple spectrogram. * @param ds a dataset * @return true if the data is a simple spectrogram. */ public static boolean isScalarTimeSeries( QDataSet ds ) { return ds.rank()==1 && isTimeSeries(ds); } /** * return a rank 2 simple spectrogram, which has two indeces * and is a TimeSeries. * @return simple spectrogram time series */ public static QDataSet simpleSpectrogramTimeSeries() { return Ops.ripplesSpectrogramTimeSeries(1440); } /** * return true if the data is a simple spectrogram. * @param ds a dataset * @return true if the data is a simple spectrogram. */ public static boolean isSimpleSpectrogramTimeSeries( QDataSet ds ) { return isSimpleSpectrogram(ds) && isTimeSeries(ds); } /** * returns true if the dataset is a time series. This is either something * that has DEPEND_0 as a dataset with time location units, or a join of * other datasets that are time series. * @param ds a dataset * @return true if the dataset is a time series. * @see SemanticOps#isTimeSeries(org.das2.qds.QDataSet) */ public static boolean isTimeSeries( QDataSet ds ) { return SemanticOps.isTimeSeries(ds); } /** * uniform cadence is when each tag is the same distance apart, within a reasonable threshold. * @return dataset with uniform cadence */ public static QDataSet uniformCadence() { return Ops.linspace( 0., 4., 100 ); } /** * return true of the data has a uniform cadence. Note that * the CADENCE property is ignored. * @param ds a rank 1 dataset * @return true if the data has uniform cadence. */ public static boolean isUniformCadence( QDataSet ds ) { if ( ds.rank()!=1 ) return false; double dv= ds.value(1)-ds.value(0); double manyDv= ( ds.value(ds.length()-1)-ds.value(0) ) / ( ds.length()-1) ; return ( ( manyDv - dv ) / dv ) < 0.001; } /** * uniform ratiometric cadence is when the tags are uniform in log space. * @return dataset with uniform ratiometric cadence. */ public static QDataSet uniformRatiometricCadence() { return Ops.pow( 10, Ops.linspace( 0., 4., 100 ) ); } /** * return true of the data has a uniform cadence. Note that * the CADENCE property is ignored. * @param ds a rank 1 dataset * @return true if the data has uniform cadence. */ public static boolean isUniformRatiometricCadence( QDataSet ds ) { if ( ds.rank()!=1 ) return false; double dv= Math.log( ds.value(1)/ds.value(0) ); double manyDv= Math.log( ds.value(ds.length()-1) / ds.value(0) ) / ( ds.length()-1 ); return ( ( manyDv - dv ) / dv ) < 0.001; } /** * return an example of a compositeImage. * @return image[320,240,3] */ public static QDataSet compositeImage() { WritableDataSet rgb= Ops.zeros( 320, 240, 3 ); for ( int i=0; i<320; i++ ) { for ( int j=0; j<240; j++ ) { if ( ( Math.pow((i-160),2) + Math.pow((j-120),2) ) <2500 ) rgb.putValue( i,j,0, 255 ); if ( i<160 ) rgb.putValue( i,j,1, 255 ); if ( j<120 ) rgb.putValue( i,j,2, 255 ); } } rgb.putProperty( QDataSet.DEPEND_0, Ops.linspace(0,4,rgb.length() ) ); rgb.putProperty( QDataSet.DEPEND_1, Ops.pow( 10, Ops.linspace(0,4,rgb.length(0) ) ) ); rgb.putProperty( QDataSet.RENDER_TYPE, QDataSet.VALUE_RENDER_TYPE_COMPOSITE_IMAGE ); return rgb; } /** * return true if the dataset is a composite image, and is plottable * with the RGBImageRenderer * @param ds a dataset * @return true if the dataset is a composite image. */ public static boolean isCompositeImage( QDataSet ds ) { QDataSet dep0= (QDataSet) ds.property(QDataSet.DEPEND_0); QDataSet dep1= (QDataSet) ds.property(QDataSet.DEPEND_1); return ds.rank()==3 && DataSetUtil.checkQube(ds) && ( dep0==null || isUniformCadence(dep0) || isUniformRatiometricCadence(dep0) ) && ( dep1==null || isUniformCadence(dep1) || isUniformRatiometricCadence(dep1) ); } /** * return example events list. This is a four-column rank 2 dataset with * start time, end time, RGB color, and ordinal data for the message. * @return example events list. */ public static QDataSet eventsList( ) { try { QDataSet xx= Ops.timegen( "2015-01-01", "60s", 1440 ); QDataSet dxx= Ops.putProperty( Ops.replicate( 45, 1440 ), QDataSet.UNITS, Units.seconds ); QDataSet color= Ops.replicate( 0xFFAAAA, 1440 ); for ( int i=100; i<200; i++ ) ((WritableDataSet)color).putValue( i, 0xFFAAFF ); EnumerationUnits eu= EnumerationUnits.create("default"); QDataSet msgs= Ops.putProperty( Ops.replicate( eu.createDatum("on1").doubleValue(eu), 1440 ), QDataSet.UNITS, eu ); QDataSet result= Ops.bundle( xx, Ops.add(xx,dxx), color, msgs ); return result; } catch (ParseException ex) { throw new IllegalArgumentException(ex); } } /** * return true if the data is an events list. * @param ds a dataset * @return true if the data is an events list. */ public static boolean isEventsList( QDataSet ds ) { QDataSet bundle1= (QDataSet) ds.property(QDataSet.BUNDLE_1); if ( bundle1!=null ) { if ( bundle1.length()==3 || bundle1.length()==4 || bundle1.length()==5 ) { Units u0= (Units) bundle1.property(QDataSet.UNITS,0); if ( u0==null ) u0= Units.dimensionless; Units u1= (Units) bundle1.property(QDataSet.UNITS,1); if ( u1==null ) u1= Units.dimensionless; Units u3= (Units) bundle1.property(QDataSet.UNITS,bundle1.length()-1); if ( u3!=null && UnitsUtil.isOrdinalMeasurement(u3) && u0.getOffsetUnits().isConvertibleTo(u1) ) { if ( u0.isConvertibleTo(u1) ) { QDataSet isge= Ops.ge( Ops.slice1( ds, 1 ), Ops.slice1( ds, 0 ) ); return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice1(ds,0) ) ); } else { QDataSet isge= Ops.ge( Ops.abs( Ops.slice1( ds, 1 ) ), 0. ); return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice1(ds,0) ) ); } } else if ( u3!=null && UnitsUtil.isOrdinalMeasurement(u3) && u0.isConvertibleTo(u1) ) { QDataSet isge= Ops.ge( Ops.slice1( ds, 1 ), Ops.slice1( ds, 0 ) ); return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice1(ds,0) ) ); } } else { Units u3= (Units) bundle1.property(QDataSet.UNITS,bundle1.length()-1); if ( u3!=null && UnitsUtil.isOrdinalMeasurement(u3) ) { return true; } } } else { if ( SemanticOps.getUnits(ds) instanceof EnumerationUnits ) { QDataSet dep0= (QDataSet) ds.property(QDataSet.DEPEND_0); if ( dep0!=null ) { return true; } } } return false; } /** * return example angle distribution. * @return example angle distribution. */ public static QDataSet angleDistribution( ) { ArrayDataSet rip= ArrayDataSet.maybeCopy( ripples( 30, 15 ) ); QDataSet angle= linspace( PI/30/2, PI-PI/30/2, 30 ); angle= Ops.putProperty( angle, QDataSet.UNITS, Units.radians ); QDataSet rad= linspace( 1, 5, 15 ); rip.putProperty( QDataSet.DEPEND_0, angle ); rip.putProperty( QDataSet.DEPEND_1, rad ); rip.putProperty( QDataSet.RENDER_TYPE, "pitchAngleDistribution" ); return rip; } /** * return example angle distribution. * @param i the example number. 0..n-1. * @return example angle distribution. */ public static QDataSet angleDistribution( int i ) { if ( i==0 ) { ArrayDataSet rip= ArrayDataSet.maybeCopy( Ops.randn( 24, 15 ) ); for ( int j= 0; j<15; j++ ) { rip.putValue( 0, j, 20. ); rip.putValue( 4, j, 20. ); } QDataSet angle= Ops.multiply( linspace( 0.5, 23.5, 24 ), 15 ); angle= Ops.putProperty( angle, QDataSet.UNITS, Units.degrees ); QDataSet rad= linspace( 1, 5, 15 ); rip.putProperty( QDataSet.DEPEND_0, angle ); rip.putProperty( QDataSet.DEPEND_1, rad ); rip.putProperty( QDataSet.RENDER_TYPE, "pitchAngleDistribution" ); return rip; } else { return null; } } /** * return true if the data is an angle distribution. * @param ds a dataset * @return true if the data is an angle distribution. */ public static boolean isAngleDistribution( QDataSet ds ) { if ( ds.rank()!=2 ) return false; QDataSet ads= (QDataSet)ds.property(QDataSet.DEPEND_0); //QDataSet rds= (QDataSet)ds.property(QDataSet.DEPEND_1); Units au= (Units) ads.property(QDataSet.UNITS); if ( au!=null && !( au==Units.dimensionless || au.isConvertibleTo(Units.degrees) ) ) { return false; } return true; } /** * A complexBundleDataSet is a set of datasets which have different rank. The * rank 2 datasets take multiple columns of the dataset, and rank 3 and 4 datasets * are unrolled to make them rank 2 and the property ELEMENT_DIMENSIONS is used * to re-form them. This returns an example bundle dataset that bundles timetags, density, velocity, and flux. * Yes, this was coded this twice, not realizing it was already done. This code * is probably easier to read, so it is left in. * * @return an example bundle dataset * @see #complexBundleDataSet() */ public static QDataSet complexBundleDataSet2() { DDataSet data= DDataSet.createRank2( 12, 9 ); QDataSet ttags= Ops.linspace( "2019-03-07T00:30Z", "2019-03-07T11:30Z", 12 ); QDataSet vel= vectorTimeSeries(); QDataSet dens= scalarTimeSeries(); QDataSet flux= simpleSpectrogram(); for ( int i=0; i