cdm/src/main/java/ucar/nc2/ft2/coverage/Time2DOffsetCoordSys.java
public Optional<List<CoverageCoordAxis>> subset(SubsetParams params, AtomicBoolean isConstantForcast, boolean makeCFcompliant) {
    List<CoverageCoordAxis> result = new ArrayList<>();

    Optional<CoverageCoordAxis> axiso = runAxis.subset(params);
    if (!axiso.isPresent())
      return Optional.empty(axiso.getErrorMessage());
    CoverageCoordAxis1D runAxisSubset = (CoverageCoordAxis1D) axiso.get();
    result.add(runAxisSubset);

    // subset on timeOffset (1a, 1c, 2a)
    if (params.hasTimeOffsetParam() || !params.hasTimeParam()) {
      axiso = timeOffset.subset(params);
      if (!axiso.isPresent())
        return Optional.empty(axiso.getErrorMessage());
      CoverageCoordAxis timeOffsetSubset = axiso.get();
      result.add(timeOffsetSubset);

      if (makeCFcompliant) // add a time cordinate
        result.add(makeCFTimeCoord(runAxisSubset, (CoverageCoordAxis1D) timeOffsetSubset)); // possible the twoD time case, if nruns > 1
      return Optional.of(result);
    }

    // subset on time, # runtimes = 1 (1b)
    if (runAxisSubset.getNcoords() == 1) {
      double val = runAxisSubset.getCoordMidpoint(0);   // not sure runAxis is needed. maybe use runtimeSubset
      CalendarDate runDate = runAxisSubset.makeDate(val);
      Optional<TimeOffsetAxis> too = timeOffset.subsetFromTime(params, runDate);
      if (!too.isPresent())
        return Optional.empty(too.getErrorMessage());
      TimeOffsetAxis timeOffsetSubset =  too.get();
      result.add(timeOffsetSubset);

      if (makeCFcompliant)
        result.add(makeCFTimeCoord(runAxisSubset, timeOffsetSubset));
      return Optional.of(result);
    }

    // tricky case 2b time (point only not range) = constant forecast dataset
    // data reader has to skip around the 2D times
    // 1) the runtimes may be subset by whats available
    // 2) timeOffset could become an aux coordinate
    // 3) time coordinate becomes a scalar,
    isConstantForcast.set(true);

    CalendarDate dateWanted;
    if (params.isTrue(SubsetParams.timePresent))
      dateWanted = CalendarDate.present();
    else
      dateWanted = (CalendarDate) params.get(SubsetParams.time);
    if (dateWanted == null)
      throw new IllegalStateException("Must have time parameter");

    double wantOffset = runAxisSubset.convert(dateWanted); // forecastDate offset from refdate
    double start = timeOffset.getStartValue();
    double end = timeOffset.getEndValue();
    CoordAxisHelper helper = new CoordAxisHelper(timeOffset);

    // brute force search LOOK specialize for regular ?
    List<Integer> runtimeIdx = new ArrayList<>();  // list of runtime indexes that have this forecast
    // List<Integer> offsetIdx = new ArrayList<>();  // list of offset indexes that have this forecast
    List<Double> offset = new ArrayList<>();      // corresponding offset from start of run
    for (int i=0; i<runAxisSubset.getNcoords(); i++) {
      // public double getOffsetInTimeUnits(CalendarDate convertFrom, CalendarDate convertTo);
      double runOffset = runAxisSubset.getCoordMidpoint(i);
      if (end + runOffset < wantOffset) continue;
      if (wantOffset < start + runOffset) break;
      int idx = helper.search(wantOffset - runOffset);
      if (idx >= 0) {
        runtimeIdx.add(i);  // the ith runtime
        // offsetIdx.add(idx);   // the idx time offset
        offset.add(wantOffset - runOffset);   // the offset from the runtime
      }
    }

    // here are the runtimes
    int ncoords = runtimeIdx.size();
    double[] runValues = new double[ncoords];
    double[] offsetValues = new double[ncoords];
    int count = 0;
    for (int k=0; k<ncoords; k++) {
      offsetValues[count] = offset.get(k);
      runValues[count++] = runAxisSubset.getCoordMidpoint(runtimeIdx.get(k));
    }

    CoverageCoordAxisBuilder runbuilder = new CoverageCoordAxisBuilder(runAxisSubset)
            .subset(null, CoverageCoordAxis.Spacing.irregularPoint, ncoords, runValues); // LOOK check for regular (in CovCoordAxis ?)
    CoverageCoordAxis1D runAxisSubset2 = new CoverageCoordAxis1D(runbuilder);

    CoverageCoordAxisBuilder timebuilder = new CoverageCoordAxisBuilder(timeOffset)
            .subset(runAxisSubset2.getName(), CoverageCoordAxis.Spacing.irregularPoint, ncoords, offsetValues); // aux coord (LOOK interval) ??
    CoverageCoordAxis1D timeOffsetSubset = new TimeOffsetAxis(timebuilder);

    CoverageCoordAxis scalarTimeCoord = makeScalarTimeCoord(wantOffset, runAxisSubset);

    // nothing needed for CF, the run coordinate acts as the CF time independent coord. timeOffset is aux, forecastTime is scalar
    return Optional.of(Lists.newArrayList(runAxisSubset2, timeOffsetSubset, scalarTimeCoord));
  }
public Optional<List<CoverageCoordAxis>> subset(SubsetParams params, AtomicBoolean isConstantForcast, boolean makeCFcompliant) {
    List<CoverageCoordAxis> result = new ArrayList<>();

    Optional<CoverageCoordAxis> axiso = runAxis.subset(params);
    if (!axiso.isPresent())
      return Optional.empty(axiso.getErrorMessage());
    CoverageCoordAxis1D runAxisSubset = (CoverageCoordAxis1D) axiso.get();
    result.add(runAxisSubset);

    // subset on timeOffset (1a, 1c, 2a)
    if (params.hasTimeOffsetParam() || !params.hasTimeParam()) {
      axiso = timeOffset.subset(params);
      if (!axiso.isPresent())
        return Optional.empty(axiso.getErrorMessage());
      CoverageCoordAxis timeOffsetSubset = axiso.get();
      result.add(timeOffsetSubset);

      if (makeCFcompliant) // add a time cordinate
        result.add(makeCFTimeCoord(runAxisSubset, (CoverageCoordAxis1D) timeOffsetSubset)); // possible the twoD time case, if nruns > 1
      return Optional.of(result);
    }

    // subset on time, # runtimes = 1 (1b)
    if (runAxisSubset.getNcoords() == 1) {
      double val = runAxisSubset.getCoordMidpoint(0);   // not sure runAxis is needed. maybe use runtimeSubset
      CalendarDate runDate = runAxisSubset.makeDate(val);
      Optional<TimeOffsetAxis> too = timeOffset.subsetFromTime(params, runDate);
      if (!too.isPresent())
        return Optional.empty(too.getErrorMessage());
      TimeOffsetAxis timeOffsetSubset =  too.get();
      result.add(timeOffsetSubset);

      if (makeCFcompliant)
        result.add(makeCFTimeCoord(runAxisSubset, timeOffsetSubset));
      return Optional.of(result);
    }

    // tricky case 2b time (point only not range) = constant forecast dataset
    // data reader has to skip around the 2D times
    // 1) the runtimes may be subset by whats available
    // 2) timeOffset could become an aux coordinate
    // 3) time coordinate becomes a scalar,
    isConstantForcast.set(true);

    CalendarDate dateWanted;
    if (params.isTrue(SubsetParams.timePresent))
      dateWanted = CalendarDate.present();
    else
      dateWanted = (CalendarDate) params.get(SubsetParams.time);
    if (dateWanted == null)
      throw new IllegalStateException("Must have time parameter");

    double wantOffset = runAxisSubset.convert(dateWanted); // forecastDate offset from refdate
    double start = timeOffset.getStartValue();
    double end = timeOffset.getEndValue();
    CoordAxisHelper helper = new CoordAxisHelper(timeOffset);

    // brute force search LOOK specialize for regular ?
    List<Integer> runtimeIdx = new ArrayList<>();  // list of runtime indexes that have this forecast
    // List<Integer> offsetIdx = new ArrayList<>();  // list of offset indexes that have this forecast
    List<Double> offset = new ArrayList<>();      // corresponding offset from start of run
    for (int i=0; i<runAxisSubset.getNcoords(); i++) {
      // public double getOffsetInTimeUnits(CalendarDate convertFrom, CalendarDate convertTo);
      double runOffset = runAxisSubset.getCoordMidpoint(i);
      if (end + runOffset < wantOffset) continue;
      if (wantOffset < start + runOffset) break;
      int idx = helper.search(wantOffset - runOffset);
      if (idx >= 0) {
        runtimeIdx.add(i);  // the ith runtime
        // offsetIdx.add(idx);   // the idx time offset
        offset.add(wantOffset - runOffset);   // the offset from the runtime
      }
    }

    // here are the runtimes
    int ncoords = runtimeIdx.size();
    double[] runValues = new double[ncoords];
    double[] offsetValues = new double[ncoords];
    int count = 0;
    for (int k=0; k<ncoords; k++) {
      offsetValues[count] = offset.get(k);
      runValues[count++] = runAxisSubset.getCoordMidpoint(runtimeIdx.get(k));
    }

    CoverageCoordAxisBuilder runbuilder = new CoverageCoordAxisBuilder(runAxisSubset)
            .subset(null, CoverageCoordAxis.Spacing.irregularPoint, ncoords, runValues); // LOOK check for regular (in CovCoordAxis ?)
    CoverageCoordAxis1D runAxisSubset2 = new CoverageCoordAxis1D(runbuilder);

    CoverageCoordAxisBuilder timebuilder = new CoverageCoordAxisBuilder(timeOffset)
            .subset(runAxisSubset2.getName(), CoverageCoordAxis.Spacing.irregularPoint, ncoords, offsetValues); // aux coord (LOOK interval) ??
    CoverageCoordAxis1D timeOffsetSubset = new TimeOffsetAxis(timebuilder);

    CoverageCoordAxis scalarTimeCoord = makeScalarTimeCoord(wantOffset, runAxisSubset);

    // nothing needed for CF, the run coordinate acts as the CF time independent coord. timeOffset is aux, forecastTime is scalar
    return Optional.of(Lists.newArrayList(runAxisSubset2, timeOffsetSubset, scalarTimeCoord));
  }
public Optional < List < CoverageCoordAxis > > subset ( SubsetParams params , AtomicBoolean isConstantForcast , boolean makeCFcompliant ) { List < CoverageCoordAxis > result = new ArrayList <> ( ) ; Optional < CoverageCoordAxis > axiso = runAxis . subset ( params ) ; if ( ! axiso . isPresent ( ) ) return Optional . empty ( axiso . getErrorMessage ( ) ) ; CoverageCoordAxis1D runAxisSubset = ( CoverageCoordAxis1D ) axiso . get ( ) ; result . add ( runAxisSubset ) ; // subset on timeOffset (1a, 1c, 2a) if ( params . hasTimeOffsetParam ( ) || ! params . hasTimeParam ( ) ) { axiso = timeOffset . subset ( params ) ; if ( ! axiso . isPresent ( ) ) return Optional . empty ( axiso . getErrorMessage ( ) ) ; CoverageCoordAxis timeOffsetSubset = axiso . get ( ) ; result . add ( timeOffsetSubset ) ; if ( makeCFcompliant ) // add a time cordinate result . add ( makeCFTimeCoord ( runAxisSubset , ( CoverageCoordAxis1D ) timeOffsetSubset ) ) ; // possible the twoD time case, if nruns > 1 return Optional . of ( result ) ; } // subset on time, # runtimes = 1 (1b) if ( runAxisSubset . getNcoords ( ) == 1 ) { double val = runAxisSubset . getCoordMidpoint ( 0 ) ; // not sure runAxis is needed. maybe use runtimeSubset CalendarDate runDate = runAxisSubset . makeDate ( val ) ; Optional < TimeOffsetAxis > too = timeOffset . subsetFromTime ( params , runDate ) ; if ( ! too . isPresent ( ) ) return Optional . empty ( too . getErrorMessage ( ) ) ; TimeOffsetAxis timeOffsetSubset = too . get ( ) ; result . add ( timeOffsetSubset ) ; if ( makeCFcompliant ) result . add ( makeCFTimeCoord ( runAxisSubset , timeOffsetSubset ) ) ; return Optional . of ( result ) ; } // tricky case 2b time (point only not range) = constant forecast dataset // data reader has to skip around the 2D times // 1) the runtimes may be subset by whats available // 2) timeOffset could become an aux coordinate // 3) time coordinate becomes a scalar, isConstantForcast . set ( true ) ; CalendarDate dateWanted ; if ( params . isTrue ( SubsetParams . timePresent ) ) dateWanted = CalendarDate . present ( ) ; else dateWanted = ( CalendarDate ) params . get ( SubsetParams . time ) ; if ( dateWanted == null ) throw new IllegalStateException ( "Must have time parameter" ) ; double wantOffset = runAxisSubset . convert ( dateWanted ) ; // forecastDate offset from refdate double start = timeOffset . getStartValue ( ) ; double end = timeOffset . getEndValue ( ) ; CoordAxisHelper helper = new CoordAxisHelper ( timeOffset ) ; // brute force search LOOK specialize for regular ? List < Integer > runtimeIdx = new ArrayList <> ( ) ; // list of runtime indexes that have this forecast // List<Integer> offsetIdx = new ArrayList<>();  // list of offset indexes that have this forecast List < Double > offset = new ArrayList <> ( ) ; // corresponding offset from start of run for ( int i = 0 ; i < runAxisSubset . getNcoords ( ) ; i ++ ) { // public double getOffsetInTimeUnits(CalendarDate convertFrom, CalendarDate convertTo); double runOffset = runAxisSubset . getCoordMidpoint ( i ) ; if ( end + runOffset < wantOffset ) continue ; if ( wantOffset < start + runOffset ) break ; int idx = helper . search ( wantOffset - runOffset ) ; if ( idx >= 0 ) { runtimeIdx . add ( i ) ; // the ith runtime // offsetIdx.add(idx);   // the idx time offset offset . add ( wantOffset - runOffset ) ; // the offset from the runtime } } // here are the runtimes int ncoords = runtimeIdx . size ( ) ; double [ ] runValues = new double [ ncoords ] ; double [ ] offsetValues = new double [ ncoords ] ; int count = 0 ; for ( int k = 0 ; k < ncoords ; k ++ ) { offsetValues [ count ] = offset . get ( k ) ; runValues [ count ++ ] = runAxisSubset . getCoordMidpoint ( runtimeIdx . get ( k ) ) ; } CoverageCoordAxisBuilder runbuilder = new CoverageCoordAxisBuilder ( runAxisSubset ) . subset ( null , CoverageCoordAxis . Spacing . irregularPoint , ncoords , runValues ) ; // LOOK check for regular (in CovCoordAxis ?) CoverageCoordAxis1D runAxisSubset2 = new CoverageCoordAxis1D ( runbuilder ) ; CoverageCoordAxisBuilder timebuilder = new CoverageCoordAxisBuilder ( timeOffset ) . subset ( runAxisSubset2 . getName ( ) , CoverageCoordAxis . Spacing . irregularPoint , ncoords , offsetValues ) ; // aux coord (LOOK interval) ?? CoverageCoordAxis1D timeOffsetSubset = new TimeOffsetAxis ( timebuilder ) ; CoverageCoordAxis scalarTimeCoord = makeScalarTimeCoord ( wantOffset , runAxisSubset ) ; // nothing needed for CF, the run coordinate acts as the CF time independent coord. timeOffset is aux, forecastTime is scalar return Optional . of ( Lists . newArrayList ( runAxisSubset2 , timeOffsetSubset , scalarTimeCoord ) ) ; }
/*
1) single runtime
1a timeOffset
1b time or timeRange
1c none = constant runtime dataset
2) multiple runtimes
2a timeOffset       = constant offset dataset
2b time (not range) = constant forecast dataset
/ * 1 ) single runtime 1a timeOffset 1b time or timeRange 1c none = constant runtime dataset 2 ) multiple runtimes 2a timeOffset = constant offset dataset 2b time ( not range ) = constant forecast dataset