/* * 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.HashSet; 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.BundleDataSet; import org.das2.qds.DDataSet; import org.das2.qds.DRank0DataSet; import org.das2.qds.MutablePropertyDataSet; 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; HashMap names= new HashMap<>(); names.put("x",0); for (int i = 1; i < planes.length; i++) { names.put(planes[i],i); } 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()); } AbstractDataSet v0; int i0; // location of the data v = new Vector((VectorDataSet) vds.getPlanarView(planes[i]), planes[i]); int iext= planes[i].lastIndexOf("."); String ext= planes[i].substring(iext+1); String bas; if ( ext.length()==planes[i].length() ) { bas= ext; ext= ""; v0= null; i0= -1; } else { bas= planes[i].substring(0,planes[i].length()-ext.length()-1); v0= (AbstractDataSet)Ops.unbundle( bds, bas ); i0= names.get(bas); } MutablePropertyDataSet bundleDescriptor= (MutablePropertyDataSet)bds.property(QDataSet.BUNDLE_1); v.putProperty(QDataSet.NAME, planes[i]); if ( ext.equals("min") && v0!=null ) { v0.putProperty(QDataSet.BIN_MIN, v); bundleDescriptor.putProperty( QDataSet.BIN_MIN_NAME, i0, planes[i]); } else if ( ext.equals("max") && v0!=null ) { v0.putProperty(QDataSet.BIN_MAX, v); bundleDescriptor.putProperty( QDataSet.BIN_MAX_NAME, i0, planes[i]); } else if ( ext.equals("stddev") && v0!=null ) { v0.putProperty(QDataSet.DELTA_MINUS, v); v0.putProperty(QDataSet.DELTA_PLUS, v); bundleDescriptor.putProperty( QDataSet.DELTA_MINUS_NAME, i0, planes[i]); bundleDescriptor.putProperty( QDataSet.DELTA_PLUS_NAME, i0, 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()); } if ("xyzScatter".equals(ds.getProperty("schema"))) { BundleDataSet bbds = (BundleDataSet) bds; return (BundleDataSet) Ops.bundle(new XTagsDataSet(vds), bbds.unbundle(0), bbds.unbundle(1)); } if (bds.length(0) == 1) { return DDataSet.copy(Ops.unbundle(bds, 0)); } else { 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); } } } }