Skip to content

Commit ae0209f

Browse files
committed
Discontiguous time axis subset bug
Fix case where start time of subset for discontiguous time axis is not contained within any one time interval window. Fixes #1430
1 parent 3c871ce commit ae0209f

File tree

2 files changed

+66
-26
lines changed

2 files changed

+66
-26
lines changed

cdm-test/src/test/java/ucar/nc2/ft/coverage/TestCoverageSubsetTime.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,46 @@ public void testRegularIntervalSubsetTimeRange() throws IOException, InvalidRang
792792
}
793793
}
794794

795+
@Test
796+
public void testDiscontiguousIntervalSubsetTimeRangeStartNoInInterval() throws IOException, InvalidRangeException {
797+
String endpoint = TestDir.cdmUnitTestDir + "datasets/NDFD-CONUS-5km/NDFD-CONUS-5km.ncx4";
798+
String covName = "Minimum_temperature_height_above_ground_12_Hour_Minimum";
799+
800+
logger.debug("testDiscontiguousIntervalTime Dataset {} coverage {}", endpoint, covName);
801+
802+
try (FeatureDatasetCoverage featureDatasetCoverage = CoverageDatasetFactory.open(endpoint)) {
803+
assertThat(featureDatasetCoverage).isNotNull();
804+
CoverageCollection coverageCollection = featureDatasetCoverage.findCoverageDataset(FeatureType.GRID);
805+
assertThat(coverageCollection).isNotNull();
806+
Coverage coverage = coverageCollection.findCoverage(covName);
807+
assertThat(coverage).isNotNull();
808+
809+
SubsetParams params = new SubsetParams();
810+
811+
// key point of test - start time lands between two discontiguous time windows
812+
CalendarDate subsetTimeStart = CalendarDate.parseISOformat(null, "2013-12-18T20:00:00Z");
813+
CalendarDate subsetTimeEnd = CalendarDate.parseISOformat(null, "2013-12-19T13:00:00Z");
814+
// expect: any interval containing or ending on the start or end times
815+
// idx keep interval
816+
// 0 N ( 84.000000, 96.000000) == (2013-12-15T12:00:00Z, 2013-12-16T00:00:00Z)
817+
// 1 N (108.000000, 120.000000) == (2013-12-16T12:00:00Z, 2013-12-17T00:00:00Z)
818+
// 2 N (132.000000, 144.000000) == (2013-12-17T12:00:00Z, 2013-12-18T00:00:00Z)
819+
// 3 Y (156.000000, 168.000000) == (2013-12-18T12:00:00Z, 2013-12-19T00:00:00Z)
820+
// 4 Y (180.000000, 192.000000) == (2013-12-19T12:00:00Z, 2013-12-20T00:00:00Z)
821+
// 5 N (204.000000, 216.000000) == (2013-12-20T12:00:00Z, 2013-12-21T00:00:00Z)
822+
int expectedStartIndex = 3;
823+
int expectedEndIndex = 4;
824+
params.setTimeRange(CalendarDateRange.of(subsetTimeStart, subsetTimeEnd));
825+
logger.debug(" subset {}", params);
826+
827+
GeoReferencedArray geo = coverage.readData(params);
828+
assertThat(geo).isNotNull();
829+
CoverageCoordAxis timeAxis = geo.findCoordAxis("time1");
830+
assertThat(timeAxis).isNotNull();
831+
assertThat(timeAxis.getSpacing()).isEqualTo(Spacing.discontiguousInterval);
832+
assertThat(timeAxis.getRange()).isEqualTo(new Range(expectedStartIndex, expectedEndIndex));
833+
}
834+
}
795835
///////////////////////////////////////////////////////////////////////////////////////////
796836
// ENsemble
797837

cdm/core/src/main/java/ucar/nc2/ft2/coverage/CoordAxisHelper.java

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -205,13 +205,16 @@ else if (upperIndex > axis.getCoordEdgeLast())
205205
// same contract as findCoordElement(); in addition, -1 is returned when the target is not contained in any interval
206206
private int findCoordElementDiscontiguousInterval(double target, boolean bounded) {
207207
int idx = findSingleHit(target);
208-
// multiple hits = choose closest (definition of closest will be based on axis type)
209-
if (idx == MULTIPLE_HITS) {
210-
return findClosestDiscontiguousInterval(target);
211-
}
212208
if (bounded && (idx >= axis.getNcoords())) {
213209
return -1;
214210
}
211+
// multiple hits = choose closest (definition of closest will be based on axis type)
212+
// - OR -
213+
// idx will be Ncoords if target not contained within an interval window - however, that
214+
// does not mean target isn't between two discontiguous windows, so find the closest one
215+
if (idx == MULTIPLE_HITS || idx == axis.getNcoords()) {
216+
return findClosestDiscontiguousInterval(target);
217+
}
215218
return idx;
216219
}
217220

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

274277
for (int i = 0; i < axis.getNcoords(); i++) {
275-
// only check if target is in discontiguous interval i
276-
if (intervalContains(target, i)) {
277-
// find the end of the time interval
278-
double coord = axis.getCoordEdge2(i);
279-
// We want to make sure the interval includes our target point, and that means the end of the interval
280-
// must be greater than or equal to the target.
281-
if (coord >= target) {
282-
// compute the width (in time) of the interval
283-
double width = coord - axis.getCoordEdge1(i);
284-
// we want to identify the interval with the end point closest to our target
285-
// why? Because a statistic computed over a time window will only have meaning at the end
286-
// of that interval, so the closer we can get to that the better.
287-
double diff = Math.abs(coord - target);
288-
// Here we minimize the difference between the end of an interval and our target value. If multiple
289-
// intervals result in the same difference value, we will pick the one with the smallest non-zero
290-
// width interval.
291-
boolean tiebreaker = (diff == minDiff) && (width != 0) && (width < useValue);
292-
if (diff < minDiff || tiebreaker) {
293-
minDiff = diff;
294-
idxFound = i;
295-
useValue = width;
296-
}
278+
// find the end of the time interval
279+
double coord = axis.getCoordEdge2(i);
280+
// We want to make sure the interval includes our target point, and that means the end of the interval
281+
// must be greater than or equal to the target.
282+
if (coord >= target) {
283+
// compute the width (in time) of the interval
284+
double width = coord - axis.getCoordEdge1(i);
285+
// we want to identify the interval with the end point closest to our target
286+
// why? Because a statistic computed over a time window will only have meaning at the end
287+
// of that interval, so the closer we can get to that the better.
288+
double diff = Math.abs(coord - target);
289+
// Here we minimize the difference between the end of an interval and our target value. If multiple
290+
// intervals result in the same difference value, we will pick the one with the smallest non-zero
291+
// width interval.
292+
boolean tiebreaker = (diff == minDiff) && (width != 0) && (width < useValue);
293+
if (diff < minDiff || tiebreaker) {
294+
minDiff = diff;
295+
idxFound = i;
296+
useValue = width;
297297
}
298298
}
299299
}

0 commit comments

Comments
 (0)