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. * * All schemes can be executed using reflection, looking for methods starting * with "is". This Jython code scans through the class looking for all the * methods which make a dataset: *
{@code
 * mm= dir( Schemes )
 * i=0
 * for m in mm:
 * if ( m[0:2]=='is' ):
 *     continue
 * else:
 *     i=i+1
 * }
* * @see https://github.com/autoplot/dev/blob/master/bugs/2022/20220125/showAllRanks.md * @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) * @see #arrayOfBoundingBox() */ public static QDataSet boundingBox( ) { try { QDataSet xx= Ops.timegen( "2015-02-20T00:30", "60 s", 1440 ); QDataSet yy= Ops.putProperty( Ops.linspace( 14., 16., 1440 ), QDataSet.UNITS, Units.nT ); 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. A bounding box is * rank 2 and has length 2. ds[0,:] is typically the horizontal * direction, and ds[1,:] is the vertical. * * @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()==2; } /** * array of bounding boxes, joined by the zeroth dimension. * @return array of bounding boxes * @see #boundingBox() */ public static QDataSet arrayOfBoundingBox( ) { Ops.randomSeed(0); QDataSet xx = Ops.floor( Ops.add( Ops.multiply( Ops.randn(100), 10 ), 100) ); xx= Ops.putProperty( xx, QDataSet.UNITS, Units.MeV ); QDataSet yy = (MutablePropertyDataSet) Ops.randn(100); QDataSet bx = Ops.extent(xx); QDataSet by = Ops.extent(yy); QDataSet xx2 = Ops.floor( Ops.add( Ops.multiply( Ops.randn(100), 12), 170 ) ); xx2= Ops.putProperty( xx2, QDataSet.UNITS, Units.MeV ); QDataSet yy2 = Ops.add( Ops.randn(100), 2 ); QDataSet bx2 = Ops.extent(xx2); QDataSet by2 = Ops.extent(yy2); QDataSet xx3 = Ops.floor( Ops.add( Ops.multiply( Ops.randn(100),12), 140) ); xx3= Ops.putProperty( xx3, QDataSet.UNITS, Units.MeV ); QDataSet yy3 = Ops.subtract( Ops.randn(100), 1); QDataSet bx3 = Ops.extent(xx3); QDataSet by3 = Ops.extent(yy3); JoinDataSet bounds = (JoinDataSet)Ops.join(Ops.join(bx,by),Ops.join(bx2,by2)); bounds.join( Ops.join(bx3,by3) ); return bounds; } /** * return true if the data is a rank 3 array of bounding boxes. Presently only * 2-D bounding boxes are allowed. The zeroth dimension is the number of boxes, * and each slice is a bounding box. * @param ds * @return true if the data is a rank 3 array of bounding boxes. */ public static boolean isArrayOfBoundingBox( QDataSet ds ) { int[] dims= DataSetUtil.qubeDims(ds); return ds.rank()==3 && dims!=null && dims[1]==2 && dims[2]==2 ; } /** * 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 time series of scalars. * @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 1 scalar series with errors. * @return a rank 1 scalar series with errors. */ public static QDataSet scalarSeriesWithErrors() { QDataSet x= Ops.add( 1, Ops.divide( Ops.findgen(41),2 ) ); QDataSet y= Ops.exp( Ops.multiply( -1, Ops.pow( Ops.subtract(x,10), 2 ) ) ); MutablePropertyDataSet result= Ops.maybeCopy( Ops.link( x, y ) ); result.putProperty( QDataSet.DELTA_PLUS, Ops.replicate(0.04,41) ); result.putProperty( QDataSet.DELTA_MINUS, Ops.replicate(0.04,41) ); return result; } /** * return true is the data is a simple series of scalars with errors. * @param ds dataset * @return true is the data is a simple series of scalars with errors. */ public static boolean isScalarSeriesWithErrors( QDataSet ds ) { return ds.rank()==1 && ds.property(QDataSet.DELTA_PLUS)!=null && ds.property(QDataSet.DELTA_MINUS)!=null; } /** * 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 Math.abs( ( 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 in log space. 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; if ( !UnitsUtil.isRatioMeasurement( SemanticOps.getUnits(ds) ) ) 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 Math.abs( ( 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); boolean t1t2= UnitsUtil.isTimeLocation(u0) && UnitsUtil.isTimeLocation(u1); if ( t1t2 || ( 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 *
  • rank 2, *
  • length() gives the number of columns. *
  • length(i) gives the number of indices of the ith column, or the ith column's rank. *
  • property(i,NAME) gives the name of the column *
  • property(i,START_INDEX) gives the column which is the first column of the rank 2 or higher bundled data set. *
  • property(i,xxx) gives property xxx of the column *
  • property(i,DEPEND0_NAME) gives the name of the column containing the depend 0 values. * * @return data that describes the columns of another dataset. */ public static QDataSet bundleDescriptor() { QDataSet bundle= bundleDataSet(); return (QDataSet)bundle.property(QDataSet.BUNDLE_1); } /** * return a complex rank 2 dataset, N by 2, which can be thought of as a 1-D array of N complex numbers * @return a complex rank 2 dataset ds[N,2] * @see #isComplexNumbers(org.das2.qds.QDataSet) */ public static QDataSet rank2ComplexNumbers() { QDataSet w= rank2Waveform(); w= w.slice(0); return Ops.fft(w); } private static final QDataSet COMPLEX_COORDINATE_SYSTEM_DEPEND; static { EnumerationUnits u1 = EnumerationUnits.create("complexCoordinates"); DDataSet dep1 = DDataSet.createRank1(2); dep1.putValue(0, u1.createDatum("real").doubleValue(u1)); dep1.putValue(1, u1.createDatum("imag").doubleValue(u1)); dep1.putProperty(QDataSet.COORDINATE_FRAME, QDataSet.VALUE_COORDINATE_FRAME_COMPLEX_NUMBER); dep1.putProperty(QDataSet.UNITS, u1); COMPLEX_COORDINATE_SYSTEM_DEPEND= dep1; } /** * returns the QDataSet used to identify the columns of a complex coordinate frame. * @return the QDataSet used to identify the columns of a complex coordinate frame. */ public static QDataSet complexCoordinateSystemDepend() { return COMPLEX_COORDINATE_SYSTEM_DEPEND; } /** * return true if the data is length 2, rank 1, and has "ComplexNumber" as the COORDINATE_FRAME. * @param dep * @return */ public static boolean isComplexCoordinateSystemDepend( QDataSet dep ) { return dep.length()==2 && QDataSet.VALUE_COORDINATE_FRAME_COMPLEX_NUMBER.equals(dep.property(QDataSet.COORDINATE_FRAME)); } /** * return true if the data represents an array of complex numbers, containing the property COORDINATE_FRAME * on the last DEPEND, which is equal to "ComplexNumber" * @param ds1 a dataset * @return true if the data represents an array of complex numbers. * @see Ops#checkComplexArgument(org.das2.qds.QDataSet) */ public static boolean isComplexNumbers( QDataSet ds1 ) { int r= ds1.rank(); QDataSet dep; switch (r) { case 0: return false; case 1: if ( ds1.length()!=2 ) return false; dep= (QDataSet) ds1.property(QDataSet.DEPEND_0); break; case 2: if ( ds1.length(0)!=2 ) return false; dep= (QDataSet) ds1.property(QDataSet.DEPEND_1); break; default: return false; } if ( dep==null ) return false; return isComplexCoordinateSystemDepend(dep); } /** * return bundle with Time, Density, Speed, and Flux, to demonstrate * a bundle of datasets with differing rank. * @return bundle with Time, Density, Speed, and Flux * @see #complexBundleDataSet2() */ public static QDataSet complexBundleDataSet() { try { QDataSet tt= Ops.timegen( "2016-12-21T00:00", "60s", 1440 ); tt= Ops.putProperty( tt, QDataSet.NAME, "time" ); Ops.randomSeed(5334); QDataSet density= Ops.pow( 10, Ops.add( Ops.divide( Ops.randn(1440),10 ), 1 ) ); // 10**(1+randn/10) density= Ops.putProperty( density, QDataSet.UNITS, Units.pcm3 ); density= Ops.putProperty( density, QDataSet.NAME, "density" ); density= Ops.putProperty( density, QDataSet.DEPENDNAME_0, "time" ); QDataSet vv= Ops.transpose( Ops.reform( Ops.accum( Ops.randn(1440*3) ), new int[] { 3, 1440 } ) ); vv= Ops.putProperty( vv, QDataSet.UNITS, Units.cmps ); vv= Ops.putProperty( vv, QDataSet.NAME, "speed" ); vv= Ops.putProperty( vv, QDataSet.DEPENDNAME_0, "time" ); DDataSet ff= DDataSet.createRank2(1440,4); ff.putProperty( QDataSet.UNITS, Units.lookupUnits("s!E-1!Ncm!E-2!Nster!E-1!NkeV!E-1!N") ); ff.putProperty( QDataSet.NAME, "flux" ); for ( int i=0; i<1440; i++ ) { ff.putValue( i, 0, 23.0 + vv.value(i,0) ); ff.putValue( i, 1, 45.0 + vv.value(i,0) ); ff.putValue( i, 2, 31.0 + vv.value(i,0) ); ff.putValue( i, 3, 11.0 + vv.value(i,0) ); } ff.putProperty( QDataSet.DEPEND_1, Ops.pow( 10, linspace(1.,4.,4) ) ); QDataSet result= Ops.bundle( tt, density ); for ( int j=0; j