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.QubeDataSetIterator; 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; import org.das2.util.ColorUtil; /** * 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 a canonical event
* @return a canonical event
*/
public static QDataSet canonicalEvent() {
QDataSet ds= Ops.bundle( Ops.dataset("2025-05-06T14:29"),
Ops.dataset("2025-05-06T14:31"),
Ops.dataset(Units.rgbColor.createDatum(ColorUtil.SKY_BLUE.getRGB())),
Ops.dataset(Units.nominal().createDatum("Sunny and Warm")) );
DataSetUtil.toString(ds);
return ds;
}
/**
* returns true if the dataset is a rank 1 canonical event, having
* three or four elements, the first two are times or the same units and
* the last is a nominal datum describing the event.
* @param ds dataset
* @return true if is a canonical event.
*/
public static boolean isCanonicalEvent( QDataSet ds ) {
if ( ds.rank()!=1 ) return false;
QDataSet bundle0= (QDataSet) ds.property(QDataSet.BUNDLE_0);
if ( bundle0!=null ) {
if ( bundle0.length()==3 || bundle0.length()==4 || bundle0.length()==5 ) {
Units u0= (Units) bundle0.property(QDataSet.UNITS,0);
if ( u0==null ) u0= Units.dimensionless;
Units u1= (Units) bundle0.property(QDataSet.UNITS,1);
if ( u1==null ) u1= Units.dimensionless;
Units u3= (Units) bundle0.property(QDataSet.UNITS,bundle0.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.slice0( ds, 1 ), Ops.slice0( ds, 0 ) );
return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice0(ds,0) ) );
} else {
QDataSet isge= Ops.ge( Ops.abs( Ops.slice0( ds, 1 ) ), 0. );
return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice0(ds,0) ) );
}
} else if ( u3!=null && UnitsUtil.isOrdinalMeasurement(u3) && u0.isConvertibleTo(u1) ) {
QDataSet isge= Ops.ge( Ops.slice0( ds, 1 ), Ops.slice0( ds, 0 ) );
return Ops.total(isge) == Ops.total( Ops.valid( Ops.slice0(ds,0) ) );
}
} else {
Units u3= (Units) bundle0.property(QDataSet.UNITS,bundle0.length()-1);
if ( u3!=null && UnitsUtil.isOrdinalMeasurement(u3) ) {
return true;
}
}
}
return false; //TODO: this means a slice of some events lists are not events!
}
/**
* 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