Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,46 @@ public void testRegularIntervalSubsetTimeRange() throws IOException, InvalidRang
}
}

@Test
public void testDiscontiguousIntervalSubsetTimeRangeStartNoInInterval() throws IOException, InvalidRangeException {
String endpoint = TestDir.cdmUnitTestDir + "datasets/NDFD-CONUS-5km/NDFD-CONUS-5km.ncx4";
String covName = "Minimum_temperature_height_above_ground_12_Hour_Minimum";

logger.debug("testDiscontiguousIntervalTime Dataset {} coverage {}", endpoint, covName);

try (FeatureDatasetCoverage featureDatasetCoverage = CoverageDatasetFactory.open(endpoint)) {
assertThat(featureDatasetCoverage).isNotNull();
CoverageCollection coverageCollection = featureDatasetCoverage.findCoverageDataset(FeatureType.GRID);
assertThat(coverageCollection).isNotNull();
Coverage coverage = coverageCollection.findCoverage(covName);
assertThat(coverage).isNotNull();

SubsetParams params = new SubsetParams();

// key point of test - start time lands between two discontiguous time windows
CalendarDate subsetTimeStart = CalendarDate.parseISOformat(null, "2013-12-18T20:00:00Z");
CalendarDate subsetTimeEnd = CalendarDate.parseISOformat(null, "2013-12-19T13:00:00Z");
// expect: any interval containing or ending on the start or end times
// idx keep interval
// 0 N ( 84.000000, 96.000000) == (2013-12-15T12:00:00Z, 2013-12-16T00:00:00Z)
// 1 N (108.000000, 120.000000) == (2013-12-16T12:00:00Z, 2013-12-17T00:00:00Z)
// 2 N (132.000000, 144.000000) == (2013-12-17T12:00:00Z, 2013-12-18T00:00:00Z)
// 3 Y (156.000000, 168.000000) == (2013-12-18T12:00:00Z, 2013-12-19T00:00:00Z)
// 4 Y (180.000000, 192.000000) == (2013-12-19T12:00:00Z, 2013-12-20T00:00:00Z)
// 5 N (204.000000, 216.000000) == (2013-12-20T12:00:00Z, 2013-12-21T00:00:00Z)
int expectedStartIndex = 3;
int expectedEndIndex = 4;
params.setTimeRange(CalendarDateRange.of(subsetTimeStart, subsetTimeEnd));
logger.debug(" subset {}", params);

GeoReferencedArray geo = coverage.readData(params);
assertThat(geo).isNotNull();
CoverageCoordAxis timeAxis = geo.findCoordAxis("time1");
assertThat(timeAxis).isNotNull();
assertThat(timeAxis.getSpacing()).isEqualTo(Spacing.discontiguousInterval);
assertThat(timeAxis.getRange()).isEqualTo(new Range(expectedStartIndex, expectedEndIndex));
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// ENsemble

Expand Down
52 changes: 26 additions & 26 deletions cdm/core/src/main/java/ucar/nc2/ft2/coverage/CoordAxisHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,16 @@ else if (upperIndex > axis.getCoordEdgeLast())
// same contract as findCoordElement(); in addition, -1 is returned when the target is not contained in any interval
private int findCoordElementDiscontiguousInterval(double target, boolean bounded) {
int idx = findSingleHit(target);
// multiple hits = choose closest (definition of closest will be based on axis type)
if (idx == MULTIPLE_HITS) {
return findClosestDiscontiguousInterval(target);
}
if (bounded && (idx >= axis.getNcoords())) {
return -1;
}
// multiple hits = choose closest (definition of closest will be based on axis type)
// - OR -
// idx will be Ncoords if target not contained within an interval window - however, that
// does not mean target isn't between two discontiguous windows, so find the closest one
if (idx == MULTIPLE_HITS || idx == axis.getNcoords()) {
return findClosestDiscontiguousInterval(target);
}
return idx;
}

Expand Down Expand Up @@ -272,28 +275,25 @@ private int findClosestDiscontiguousTimeInterval(double target) {
int idxFound = -1;

for (int i = 0; i < axis.getNcoords(); i++) {
// only check if target is in discontiguous interval i
if (intervalContains(target, i)) {
// find the end of the time interval
double coord = axis.getCoordEdge2(i);
// We want to make sure the interval includes our target point, and that means the end of the interval
// must be greater than or equal to the target.
if (coord >= target) {
// compute the width (in time) of the interval
double width = coord - axis.getCoordEdge1(i);
// we want to identify the interval with the end point closest to our target
// why? Because a statistic computed over a time window will only have meaning at the end
// of that interval, so the closer we can get to that the better.
double diff = Math.abs(coord - target);
// Here we minimize the difference between the end of an interval and our target value. If multiple
// intervals result in the same difference value, we will pick the one with the smallest non-zero
// width interval.
boolean tiebreaker = (diff == minDiff) && (width != 0) && (width < useValue);
if (diff < minDiff || tiebreaker) {
minDiff = diff;
idxFound = i;
useValue = width;
}
// find the end of the time interval
double coord = axis.getCoordEdge2(i);
// We want to make sure the interval includes our target point, and that means the end of the interval
// must be greater than or equal to the target.
if (coord >= target) {
// compute the width (in time) of the interval
double width = coord - axis.getCoordEdge1(i);
// we want to identify the interval with the end point closest to our target
// why? Because a statistic computed over a time window will only have meaning at the end
// of that interval, so the closer we can get to that the better.
double diff = Math.abs(coord - target);
// Here we minimize the difference between the end of an interval and our target value. If multiple
// intervals result in the same difference value, we will pick the one with the smallest non-zero
// width interval.
boolean tiebreaker = (diff == minDiff) && (width != 0) && (width < useValue);
if (diff < minDiff || tiebreaker) {
minDiff = diff;
idxFound = i;
useValue = width;
}
}
}
Expand Down