/* * DataSetAdapter.java * * Created on April 2, 2007, 8:49 AM * * To change this template, choose Tools | Template Manager * and open the template in the editor. */ package org.das2.dataset; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.das2.datum.Datum; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import org.das2.datum.InconvertibleUnitsException; import org.das2.datum.Units; import org.das2.system.DasLogger; import org.das2.qds.AbstractDataSet; import org.das2.qds.DDataSet; import org.das2.qds.DRank0DataSet; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.das2.qds.ops.Ops; /** * Presents legacy das2 datasets as QDataSets. See also TableDataSetAdapter,VectorDataSetAdapter * * @author jbf */ public class DataSetAdapter { private static final Logger logger = DasLogger.getLogger(DasLogger.DATA_TRANSFER_LOG); public static final String PROPERTY_SOURCE = "adapterSource"; /////////////////////////////////////////////////////////////////////////////////// // Helper for conversion such as %{xCacheRange} -> %{USER_PROPERTIES.xCacheRange} protected static Map adaptSubstitutions(Map das2props) { // Defines a pattern with three subgroups Pattern ptrn = Pattern.compile("(%\\{)(.+?)(\\})"); for (Map.Entry e : das2props.entrySet()) { Object o = e.getValue(); if (!(o instanceof String)) { continue; } String s = (String) o; Matcher m = ptrn.matcher(s); while (m.find()) { // Group indices are not as expected, 0 = entire match, 1 = 1st group, etc. if (!m.group(2).contains("USER_PROPERTIES")) { s = String.format("%sUSER_PROPERTIES.%s%s", s.substring(0, m.end(1)), s.substring(m.start(2), m.end(2)), s.substring(m.start(3), s.length())); m = ptrn.matcher(s); } } e.setValue(s); } return das2props; } /** * Created a new QDataSet given a Das2 DataSet * * This function and createLegacyDataSet() are inverses, though a round trip conversion * is not guaranteed to preserve all properties * * @param ds A Das2 Dataset * @return A new QDataSet */ public static AbstractDataSet create(DataSet ds) { if (ds == null) throw new NullPointerException("dataset is null"); // X-Y Datasets if (ds instanceof VectorDataSet) { boolean newCodeWhichSupportsBinMinPlanes= false; if (newCodeWhichSupportsBinMinPlanes) { VectorDataSet vds = (VectorDataSet) ds; return createVectorQds(vds); } else { if (ds.getPlaneIds().length <= 1) { //Handle x single y as a simple vector Vector v = new Vector((VectorDataSet) ds); String sname = (String) ds.getProperty("name"); if (sname == null) { sname = "y"; } v.putProperty(QDataSet.NAME, sname); return v; } else { //Handle x multi y as a bundle VectorDataSet vds = (VectorDataSet) ds; Vector v = new Vector(vds); String sname = (String) ds.getProperty("name"); if (sname == null) { sname = "y"; } v.putProperty(QDataSet.NAME, sname); AbstractDataSet bds = (AbstractDataSet) Ops.bundle(null, v); String[] planes = ds.getPlaneIds(); Units unitsY = null; boolean bCommonYUnits = false; for (int i = 1; i < planes.length; i++) { // Arg, everything we want to get at is hidden behind 7 levels of // interfaces. As a bonus, class names repeat in different packages from // the same dev group. org.das2.dataset.AbstractDataSet.ViewDataSet view = (org.das2.dataset.AbstractDataSet.ViewDataSet) vds.getPlanarView(planes[i]); if (unitsY == null) { unitsY = view.getYUnits(); } else { bCommonYUnits = (unitsY == view.getYUnits()); } v = new Vector((VectorDataSet) vds.getPlanarView(planes[i]), planes[i]); v.putProperty(QDataSet.NAME, planes[i]); Ops.bundle(bds, v); } // Convert Das2 property substitutions to USER_PROPERTIES substitutions Map dasProps = adaptSubstitutions(vds.getProperties()); bds.putProperty(QDataSet.USER_PROPERTIES, dasProps); bds.putProperty(QDataSet.DEPEND_0, new XTagsDataSet(vds)); bds.putProperty(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); // If all Y elements of the bundle have the same units, put those units // on the Y axis, that way something identifies Y. if (bCommonYUnits) { bds.putProperty(QDataSet.UNITS, unitsY); bds.putProperty(QDataSet.LABEL, unitsY.toString()); } // Copy more properties into the overall bundle dataset, wow this really // needs to be refactored. bds.putProperty(QDataSet.SCALE_TYPE, vds.getProperty(DataSet.PROPERTY_Y_SCALETYPE)); DatumRange yRng = (DatumRange) vds.getProperty(DataSet.PROPERTY_Y_RANGE); if (yRng != null) { bds.putProperty(QDataSet.TYPICAL_MIN, yRng.min().value()); bds.putProperty(QDataSet.TYPICAL_MAX, yRng.max().value()); } return DDataSet.copy(bds); } } } // X-YScan Datasets if (ds instanceof TableDataSet) { TableDataSet tds = (TableDataSet) ds; if (tds.tableCount() <= 1) { // Handling table datasets that may come with statistics planes. These are // denoted by having a "source" and "operations" string properties. return createSimpleTableDS(tds); } else { if (tds instanceof DefaultTableDataSet && tds.tableCount() > tds.getXLength() / 2) { return ((DefaultTableDataSet) tds).toQDataSet(); } else { return new MultipleTable(tds); } } } throw new IllegalArgumentException("unsupported dataset type: " + ds.getClass().getName()); } /////////////////////////////////////////////////////////////////////////////////// // Helper for setting up Vector QDataSet's that have statistics private static class AdapterPDim{ String sId; Map vars = new HashMap<>(); AdapterPDim(String _sId){sId = _sId;} } private static AbstractDataSet createVectorQds(VectorDataSet vds){ // The thing we care about are the number of datasets (or phys-dims in das2 speak) // in this X-Y set, not really the number of variables. If the number of physdims // is greater than one, we need a to make a bundle. Count phys-dims here. // The das2.2 stream format did not group variables into related sets with a natural // enclosing tag, instead property values were used to do this, which is... messy. // das2.3 streams are much better in this regard. String[] lPlanes = vds.getPlaneIds(); Map lPDims = new HashMap<>(); // 1st pass, find top level planes, side effect, see if have common Y units int nStashed = 0; Units commonY = null; boolean bCommonYUnits = false; for(String sId: lPlanes){ DataSet ds = vds.getPlanarView(sId); // If this has an operation property other than avg, it's not top level String sOp = (String)ds.getProperty(DataSet.PROPERTY_OPERATION); if((sOp != null)&&(!sOp.equals("BIN_AVG"))) continue; if(lPDims.containsKey(sId)) throw new IllegalArgumentException("Non-unique plane IDs in das2 X-multi-Y packet"); String sSource = (String)ds.getProperty(DataSet.PROPERTY_SOURCE); lPDims.put(sSource, new AdapterPDim(sId)); if(commonY == null) commonY = ds.getYUnits(); else bCommonYUnits = (commonY == ds.getYUnits()); nStashed += 1; } // 2nd pass, find statistics planes. for(String sId: lPlanes){ if(sId == null) continue; DataSet ds = vds.getPlanarView(sId); String sOp = (String)ds.getProperty(DataSet.PROPERTY_OPERATION); if( (sOp == null) || sOp.equals("BIN_AVG")) continue; String sSource = (String)ds.getProperty(DataSet.PROPERTY_SOURCE); AdapterPDim pdim = lPDims.get(sSource); if(pdim == null) throw new IllegalArgumentException("Statistics plane has no parent in x-multi-y packet"); pdim.vars.put(sOp, sId); nStashed += 1; } if(nStashed != lPlanes.length) throw new IllegalArgumentException("Not all plane purposes understood in x-multi-y packet"); // Now build a QDataSet, if multiple physicsal dimensions present, will need a bundle ds. AbstractDataSet bds = null; Vector v = null; for( Entry e : lPDims.entrySet() ) { AdapterPDim pdim = e.getValue(); org.das2.dataset.AbstractDataSet.ViewDataSet view = (org.das2.dataset.AbstractDataSet.ViewDataSet) vds.getPlanarView(pdim.sId); v = new Vector((VectorDataSet)view, pdim.sId); String sName= (String)view.getProperty("name"); if(sName == null) sName= "y"; v.putProperty(QDataSet.NAME, sName ); // Attach stats planes if present. for(String sOp: pdim.vars.keySet()){ if(sOp == null) continue; // TODO: Complain to logger here String sId = pdim.vars.get(sOp); QDataSet qdsStats = new Vector((VectorDataSet) vds.getPlanarView(sId), sId); if(sOp.equals("BIN_MAX")) v.putProperty(QDataSet.BIN_MAX, qdsStats); if(sOp.equals("BIN_MIN")) v.putProperty(QDataSet.BIN_MIN, qdsStats); if(sOp.equals("DELTA_PLUS")) v.putProperty(QDataSet.DELTA_PLUS, qdsStats); if(sOp.equals("DELTA_MINUS")) v.putProperty(QDataSet.DELTA_MINUS, qdsStats); } // The bundle decision. If this physical dimension is part of a bundle, the X-tags, and // properties have to be copied over. if(lPDims.size() > 1){ if(bds == null){ bds = (AbstractDataSet) Ops.bundle(null, v); // If all top-level elements have the same units, put those units on the Y axis, // that way something identifies Y. if((bCommonYUnits)&&(commonY != null)) { bds.putProperty(QDataSet.UNITS, commonY); bds.putProperty(QDataSet.LABEL, commonY.toString()); } // Convert Das2 property substitutions to USER_PROPERTIES substitutions Map dasProps = adaptSubstitutions(vds.getProperties()); bds.putProperty(QDataSet.USER_PROPERTIES, dasProps); bds.putProperty(QDataSet.DEPEND_0, new XTagsDataSet(vds)); bds.putProperty(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); // Copy more properties into the overall bundle dataset, wow this really // needs to be refactored. bds.putProperty(QDataSet.SCALE_TYPE, vds.getProperty(DataSet.PROPERTY_Y_SCALETYPE)); DatumRange yRng = (DatumRange) vds.getProperty(DataSet.PROPERTY_Y_RANGE); if (yRng != null) { bds.putProperty(QDataSet.TYPICAL_MIN, yRng.min().value()); bds.putProperty(QDataSet.TYPICAL_MAX, yRng.max().value()); } } else{ Ops.bundle(bds, v); } } } // Output either the bundle or the single dataset if(bds != null) return DDataSet.copy(bds); else return v; } /** Created a new Das2 DataSet given a QDataSet * * This function and create() are inverses, though a round trip conversion is not guaranteed to * preserve all properties. Note that not all QDataSets can be represented as Das2 DataSets. If * the given QDataSet has no Das2 analog, an IllegalArgumentException is thrown. * * @param ds A QDataSet * @return A new Das2 DataSet * * DON'T USE THIS CODE! Use org.das2.qstream.QdsToD2sStream IN THE QStream project! */ @Deprecated public static DataSet createLegacyDataSet(org.das2.qds.QDataSet ds) { if (ds.rank() == 1) { return VectorDataSetAdapter.create(ds); } else if (SemanticOps.isBundle(ds)) { return VectorDataSetAdapter.createFromBundle(ds); } else if (ds.rank() == 2) { return TableDataSetAdapter.create(ds); } else if (ds.rank() == 3) { return TableDataSetAdapter.create(ds); } else { throw new IllegalArgumentException("unsupported rank: " + ds.rank()); } } /////////////////////////////////////////////////////////////////////////////////// // Helper dataset holds DEPEND_0 for MultipleTable QDataSets static class MultiTableXTagsDataSet extends AbstractDataSet { DataSet source; int offset; int length; MultiTableXTagsDataSet(DataSet source, int offset, int length) { this.source = source; this.offset = offset; this.length = length; properties.put(QDataSet.UNITS, source.getXUnits()); properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_X_LABEL)); Object o = source.getProperty(DataSet.PROPERTY_X_MONOTONIC); if (o != null) { properties.put(QDataSet.MONOTONIC, o); } Datum xTagWidth = (Datum) source.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); if (xTagWidth != null) { properties.put(QDataSet.CADENCE, org.das2.qds.DataSetUtil.asDataSet(xTagWidth)); } } @Override public int rank() { return 1; } @Override public double value(int i) { return source.getXTagDouble(i + offset, source.getXUnits()); } @Override public int length() { return length; } } /////////////////////////////////////////////////////////////////////////////////// // Helper dataset, holds DEPEND_0 for Vector & SimpleTable QDataSets static class XTagsDataSet extends AbstractDataSet { org.das2.dataset.DataSet source; XTagsDataSet(org.das2.dataset.DataSet source) { this.source = source; properties.put(QDataSet.UNITS, source.getXUnits()); properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_X_LABEL)); // QDataSet Cadences are a rank 0 dataset Datum d = (Datum) source.getProperty(DataSet.PROPERTY_X_TAG_WIDTH); if (d != null) { properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); } Object o = source.getProperty(org.das2.dataset.DataSet.PROPERTY_X_MONOTONIC); if (o != null) { properties.put(QDataSet.MONOTONIC, o); } } @Override public int rank() { return 1; } @Override public double value(int i) { return source.getXTagDouble(i, source.getXUnits()); } @Override public int length() { return source.getXLength(); } } /////////////////////////////////////////////////////////////////////////////////// // Top Level QDataSet for X vs Y data static class Vector extends AbstractDataSet { VectorDataSet source; // Time for more ugly hacks: TODO: Convert straight to QDataSet and get // rid of stupid crap like this. Vector(VectorDataSet source) { this(source, null); } private static Object hack(Map m, String k, String id) { if((id == null)||(id.isEmpty())) return m.get(k); else return m.get(id + "." + k); } // This constructor takes a plane ID so that property values can be gathered. // It's a dumb hack to get around: // 1. Das2 DataSet objects are immutable // 2. We are not converting straght to QDataSet Vector(VectorDataSet source, String sPlaneID) { super(); this.source = source; //Throw everything including the well-known stuff into user properties Map dasProps = adaptSubstitutions(source.getProperties()); properties.put(QDataSet.USER_PROPERTIES, dasProps); properties.put(QDataSet.TITLE, hack(dasProps, DataSet.PROPERTY_TITLE, sPlaneID)); properties.put(QDataSet.UNITS, source.getYUnits()); //properties.put(QDataSet.FILL_VALUE, source.getYUnits().getFillDouble() ); //Object ofill= hack(dasProps, DataSet.PROPERTY_Y_FILL, sPlaneID); //if ( ofill!=null && ( ofill instanceof Number ) ) properties.put(QDataSet.FILL_VALUE, ofill ); properties.put(QDataSet.LABEL, hack(dasProps, DataSet.PROPERTY_Y_LABEL, sPlaneID)); properties.put(QDataSet.FORMAT, hack(dasProps, DataSet.PROPERTY_Y_FORMAT, sPlaneID)); AbstractDataSet xds= new XTagsDataSet(source); xds.putProperty( QDataSet.CACHE_TAG, hack( dasProps, DataSet.PROPERTY_CACHE_TAG, sPlaneID ) ); properties.put(QDataSet.DEPEND_0, xds); properties.put(PROPERTY_SOURCE, source); // http://www.sarahandjeremy.net/~jbf/1wire/data/2007/0B000800408DD710.20071201.d2s uses property "valid_range" String syValid= (String)dasProps.get("valid_range"); if ( syValid!=null ) { try { DatumRange yValid= DatumRangeUtil.parseDatumRange( syValid, source.getYUnits() ); double val = yValid.min().doubleValue(source.getYUnits()); properties.put( QDataSet.VALID_MIN, val ); val = yValid.max().doubleValue(source.getYUnits()); properties.put( QDataSet.VALID_MAX, val ); } catch (ParseException ex) { logger.log(Level.SEVERE, null, ex); } } //New properties after 2014-05-28 Das2 Dev meeting Object obj = hack(dasProps, DataSet.PROPERTY_Y_VALID_MIN, sPlaneID); if ( obj != null ) { if ( obj instanceof Double ) { properties.put(QDataSet.VALID_MIN, (Double)obj); } else if ( obj instanceof Datum ) { properties.put(QDataSet.VALID_MIN, ((Datum)obj).doubleValue(source.getYUnits()) ); } else { logger.warning("property "+DataSet.PROPERTY_Y_VALID_MIN+" should be type Double"); } } obj = hack(dasProps, DataSet.PROPERTY_Y_VALID_MAX, sPlaneID); if ( obj != null ) { if ( obj instanceof Double ) { properties.put(QDataSet.VALID_MAX, (Double)obj); } else if ( obj instanceof Datum ) { properties.put(QDataSet.VALID_MAX, ((Datum)obj).doubleValue(source.getYUnits()) ); } else { logger.warning("property "+DataSet.PROPERTY_Y_VALID_MAX+" should be type Double"); } } properties.put(QDataSet.FILL_VALUE, hack(dasProps, DataSet.PROPERTY_Y_FILL, sPlaneID)); properties.put(QDataSet.SCALE_TYPE, hack(dasProps, DataSet.PROPERTY_Y_SCALETYPE, sPlaneID)); properties.put(QDataSet.MONOTONIC, hack(dasProps, DataSet.PROPERTY_Y_MONOTONIC, sPlaneID)); //Add this in after next autoplot update properties.put(QDataSet.DESCRIPTION, hack(dasProps, DataSet.PROPERTY_Y_SUMMARY, sPlaneID)); //Let Das2 Streams set a Y-Axis range DatumRange yRng = (DatumRange) hack(dasProps, DataSet.PROPERTY_Y_RANGE, sPlaneID); if (yRng != null) { try { properties.put(QDataSet.TYPICAL_MIN, yRng.min().doubleValue(source.getYUnits()) ); properties.put(QDataSet.TYPICAL_MAX, yRng.max().doubleValue(source.getYUnits()) ); } catch ( InconvertibleUnitsException ex ) { logger.info("yRange has inconvertible units"); } } Datum d = (Datum) hack(dasProps, DataSet.PROPERTY_Y_TAG_WIDTH, sPlaneID); if (d != null) { properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); } } @Override public int rank() { return 1; } @Override public double value(int i) { return source.getDouble(i, source.getYUnits()); } @Override public int length() { return source.getXLength(); } } /////////////////////////////////////////////////////////////////////////////////// // Helper Dataset, holds DEPEND_1 for SimpleTable QDataSets static class YTagsDataSet extends AbstractDataSet { TableDataSet source; int table; YTagsDataSet(TableDataSet source, int table) { this.source = source; this.table = table; properties.put(QDataSet.UNITS, source.getYUnits()); properties.put(QDataSet.LABEL, source.getProperty(DataSet.PROPERTY_Y_LABEL)); properties.put(QDataSet.SCALE_TYPE, source.getProperty(DataSet.PROPERTY_Y_SCALETYPE)); Datum d = (Datum) source.getProperty(DataSet.PROPERTY_Y_TAG_WIDTH); if (d != null) { properties.put(QDataSet.CADENCE, DRank0DataSet.create(d)); } DatumRange yRng = (DatumRange) source.getProperty(DataSet.PROPERTY_Y_RANGE); if (yRng != null) { properties.put(QDataSet.TYPICAL_MIN, yRng.min().value()); properties.put(QDataSet.TYPICAL_MAX, yRng.max().value()); } } @Override public int rank() { return 1; } @Override public double value(int i) { return source.getYTagDouble(table, i, source.getYUnits()); } @Override public int length() { return source.tableCount() > 0 ? source.getYLength(table) : 99; } } /////////////////////////////////////////////////////////////////////////////////// // Helper for setting up Table QDataSet's that have statistics private static AbstractDataSet createSimpleTableDS(TableDataSet tds){ // Find the top level plane, expect there to be only one since this isn't a bundle // dataset handler. String sTopDs = null; String sTopSrc = null; String[] lPlanes = tds.getPlaneIds(); if(lPlanes.length == 1){ sTopDs = lPlanes[0]; } else{ // Take the first plane that meets the criteria as the top-level dataset, the // criteria is, (1) Don't have a source property or (2) be the average for(String sPlane: lPlanes){ DataSet ds = tds.getPlanarView(sPlane); sTopSrc = (String)ds.getProperty(DataSet.PROPERTY_SOURCE); if(sTopSrc == null){ sTopDs = sPlane; break; } else{ String sOp = (String)ds.getProperty(DataSet.PROPERTY_OPERATION); if((sOp != null)&&(sOp.equals("BIN_AVG"))){ sTopDs = sPlane; break; } } } } // TODO: Use some sort of conversion exception instead of this if(sTopDs == null) throw new IllegalArgumentException( "Couldn't locate the top-level in the set of . "+ "HINT: If this is simple bundle then you've hit a missing feature in Autoplot."+ " If this is a peak and averages dataset, use the 'source' and 'operation'"+ " properties to clairify the relationships. " ); AbstractDataSet qds = new SimpleTable((TableDataSet)tds.getPlanarView(sTopDs)); // Create every other dataset and bind it to the top level one based on it's // operation property. If that's not specified OR it's source disagrees ignore it for(String sPlane: lPlanes){ if(sPlane.equals(sTopDs)) continue; TableDataSet dsPlane = (TableDataSet) tds.getPlanarView(sPlane); String sPlaneSrc = (String)dsPlane.getProperty(DataSet.PROPERTY_SOURCE); if( ((sPlaneSrc == null)&&(sTopSrc == null)) || sTopSrc.equals(sPlaneSrc)){ // Okay, they have the same source see if we understand the operation String sOp = (String)dsPlane.getProperty(DataSet.PROPERTY_OPERATION); if(sOp == null) continue; // TODO: Complain to logger here QDataSet qdsAncillary = new SimpleTable(dsPlane); if(sOp.equals("BIN_MAX")) qds.putProperty(QDataSet.BIN_MAX, qdsAncillary); if(sOp.equals("BIN_MIN")) qds.putProperty(QDataSet.BIN_MIN, qdsAncillary); if(sOp.equals(QDataSet.DELTA_PLUS)) qds.putProperty(sOp, qdsAncillary); if(sOp.equals(QDataSet.DELTA_MINUS)) qds.putProperty(sOp, qdsAncillary); // TODO: Complain to logger here } } return qds; } /////////////////////////////////////////////////////////////////////////////////// // Toplevel QDataSet for X,Y,Z "grid" data static class SimpleTable extends AbstractDataSet { TableDataSet source; SimpleTable(TableDataSet source) { super(); if (source.tableCount() > 1) { throw new IllegalArgumentException("only simple tables are supported"); } this.source = source; Map dasProps = adaptSubstitutions(source.getProperties()); // Save properterties with value substitution strings in Autoplot Stlye properties.put(QDataSet.USER_PROPERTIES, dasProps); properties.put(QDataSet.UNITS, source.getZUnits()); properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); properties.put(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); QDataSet xtags = new XTagsDataSet(source); properties.put(QDataSet.DEPEND_0, xtags); QDataSet ytags = new YTagsDataSet(source, 0); properties.put(QDataSet.DEPEND_1, ytags); properties.put(QDataSet.QUBE, Boolean.TRUE); properties.put(PROPERTY_SOURCE, source); //Let Das2 Streams set a Z-Axis range DatumRange zRng = (DatumRange) dasProps.get(DataSet.PROPERTY_Z_RANGE); if (zRng != null) { properties.put(QDataSet.TYPICAL_MIN, zRng.min().value()); properties.put(QDataSet.TYPICAL_MAX, zRng.max().value()); } properties.put(QDataSet.RENDER_TYPE, dasProps.get(DataSet.PROPERTY_RENDERER)); //properties.put(QDataSet.MONOTONIC, dasProps.get(DataSet.PROPERTY_X_MONOTONIC)); properties.put(QDataSet.FILL_VALUE, dasProps.get(DataSet.PROPERTY_Z_FILL)); properties.put(QDataSet.VALID_MIN, dasProps.get(DataSet.PROPERTY_Z_VALID_MIN)); properties.put(QDataSet.VALID_MAX, dasProps.get(DataSet.PROPERTY_Z_VALID_MAX)); properties.put(QDataSet.SCALE_TYPE, dasProps.get(DataSet.PROPERTY_Z_SCALETYPE)); properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); } @Override public int rank() { return 2; } @Override public int length(int i) { return source.getYLength(0); } @Override public double value(int i, int j) { return source.getDouble(i, j, source.getZUnits()); } @Override public int length() { return source.getXLength(); } } /////////////////////////////////////////////////////////////////////////////////// // Toplevel QDataSet for multiple sets of Z data on an X,Y "grid" static class MultipleTable extends AbstractDataSet { TableDataSet source; MultipleTable(TableDataSet source) { super(); this.source = source; Map dasProps = adaptSubstitutions(source.getProperties()); // Save properterties with value substitution strings in Autoplot Stlye properties.put(QDataSet.USER_PROPERTIES, dasProps); properties.put(QDataSet.JOIN_0, DDataSet.create(new int[0])); properties.put(QDataSet.UNITS, source.getZUnits()); properties.put(PROPERTY_SOURCE, source); properties.put(QDataSet.TITLE, dasProps.get(DataSet.PROPERTY_TITLE)); //Let Das2 Streams set Z-Axis properties DatumRange zRng = (DatumRange) dasProps.get(DataSet.PROPERTY_Z_RANGE); if (zRng != null) { properties.put(QDataSet.TYPICAL_MIN, zRng.min().value()); properties.put(QDataSet.TYPICAL_MAX, zRng.max().value()); } properties.put(QDataSet.RENDER_TYPE, dasProps.get(DataSet.PROPERTY_RENDERER)); //properties.put(QDataSet.MONOTONIC, dasProps.get(DataSet.PROPERTY_X_MONOTONIC)); properties.put(QDataSet.FILL_VALUE, dasProps.get(DataSet.PROPERTY_Z_FILL)); properties.put(QDataSet.VALID_MIN, dasProps.get(DataSet.PROPERTY_Z_VALID_MIN)); properties.put(QDataSet.VALID_MAX, dasProps.get(DataSet.PROPERTY_Z_VALID_MAX)); properties.put(QDataSet.SCALE_TYPE, dasProps.get(DataSet.PROPERTY_Z_SCALETYPE)); properties.put(QDataSet.LABEL, dasProps.get(DataSet.PROPERTY_Z_LABEL)); } @Override public int rank() { return 3; } @Override public int length() { return source.tableCount(); } @Override public int length(int i) { return source.tableEnd(i) - source.tableStart(i); } @Override public int length(int i, int j) { try { return source.getYLength(i); } catch (IndexOutOfBoundsException ex) { throw ex; } } @Override public double value(int i, int j, int k) { int ts = source.tableStart(i); try { return source.getDouble(ts + j, k, source.getZUnits()); } catch (IndexOutOfBoundsException ex) { throw ex; } } @Override public Object property(String name, int i) { if (name.equals(QDataSet.DEPEND_0)) { return new MultiTableXTagsDataSet(source, source.tableStart(i), source.tableEnd(i) - source.tableStart(i)); } else if (name.equals(QDataSet.DEPEND_1)) { return new YTagsDataSet(source, i); } else { return super.property(name, i); } } } }