Skip to content

Commit 383b5a0

Browse files
committed
fix interpolation issue on anti-meridian
1 parent 28ee7f0 commit 383b5a0

File tree

9 files changed

+106
-40
lines changed

9 files changed

+106
-40
lines changed

core/src/main/java/com/bc/fiduceo/util/JDomUtils.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,15 @@ public class JDomUtils {
3434

3535
public static Attribute getMandatoryAttribute(final Element element, final String name) {
3636
final Attribute attribute = element.getAttribute(name);
37-
String elementName = element.getName();
3837
if (attribute == null) {
39-
throw new RuntimeException(ATTRIBUTE + " '" + name + "' expected at element '" + elementName + "'");
38+
throw new RuntimeException(ATTRIBUTE + " '" + name + "' expected at element '" + element.getName() + "'");
4039
}
4140
return attribute;
4241
}
4342

4443
public static String getMandatoryText(final Element element) {
4544
final String textTrim = element.getTextTrim();
46-
if (textTrim.length() == 0) {
45+
if (textTrim.isEmpty()) {
4746
throw new RuntimeException(VALUE + " of element '" + element.getName() + "' expected");
4847
}
4948
return textTrim;

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessing.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
class Era5PostProcessing extends PostProcessing {
1515

1616
private static final double EPS = 0.00000001;
17+
public static final int DATA_ARRAY_WIDTH = 1440;
1718

1819
private final Configuration configuration;
1920

@@ -59,7 +60,11 @@ private static InterpolationContext createInterpolationContext_2D(Array lonArray
5960
final Index latIdx = latArray.getIndex();
6061
for (int y = 0; y < shape[0]; y++) {
6162
for (int x = 0; x < shape[1]; x++) {
62-
lonIdx.set(y, x);
63+
if (x >= DATA_ARRAY_WIDTH) {
64+
lonIdx.set(y, x - DATA_ARRAY_WIDTH);
65+
} else {
66+
lonIdx.set(y, x);
67+
}
6368
latIdx.set(y, x);
6469

6570
final float lon = lonArray.getFloat(lonIdx);
@@ -82,7 +87,6 @@ private static InterpolationContext createInterpolationContext_2D(Array lonArray
8287
}
8388
}
8489

85-
8690
return context;
8791
}
8892

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingPlugin.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ public class Era5PostProcessingPlugin implements PostProcessingPlugin {
2828
private static final String ATT_NAME_LONG_NAME = "long_name";
2929
private static final String ATT_NAME_STANDARD_NAME = "standard_name";
3030
private static final String ATT_NAME_FILL_VALUE = "_FillValue";
31+
private static final String ATT_NAME_LENGTH = "length";
3132

3233
private static final Set<String> KNOWN_ATTRIB_NAMES = new TreeSet<>(
3334
Arrays.asList(ATT_NAME_UNITS, ATT_NAME_LONG_NAME, ATT_NAME_STANDARD_NAME, ATT_NAME_FILL_VALUE));
3435

36+
3537
static Configuration createConfiguration(Element rootElement, PostProcessingContext context) {
3638
final Configuration configuration = new Configuration();
3739

@@ -75,23 +77,23 @@ static void parseSatelliteFields(Element rootElement, Configuration configuratio
7577
if (xDimElement != null) {
7678
final Attribute nameElement = JDomUtils.getMandatoryAttribute(xDimElement, "name");
7779
satelliteFieldsConfiguration.set_x_dim_name(nameElement.getValue());
78-
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(xDimElement, "length");
80+
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(xDimElement, ATT_NAME_LENGTH);
7981
satelliteFieldsConfiguration.set_x_dim(Integer.parseInt(lengthElement.getValue()));
8082
}
8183

8284
final Element yDimElement = satelliteFieldsElement.getChild("y_dim");
8385
if (yDimElement != null) {
8486
final Attribute nameElement = JDomUtils.getMandatoryAttribute(yDimElement, "name");
8587
satelliteFieldsConfiguration.set_y_dim_name(nameElement.getValue());
86-
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(yDimElement, "length");
88+
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(yDimElement, ATT_NAME_LENGTH);
8789
satelliteFieldsConfiguration.set_y_dim(Integer.parseInt(lengthElement.getValue()));
8890
}
8991

9092
final Element zDimElement = satelliteFieldsElement.getChild("z_dim");
9193
if (zDimElement != null) {
9294
final Attribute nameElement = JDomUtils.getMandatoryAttribute(zDimElement, "name");
9395
satelliteFieldsConfiguration.set_z_dim_name(nameElement.getValue());
94-
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(zDimElement, "length");
96+
final Attribute lengthElement = JDomUtils.getMandatoryAttribute(zDimElement, ATT_NAME_LENGTH);
9597
satelliteFieldsConfiguration.set_z_dim(Integer.parseInt(lengthElement.getValue()));
9698
}
9799

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/FieldsProcessor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@
88
import java.awt.*;
99
import java.io.IOException;
1010

11-
class FieldsProcessor {
11+
import static com.bc.fiduceo.post.plugin.era5.Era5PostProcessing.DATA_ARRAY_WIDTH;
1212

13-
private static final int RASTER_WIDTH = 1440;
13+
class FieldsProcessor {
1414

1515
TemplateVariable createTemplate(String name, String units, String longName, String standardName, boolean is3d) {
1616
return new TemplateVariable(name, units, longName, standardName, is3d);
@@ -25,7 +25,7 @@ static Array mergeData(Array leftSubset, Array rightSubset, int numLayers, Recta
2525
final int xMax = era5RasterPosition.width + era5RasterPosition.x;
2626
mergeArrays_3D(leftSubset, rightSubset, era5RasterPosition, mergedArray, xMax);
2727
} else {
28-
final int xMax = RASTER_WIDTH - era5RasterPosition.x;
28+
final int xMax = DATA_ARRAY_WIDTH - era5RasterPosition.x;
2929
mergeArrays_3D(rightSubset, leftSubset, era5RasterPosition, mergedArray, xMax);
3030
}
3131
} else {
@@ -34,7 +34,7 @@ static Array mergeData(Array leftSubset, Array rightSubset, int numLayers, Recta
3434
final int xMax = era5RasterPosition.width + era5RasterPosition.x;
3535
mergeArrays(leftSubset, rightSubset, era5RasterPosition, mergedArray, xMax);
3636
} else {
37-
final int xMax = RASTER_WIDTH - era5RasterPosition.x;
37+
final int xMax = DATA_ARRAY_WIDTH - era5RasterPosition.x;
3838
mergeArrays(rightSubset, leftSubset, era5RasterPosition, mergedArray, xMax);
3939
}
4040
}

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/InterpolationContext.java

Lines changed: 42 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import java.util.ArrayList;
66
import java.util.List;
77

8+
import static com.bc.fiduceo.post.plugin.era5.Era5PostProcessing.DATA_ARRAY_WIDTH;
9+
810
class InterpolationContext {
911

1012
private final BilinearInterpolator[][] interpolators;
@@ -45,32 +47,23 @@ public IntRange[] getXRanges() {
4547
return xRanges;
4648
}
4749

50+
void setXRanges(IntRange[] xRanges) {
51+
this.xRanges = xRanges;
52+
initialize();
53+
}
54+
4855
private void initialize() {
49-
final List<IntRange> ranges = new ArrayList<>();
56+
final List<IntRange> tempXRanges = new ArrayList<>();
5057
IntRange current = new IntRange();
5158
for (int y = 0; y < height; y++) {
5259
for (int x = 0; x < width; x++) {
53-
if (interpolators[y][x] != null) {
54-
int min = interpolators[y][x].getXMin();
55-
int max = (min + 1) % 1440;
56-
57-
if (max == 0 && min > 0) {
58-
// we wrap around antimeridian
59-
// finish current range with min = 1439
60-
current.setMax(min);
61-
ranges.add(current);
62-
// start new range with xmin = xmax = 0
63-
current = new IntRange(0, 0);
64-
}
65-
66-
if (current.getMin() > min) {
67-
current.setMin(min);
68-
}
69-
if (current.getMax() < max) {
70-
current.setMax(max);
71-
}
60+
final BilinearInterpolator interpolator = interpolators[y][x];
61+
if (interpolator != null) {
62+
current = getXRange(interpolator, current, tempXRanges);
63+
int max;
64+
int min;
7265

73-
min = interpolators[y][x].getYMin();
66+
min = interpolator.getYMin();
7467
max = min + 1;
7568
if (yRange.getMin() > min) {
7669
yRange.setMin(min);
@@ -81,18 +74,43 @@ private void initialize() {
8174
}
8275
}
8376
}
84-
ranges.add(current);
85-
xRanges = ranges.toArray(new IntRange[0]);
77+
78+
tempXRanges.add(current);
79+
xRanges = tempXRanges.toArray(new IntRange[0]);
8680

8781
setRelativeInterpolatorOffsets();
8882

8983
mustInitialise = false;
9084
}
9185

86+
// package access for testing only tb 2025-10-08
87+
static IntRange getXRange(BilinearInterpolator interpolator, IntRange current, List<IntRange> xRanges) {
88+
int min = interpolator.getXMin();
89+
int max = (min + 1) % DATA_ARRAY_WIDTH;
90+
91+
if (max == 0 && min > 0) {
92+
// we wrap around antimeridian
93+
// finish current range with min = 1439
94+
current.setMin(1438);
95+
current.setMax(1439);
96+
xRanges.add(current);
97+
// start new range with xmin = 0 xmax = 1 - should always be the case
98+
current = new IntRange(0, 1);
99+
return current;
100+
}
101+
102+
if (current.getMin() > min) {
103+
current.setMin(min);
104+
}
105+
if (current.getMax() < max) {
106+
current.setMax(max);
107+
}
108+
return current;
109+
}
110+
92111
private void setRelativeInterpolatorOffsets() {
93112
final int yMinValue = yRange.getMin();
94113

95-
96114
for (int y = 0; y < height; y++) {
97115
for (int x = 0; x < width; x++) {
98116
final BilinearInterpolator interpolator = interpolators[y][x];

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/MatchupFields.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.List;
1414
import java.util.*;
1515

16+
import static com.bc.fiduceo.post.plugin.era5.Era5PostProcessing.DATA_ARRAY_WIDTH;
1617
import static com.bc.fiduceo.post.plugin.era5.VariableUtils.*;
1718

1819
class MatchupFields extends FieldsProcessor {
@@ -138,7 +139,7 @@ void compute(Configuration config, NetcdfFile reader, NetcdfFileWriter writer) t
138139
}
139140

140141
final int offsetX = bilinearInterpolator.getXMin();
141-
final int offsetX1 = (offsetX + 1) % 1440;
142+
final int offsetX1 = (offsetX + 1) % DATA_ARRAY_WIDTH;
142143
final int offsetY = bilinearInterpolator.getYMin();
143144

144145
Array leftSide = variable.read(new int[]{0, offsetY, offsetX}, new int[]{1, 2, 1}).reduce();

post-processing-tool/src/main/java/com/bc/fiduceo/post/plugin/era5/SatelliteFields.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313

1414
import java.io.IOException;
1515
import java.util.*;
16+
import java.util.logging.Logger;
1617

18+
import static com.bc.fiduceo.post.plugin.era5.Era5PostProcessing.DATA_ARRAY_WIDTH;
1719
import static com.bc.fiduceo.post.plugin.era5.VariableUtils.*;
1820
import static com.bc.fiduceo.post.util.PPUtils.convertToFitTheRangeMinus180to180;
1921

@@ -227,8 +229,8 @@ private static Array readFullEra5Array(Variable variable, int numLayers, IntRang
227229

228230
final int yMin = yRange.getMin();
229231
final int xMin = xRange.getMin();
230-
int yLength = yRange.getLength() + 1;
231-
int xlength = xRange.getLength() + 1;
232+
int yLength = yRange.getLength();
233+
int xlength = xRange.getLength();
232234

233235
if (rank == 3) {
234236
offsets = new int[]{0, yMin, xMin};
@@ -247,7 +249,10 @@ private static Array readFullEra5Array(Variable variable, int numLayers, IntRang
247249
era5Data = NetCDFUtils.scale(era5Data, scaleFactor, offset);
248250
}
249251
} catch (Exception e) {
250-
FiduceoLogger.getLogger().severe("Unable to read: " + variable.getFullName());
252+
final Logger logger = FiduceoLogger.getLogger();
253+
logger.severe("Unable to read: " + variable.getFullName());
254+
logger.severe("offsets: " + yMin + ", " + xMin);
255+
logger.severe("shape: " + yLength + ", " + xlength);
251256
throw e;
252257
}
253258

post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/Era5PostProcessingTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ public void testGetLonMin() {
108108
assertEquals(1344, Era5PostProcessing.getEra5LonMin(-23.8f));
109109
assertEquals(1438, Era5PostProcessing.getEra5LonMin(-0.26f));
110110
assertEquals(1439, Era5PostProcessing.getEra5LonMin(-0.18f));
111+
assertEquals(1439, Era5PostProcessing.getEra5LonMin(-0.01236f));
111112
assertEquals(0, Era5PostProcessing.getEra5LonMin(0.f));
112113
assertEquals(173, Era5PostProcessing.getEra5LonMin(43.32f));
113114
assertEquals(718, Era5PostProcessing.getEra5LonMin(179.58f));
@@ -159,6 +160,10 @@ public void testCreateInterpolator() {
159160
assertEquals(0.2799999713897705, interpolator.getA(), 1e-8);
160161
assertEquals(1.0, interpolator.getB(), 1e-8);
161162

163+
interpolator = Era5PostProcessing.createInterpolator(-0.01236f, 0.f, 1439, 359);
164+
assertEquals(1439, interpolator.getXMin(), 1e-8);
165+
assertEquals(359, interpolator.getYMin(), 1e-8);
166+
162167
interpolator = Era5PostProcessing.createInterpolator(43.32f, -22.19f, 173, 448);
163168
assertEquals(0.279998779296875, interpolator.getA(), 1e-8);
164169
assertEquals(0.7600021362304688, interpolator.getB(), 1e-8);

post-processing-tool/src/test/java/com/bc/fiduceo/post/plugin/era5/InterpolationContextTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import com.bc.fiduceo.core.IntRange;
44
import org.junit.Test;
55

6+
import java.util.ArrayList;
7+
68
import static org.junit.Assert.*;
79

810
public class InterpolationContextTest {
@@ -188,4 +190,34 @@ public void testGetYRange_threeInterpolators() {
188190
assertEquals(7, yRange.getMin());
189191
assertEquals(9, yRange.getMax());
190192
}
193+
194+
@Test
195+
public void testGetXRange() {
196+
final BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 100, 7);
197+
198+
final IntRange currentRange = new IntRange();
199+
final ArrayList<IntRange> xRanges = new ArrayList<>();
200+
IntRange xRange = InterpolationContext.getXRange(interpolator, currentRange, xRanges);
201+
assertEquals(100, xRange.getMin());
202+
assertEquals(101, xRange.getMax());
203+
204+
// nothing added, we're not splitting at anti-meridian tb 2025-10-08
205+
assertEquals(0, xRanges.size());
206+
}
207+
208+
@Test
209+
public void testGetXRange_antiMeridianCase() {
210+
final BilinearInterpolator interpolator = new BilinearInterpolator(0.3, 0.5, 1439, 7);
211+
212+
final IntRange currentRange = new IntRange();
213+
final ArrayList<IntRange> xRanges = new ArrayList<>();
214+
IntRange xRange = InterpolationContext.getXRange(interpolator, currentRange, xRanges);
215+
assertEquals(0, xRange.getMin());
216+
assertEquals(1, xRange.getMax());
217+
218+
assertEquals(1, xRanges.size());
219+
xRange = xRanges.get(0);
220+
assertEquals(1438, xRange.getMin());
221+
assertEquals(1439, xRange.getMax());
222+
}
191223
}

0 commit comments

Comments
 (0)