diff --git a/rugged-core/src/main/java/org/orekit/rugged/core/duvenhage/MinMaxTreeTile.java b/rugged-core/src/main/java/org/orekit/rugged/core/duvenhage/MinMaxTreeTile.java index 3dcf0b26da4c44fd830602897f1509e9bbef7181..006e5935fb20b3803a3c9d1c907dc5f42e84346d 100644 --- a/rugged-core/src/main/java/org/orekit/rugged/core/duvenhage/MinMaxTreeTile.java +++ b/rugged-core/src/main/java/org/orekit/rugged/core/duvenhage/MinMaxTreeTile.java @@ -37,12 +37,6 @@ public class MinMaxTreeTile extends SimpleTile { /** Start indices of tree levels. */ private int[] start; - /** Number of rows of tree levels. */ - private int[] rows; - - /** Number of columns of tree levels. */ - private int[] columns; - /** Simple constructor. * <p> * Creates an empty tile. @@ -55,16 +49,19 @@ public class MinMaxTreeTile extends SimpleTile { @Override protected void processUpdatedElevation(final double[] elevations) { + final int nbRows = getLatitudeRows(); + final int nbCols = getLongitudeColumns(); + // set up the levels - final int size = setLevels(0, getLatitudeRows(), getLongitudeColumns()); + final int size = setLevels(0, nbRows, nbCols); minTree = new double[size]; maxTree = new double[size]; // compute min/max trees - applyRecursively(minTree, 0, getLatitudeRows(), getLongitudeColumns(), - new Min(), elevations, 0); - applyRecursively(maxTree, 0, getLatitudeRows(), getLongitudeColumns(), - new Max(), elevations, 0); + if (start.length > 0) { + applyRecursively(minTree, start.length - 1, nbRows, nbCols, new Min(), elevations, 0); + applyRecursively(maxTree, start.length - 1, nbRows, nbCols, new Max(), elevations, 0); + } } @@ -87,11 +84,15 @@ public class MinMaxTreeTile extends SimpleTile { */ public double getMinElevation(final int i, final int j, final int level) { - // compute row index in level merged array - final int levelI = i >> ((level + 1) / 2); - final int levelJ = j >> ((level + 2) / 2); + // compute indices in level merged array + final int k = start.length - level; + final int rowShift = k / 2; + final int colShift = (k + 1) / 2; + final int levelI = i >> rowShift; + final int levelJ = j >> colShift; + final int levelC = 1 + ((getLongitudeColumns() - 1) >> colShift); - return minTree[start[level] + levelI * columns[level] + levelJ]; + return minTree[start[level] + levelI * levelC + levelJ]; } @@ -105,11 +106,15 @@ public class MinMaxTreeTile extends SimpleTile { */ public double getMaxElevation(final int i, final int j, final int level) { - // compute row index in level merged array - final int levelI = i >> ((level + 1) / 2); - final int levelJ = j >> (level / 2); + // compute indices in level merged array + final int k = start.length - level; + final int rowShift = k / 2; + final int colShift = (k + 1) / 2; + final int levelI = i >> rowShift; + final int levelJ = j >> colShift; + final int levelC = 1 + ((getLongitudeColumns() - 1) >> colShift); - return maxTree[start[level] + levelI * columns[level] + levelJ]; + return maxTree[start[level] + levelI * levelC + levelJ]; } @@ -125,53 +130,49 @@ public class MinMaxTreeTile extends SimpleTile { * <table border="0"> * <tr BGCOLOR="#EEEEFF"><font size="+1"> * <td>Level</td> <td>Dimension</td> <td>Start index</td> <td>End index</td></font></tr> - * <tr> <td>8</td> <td> 7 ⨉ 1</td> <td> 0</td> <td> 6</td> </tr> - * <tr> <td>7</td> <td> 7 ⨉ 2</td> <td> 7</td> <td> 20</td> </tr> - * <tr> <td>6</td> <td> 14 ⨉ 2</td> <td> 21</td> <td> 48</td> </tr> - * <tr> <td>5</td> <td> 14 ⨉ 3</td> <td> 49</td> <td> 90</td> </tr> + * <tr> <td>0</td> <td> 7 ⨉ 1</td> <td> 0</td> <td> 6</td> </tr> + * <tr> <td>1</td> <td> 7 ⨉ 2</td> <td> 7</td> <td> 20</td> </tr> + * <tr> <td>2</td> <td> 14 ⨉ 2</td> <td> 21</td> <td> 48</td> </tr> + * <tr> <td>3</td> <td> 14 ⨉ 3</td> <td> 49</td> <td> 90</td> </tr> * <tr> <td>4</td> <td> 27 ⨉ 3</td> <td> 91</td> <td>171</td> </tr> - * <tr> <td>3</td> <td> 27 ⨉ 5</td> <td> 172</td> <td>306</td> </tr> - * <tr> <td>2</td> <td> 54 ⨉ 5</td> <td> 307</td> <td>576</td> </tr> - * <tr> <td>1</td> <td> 54 ⨉ 10</td> <td> 577</td> <td>1116</td> </tr> - * <tr> <td>0</td> <td>107 ⨉ 10</td> <td>1117</td> <td>2186</td> </tr> + * <tr> <td>5</td> <td> 27 ⨉ 5</td> <td> 172</td> <td>306</td> </tr> + * <tr> <td>6</td> <td> 54 ⨉ 5</td> <td> 307</td> <td>576</td> </tr> + * <tr> <td>7</td> <td> 54 ⨉ 10</td> <td> 577</td> <td>1116</td> </tr> + * <tr> <td>8</td> <td>107 ⨉ 10</td> <td>1117</td> <td>2186</td> </tr> * </table> * </p> - * @param level current level (counting from leafs to root) - * @param levelRows number of rows at current level - * @param levelColumns number of columns at current level - * @return size cumulative size from current level to root + * @param stage number of merging stages + * @param stageRows number of rows at current stage + * @param stageColumns number of columns at current stage + * @return size cumulative size from root to current level */ - private int setLevels(final int level, final int levelRows, final int levelColumns) { + private int setLevels(final int stage, final int stageRows, final int stageColumns) { - if (levelRows == 1 || levelColumns == 1) { + if (stageRows == 1 || stageColumns == 1) { // we have found root, stop recursion - start = new int[level]; - rows = new int[level]; - columns = new int[level]; - start[level - 1] = 0; - rows[level - 1] = levelRows; - columns[level - 1] = levelColumns; - return levelRows * levelColumns; + start = new int[stage]; + if (stage > 0) { + start[0] = 0; + } + return stageRows * stageColumns; } final int size; - if ((level & 0x1) == 0) { + if ((stage & 0x1) == 0) { // columns merging - size = setLevels(level + 1, levelRows, (levelColumns + 1) / 2); + size = setLevels(stage + 1, stageRows, (stageColumns + 1) / 2); } else { // rows merging - size = setLevels(level + 1, (levelRows + 1) / 2, levelColumns); + size = setLevels(stage + 1, (stageRows + 1) / 2, stageColumns); } - if (level > 0) { + if (stage > 0) { // store current level characteristics - start[level - 1] = size; - rows[level - 1] = levelRows; - columns[level - 1] = levelColumns; - return size + levelRows * levelColumns; + start[start.length - stage] = size; + return size + stageRows * stageColumns; } else { - // we don't count the elements at leaf as they are not stored - // in the min/max trees + // we don't count the elements at stage 0 as they are not stored in the + // min/max trees (they correspond to the raw elevation, without merging) return size; } @@ -197,10 +198,10 @@ public class MinMaxTreeTile extends SimpleTile { int iBase = first; final int nextColumns = (levelColumns + 1) / 2; final boolean odd = (levelColumns & 0x1) != 0; + int jEnd = odd ? nextColumns - 1 : nextColumns; for (int i = 0; i < levelRows; ++i) { // regular pairs - int jEnd = odd ? nextColumns - 1 : nextColumns; for (int j = 0; j < jEnd; ++j) { tree[iTree++] = f.value(base[iBase], base[iBase + 1]); iBase += 2; @@ -214,8 +215,8 @@ public class MinMaxTreeTile extends SimpleTile { } - if (level < start.length - 1) { - applyRecursively(tree, level + 1, levelRows, nextColumns, f, tree, start[level]); + if (level > 0) { + applyRecursively(tree, level - 1, levelRows, nextColumns, f, tree, start[level]); } } else { @@ -225,9 +226,9 @@ public class MinMaxTreeTile extends SimpleTile { int iBase = first; final int nextRows = (levelRows + 1) / 2; final boolean odd = (levelRows & 0x1) != 0; + int iEnd = odd ? nextRows - 1 : nextRows; // regular pairs - int iEnd = odd ? nextRows - 1 : nextRows; for (int i = 0; i < iEnd; ++i) { for (int j = 0; j < levelColumns; ++j) { @@ -243,8 +244,8 @@ public class MinMaxTreeTile extends SimpleTile { System.arraycopy(base, iBase, tree, iTree, levelColumns); } - if (level < start.length - 1) { - applyRecursively(tree, level + 1, nextRows, levelColumns, f, tree, start[level]); + if (level > 0) { + applyRecursively(tree, level - 1, nextRows, levelColumns, f, tree, start[level]); } } diff --git a/rugged-core/src/test/java/orekit/rugged/core/duvenhage/MinMaxTreeTileTest.java b/rugged-core/src/test/java/orekit/rugged/core/duvenhage/MinMaxTreeTileTest.java index 68295ff633cd16a6f3e0c0aba4ea892f372fb9bf..7b69d6f6c74940f4ab789b84af705589963de60e 100644 --- a/rugged-core/src/test/java/orekit/rugged/core/duvenhage/MinMaxTreeTileTest.java +++ b/rugged-core/src/test/java/orekit/rugged/core/duvenhage/MinMaxTreeTileTest.java @@ -18,8 +18,6 @@ package orekit.rugged.core.duvenhage; import java.lang.reflect.Field; -import org.apache.commons.math3.random.RandomGenerator; -import org.apache.commons.math3.random.Well19937a; import org.apache.commons.math3.util.FastMath; import org.junit.Assert; import org.junit.Test; @@ -41,47 +39,23 @@ public class MinMaxTreeTileTest { Field startField = MinMaxTreeTile.class.getDeclaredField("start"); startField.setAccessible(true); int[] start = (int[]) startField.get(tile); - Assert.assertEquals( 0, start[ 8]); - Assert.assertEquals( 7, start[ 7]); - Assert.assertEquals( 21, start[ 6]); - Assert.assertEquals( 49, start[ 5]); + Assert.assertEquals( 0, start[ 0]); + Assert.assertEquals( 7, start[ 1]); + Assert.assertEquals( 21, start[ 2]); + Assert.assertEquals( 49, start[ 3]); Assert.assertEquals( 91, start[ 4]); - Assert.assertEquals( 172, start[ 3]); - Assert.assertEquals( 307, start[ 2]); - Assert.assertEquals( 577, start[ 1]); - Assert.assertEquals(1117, start[ 0]); - - Field rowsField = MinMaxTreeTile.class.getDeclaredField("rows"); - rowsField.setAccessible(true); - int[] rows = (int[]) rowsField.get(tile); - Assert.assertEquals( 7, rows[ 8]); - Assert.assertEquals( 7, rows[ 7]); - Assert.assertEquals( 14, rows[ 6]); - Assert.assertEquals( 14, rows[ 5]); - Assert.assertEquals( 27, rows[ 4]); - Assert.assertEquals( 27, rows[ 3]); - Assert.assertEquals( 54, rows[ 2]); - Assert.assertEquals( 54, rows[ 1]); - Assert.assertEquals( 107, rows[ 0]); - - Field columnsField = MinMaxTreeTile.class.getDeclaredField("columns"); - columnsField.setAccessible(true); - int[] columns = (int[]) columnsField.get(tile); - Assert.assertEquals( 1, columns[ 8]); - Assert.assertEquals( 2, columns[ 7]); - Assert.assertEquals( 2, columns[ 6]); - Assert.assertEquals( 3, columns[ 5]); - Assert.assertEquals( 3, columns[ 4]); - Assert.assertEquals( 5, columns[ 3]); - Assert.assertEquals( 5, columns[ 2]); - Assert.assertEquals( 10, columns[ 1]); - Assert.assertEquals( 10, columns[ 0]); + Assert.assertEquals( 172, start[ 5]); + Assert.assertEquals( 307, start[ 6]); + Assert.assertEquals( 577, start[ 7]); + Assert.assertEquals(1117, start[ 8]); + Field minTreeField = MinMaxTreeTile.class.getDeclaredField("minTree"); minTreeField.setAccessible(true); Assert.assertEquals(2187, ((double[]) minTreeField.get(tile)).length); Field maxTreeField = MinMaxTreeTile.class.getDeclaredField("maxTree"); maxTreeField.setAccessible(true); Assert.assertEquals(2187, ((double[]) maxTreeField.get(tile)).length); + } @Test @@ -96,26 +70,10 @@ public class MinMaxTreeTileTest { Field startField = MinMaxTreeTile.class.getDeclaredField("start"); startField.setAccessible(true); int[] start = (int[]) startField.get(tile); - Assert.assertEquals( 0, start[ 3]); - Assert.assertEquals( 2, start[ 2]); - Assert.assertEquals( 6, start[ 1]); - Assert.assertEquals(14, start[ 0]); - - Field rowsField = MinMaxTreeTile.class.getDeclaredField("rows"); - rowsField.setAccessible(true); - int[] rows = (int[]) rowsField.get(tile); - Assert.assertEquals( 1, rows[ 3]); - Assert.assertEquals( 2, rows[ 2]); - Assert.assertEquals( 2, rows[ 1]); - Assert.assertEquals( 4, rows[ 0]); - - Field columnsField = MinMaxTreeTile.class.getDeclaredField("columns"); - columnsField.setAccessible(true); - int[] columns = (int[]) columnsField.get(tile); - Assert.assertEquals( 2, columns[ 3]); - Assert.assertEquals( 2, columns[ 2]); - Assert.assertEquals( 4, columns[ 1]); - Assert.assertEquals( 4, columns[ 0]); + Assert.assertEquals( 0, start[ 0]); + Assert.assertEquals( 2, start[ 1]); + Assert.assertEquals( 6, start[ 2]); + Assert.assertEquals(14, start[ 3]); Field minTreeField = MinMaxTreeTile.class.getDeclaredField("minTree"); minTreeField.setAccessible(true); @@ -123,84 +81,91 @@ public class MinMaxTreeTileTest { Field maxTreeField = MinMaxTreeTile.class.getDeclaredField("maxTree"); maxTreeField.setAccessible(true); Assert.assertEquals(30, ((double[]) maxTreeField.get(tile)).length); + + } + + @Test + public void testSinglePixel() throws RuggedException { + MinMaxTreeTile tile = new MinMaxTreeTileFactory().createTile(); + tile.setGeometry(1.0, 2.0, 0.1, 0.2, 1, 1); + tile.setElevation(0, 0, 2.5); + tile.tileUpdateCompleted(); + Assert.assertEquals(0, tile.getLevels()); } @Test public void testMinMax() throws RuggedException { - RandomGenerator random = new Well19937a(0xfbbc1d1739b23555l); - checkMinMax(4, 7, 100, random); + for (int nbRows = 1; nbRows < 25; nbRows++) { + for (int nbColumns = 1; nbColumns < 25; nbColumns++) { + checkMinMax(nbRows, nbColumns); + } + } } - private void checkMinMax(int nbRows, int nbColumns, int nbChecks, RandomGenerator random) + private void checkMinMax(int nbRows, int nbColumns) throws RuggedException { MinMaxTreeTile tile = new MinMaxTreeTileFactory().createTile(); tile.setGeometry(1.0, 2.0, 0.1, 0.2, nbRows, nbColumns); for (int i = 0; i < nbRows; ++i) { for (int j = 0; j < nbColumns; ++j) { - tile.setElevation(i, j, 1000 * random.nextDouble()); + tile.setElevation(i, j, i + 0.01 * j); } } tile.tileUpdateCompleted(); - for (int k = 0; k < nbChecks; ++k) { - int row = random.nextInt(nbRows); - int column = random.nextInt(nbColumns); - int level = random.nextInt(tile.getLevels()); - - // reference min and max - int[] neighbors = neighbors(row, column, nbRows, nbColumns, level); - System.out.println(row + " " + column + " (" + level + "): [" + - neighbors[0] + ", " + neighbors[1] + "] [" + - neighbors[2] + ", " + neighbors[3] + "]"); - double min = Double.POSITIVE_INFINITY; - double max = Double.POSITIVE_INFINITY; - for (int i = neighbors[0]; i < neighbors[1]; ++i) { - for (int j = neighbors[2]; j < neighbors[3]; ++j) { - double pixelValue = tile.getElevationAtIndices(i, j); - min = FastMath.min(min, pixelValue); - max = FastMath.max(max, pixelValue); + for (int level = 0; level < tile.getLevels(); level++) { + for (int row = 0; row < nbRows; row++) { + for (int column = 0; column < nbColumns; column++) { + + // reference min and max + int[] neighbors = neighbors(row, column, nbRows, nbColumns, tile.getLevels() - level); + double min = Double.POSITIVE_INFINITY; + double max = Double.NEGATIVE_INFINITY; + for (int i = neighbors[0]; i < neighbors[1]; ++i) { + for (int j = neighbors[2]; j < neighbors[3]; ++j) { + double pixelValue = tile.getElevationAtIndices(i, j); + min = FastMath.min(min, pixelValue); + max = FastMath.max(max, pixelValue); + } + } + + Assert.assertEquals(min, tile.getMinElevation(row, column, level), 1.0e-10 * min); + Assert.assertEquals(max, tile.getMaxElevation(row, column, level), 1.0e-10 * max); } } - - Assert.assertEquals(min, tile.getMinElevation(row, column, level), 1.0e-10 * min); - Assert.assertEquals(max, tile.getMaxElevation(row, column, level), 1.0e-10 * max); - } } - private int[] neighbors(int row, int column, int nbRows, int nbColumns, int level) { + private int[] neighbors(int row, int column, int nbRows, int nbColumns, int stages) { // poor man identification of neighbors cells merged together with specified cell - int rMin = 0; - int rMax = row; - int cMin = 0; - int cMax = column; + int rMin = row; + int rN = 1; + int rMask = -1; + int cMin = column; + int cMask = -1; + int cN = 1; boolean mergeColumns = true; - for (int i = 0; i <= level; ++i) { + for (int i = 0; i < stages; ++i) { if (mergeColumns) { - int split = (nbColumns + 1) / 2; - if (column < split) { - cMax = split; - } else { - cMin = split; - } - nbColumns = cMax - cMin; + cMask = cMask << 1; + cMin = cMin & cMask; + cN = cN * 2; } else { - int split = (nbRows + 1) / 2; - if (row < split) { - rMax = split; - } else { - rMin = split; - } - nbRows = rMax - rMin; + rMask = rMask << 1; + rMin = rMin & rMask; + rN = rN * 2; } mergeColumns = !mergeColumns; } - return new int[] { rMin, rMax, cMin, cMax }; + return new int[] { + rMin, FastMath.min(rMin + rN, nbRows), + cMin, FastMath.min(cMin + cN, nbColumns) + }; }