]> gitweb.fperrin.net Git - GpsPrune.git/blobdiff - tim/prune/threedee/TerrainHelper.java
Version 18, July 2015
[GpsPrune.git] / tim / prune / threedee / TerrainHelper.java
index 71bc196a46dce7c8c4658dfb23490ab8f23fc323..ffe9db888d1ea443ebf1f488d9d4e0e0cc9d9106 100644 (file)
@@ -149,8 +149,8 @@ public class TerrainHelper
                                // Create a new point with the appropriate lat and long, with no altitude
                                double pX = xRange.getMinimum() + j * xStep;
                                DataPoint point = new DataPoint(
-                                       new Latitude(MapUtils.getLatitudeFromY(pY), Coordinate.FORMAT_NONE),
-                                       new Longitude(MapUtils.getLongitudeFromX(pX), Coordinate.FORMAT_NONE),
+                                       new Latitude(MapUtils.getLatitudeFromY(pY), Coordinate.FORMAT_DECIMAL_FORCE_POINT),
+                                       new Longitude(MapUtils.getLongitudeFromX(pX), Coordinate.FORMAT_DECIMAL_FORCE_POINT),
                                        null);
                                //System.out.println("Created point at " + point.getLatitude().output(Coordinate.FORMAT_DEG_MIN_SEC)
                                //      + ", " + point.getLongitude().output(Coordinate.FORMAT_DEG_MIN_SEC));
@@ -196,12 +196,13 @@ public class TerrainHelper
        {
                int numVoids = countVoids(inTerrainTrack);
                if (numVoids == 0) {return;}
-               // System.out.println("Starting to fix, num voids = " + numVoids);
+               //System.out.println("Starting to fix, num voids = " + numVoids);
                // Fix the holes which are surrounded on all four sides by non-holes
                fixSingleHoles(inTerrainTrack);
-               // System.out.println("Fixed single holes, now num voids = " + countVoids(inTerrainTrack));
+               //System.out.println("Fixed single holes, now num voids = " + countVoids(inTerrainTrack));
                // Maybe there is something to do in the corners?
-               fixCorners(inTerrainTrack);
+               fixCornersAndEdges(inTerrainTrack);
+               //System.out.println("Fixed corners, now num voids = " + countVoids(inTerrainTrack));
                // Now fix the bigger holes, which should fix everything left
                fixBiggerHoles(inTerrainTrack);
                final int numHolesLeft = countVoids(inTerrainTrack);
@@ -216,6 +217,21 @@ public class TerrainHelper
         */
        private static int countVoids(Track inTerrainTrack)
        {
+               // DEBUG: Show state of voids first
+//             final int gridSize = (int) Math.sqrt(inTerrainTrack.getNumPoints());
+//             StringBuilder sb = new StringBuilder();
+//             for (int i=0; i<inTerrainTrack.getNumPoints(); i++)
+//             {
+//                     if ((i%gridSize) == 0) sb.append('\n');
+//                     if (inTerrainTrack.getPoint(i).hasAltitude()) {
+//                             sb.append('A');
+//                     } else {
+//                             sb.append(' ');
+//                     }
+//             }
+//             System.out.println("Voids:" + sb.toString());
+               // END DEBUG
+
                int numVoids = 0;
                if (inTerrainTrack != null)
                {
@@ -271,7 +287,7 @@ public class TerrainHelper
 
                                                double altitude = 0.0;
                                                if (pll != null && pll.hasAltitude() && prr != null && prr.hasAltitude()
-                                                       && puu != null && puu.hasAltitude() && pdd != null & pdd.hasAltitude())
+                                                       && puu != null && puu.hasAltitude() && pdd != null && pdd.hasAltitude())
                                                {
                                                        // Use the double-neighbours too to take into account the gradients
                                                        altitude = (
@@ -304,15 +320,19 @@ public class TerrainHelper
        }
 
        /**
-        * Try to fix the corners, if they're blank
+        * Try to fix the corners and edges, if they're blank
         * @param inTerrainTrack terrain track
         */
-       private void fixCorners(Track inTerrainTrack)
+       private void fixCornersAndEdges(Track inTerrainTrack)
        {
                fixCorner(inTerrainTrack, 0, 1, 1);
                fixCorner(inTerrainTrack, _gridSize-1, -1, 1);
                fixCorner(inTerrainTrack, (_gridSize-1)*_gridSize, 1, -1);
                fixCorner(inTerrainTrack, _gridSize*_gridSize-1, -1, -1);
+               fixEdge(inTerrainTrack, 0, 1);
+               fixEdge(inTerrainTrack, _gridSize-1, _gridSize);
+               fixEdge(inTerrainTrack, (_gridSize-1)*_gridSize, -_gridSize);
+               fixEdge(inTerrainTrack, _gridSize*_gridSize-1, -1);
        }
 
        /**
@@ -351,6 +371,46 @@ public class TerrainHelper
                                // System.out.println("Averaging values " + alt1.getMetricValue() + " and " + alt2.getMetricValue());
                                int newAltitude = (int) ((alt1.getMetricValue() + alt2.getMetricValue()) / 2.0);
                                corner.setFieldValue(Field.ALTITUDE, "" + newAltitude, false);
+                               // TODO: Check forcing metres?  Is there a nicer way?
+                       }
+               }
+       }
+
+       /**
+        * Fix any holes found in the specified edge
+        * @param inTerrainTrack terrain track
+        * @param inCornerIndex index of corner to start from
+        * @param inInc increment along edge
+        */
+       private void fixEdge(Track inTerrainTrack, int inCornerIndex, int inInc)
+       {
+               int prevIndexWithAlt = -1;
+               int sIndex = inCornerIndex;
+               if (inTerrainTrack.getPoint(sIndex).hasAltitude()) {prevIndexWithAlt = 0;}
+               for (int i=1; i<_gridSize; i++)
+               {
+                       sIndex += inInc;
+                       if (inTerrainTrack.getPoint(sIndex).hasAltitude())
+                       {
+                               if (prevIndexWithAlt >= 0 && prevIndexWithAlt < (i-1))
+                               {
+                                       final int gapLen = i - prevIndexWithAlt;
+                                       final int cellIndex1 = inCornerIndex + prevIndexWithAlt * inInc;
+                                       final double alt1 = inTerrainTrack.getPoint(cellIndex1).getAltitude().getMetricValue();
+                                       final int cellIndex2 = inCornerIndex + i * inInc;
+                                       final double alt2 = inTerrainTrack.getPoint(cellIndex2).getAltitude().getMetricValue();
+                                       //System.out.println("Altitude along edge goes from " + alt1 + " (at " + prevIndexWithAlt + ") to " +
+                                       //              alt2 + " (at " + i + ")");
+                                       for (int j = 1; j < gapLen; j++)
+                                       {
+                                               final double alt = alt1 + (alt2-alt1) * j / gapLen;
+                                               //System.out.println("Fill in " + (prevIndexWithAlt + j) + "(" + (inCornerIndex + (prevIndexWithAlt + j) * inInc) + ")  with alt " + (int) alt);
+                                               final DataPoint p = inTerrainTrack.getPoint(inCornerIndex + (prevIndexWithAlt + j) * inInc);
+                                               p.setFieldValue(Field.ALTITUDE, "" + (int) alt, false);
+                                               // TODO: Check forcing metres?
+                                       }
+                               }
+                               prevIndexWithAlt = i;
                        }
                }
        }
@@ -361,7 +421,7 @@ public class TerrainHelper
         */
        private void fixBiggerHoles(Track inTerrainTrack)
        {
-               double[] altitudes = new double[inTerrainTrack.getNumPoints()];
+               TerrainPatch patch = new TerrainPatch(_gridSize);
                for (int i=0; i<_gridSize; i++)
                {
                        int prevHoriz = -1, prevVert = -1;
@@ -371,18 +431,13 @@ public class TerrainHelper
                                {
                                        if (prevHoriz > -1 && prevHoriz != (j-1))
                                        {
-//                                             System.out.println("Found a gap for y=" + i +" between x=" + prevHoriz + " and " + j + " (" + (j-prevHoriz-1) + ")");
+                                               //System.out.println("Found a gap for y=" + i +" between x=" + prevHoriz + " and " + j + " (" + (j-prevHoriz-1) + ")");
                                                double startVal = inTerrainTrack.getPoint(i * _gridSize + prevHoriz).getAltitude().getMetricValue();
                                                double endVal   = inTerrainTrack.getPoint(i * _gridSize + j).getAltitude().getMetricValue();
                                                for (int k=prevHoriz + 1; k< j; k++)
                                                {
                                                        double val = startVal + (k-prevHoriz) * (endVal-startVal) / (j-prevHoriz);
-                                                       if (altitudes[i * _gridSize + k] > 0.0) {
-                                                               altitudes[i * _gridSize + k] = (altitudes[i * _gridSize + k] + val) / 2.0;
-                                                       }
-                                                       else {
-                                                               altitudes[i * _gridSize + k] = val;
-                                                       }
+                                                       patch.addAltitude(i * _gridSize + k, val, k-prevHoriz, j-prevHoriz);
                                                }
                                        }
                                        prevHoriz = j;
@@ -391,32 +446,31 @@ public class TerrainHelper
                                {
                                        if (prevVert > -1 && prevVert != (j-1))
                                        {
-//                                             System.out.println("Found a gap for x=" + i +" between y=" + prevVert + " and " + j + " (" + (j-prevVert-1) + ")");
+                                               //System.out.println("Found a gap for x=" + i +" between y=" + prevVert + " and " + j + " (" + (j-prevVert-1) + ")");
                                                double startVal = inTerrainTrack.getPoint(prevVert * _gridSize + i).getAltitude().getMetricValue();
                                                double endVal   = inTerrainTrack.getPoint(j * _gridSize + i).getAltitude().getMetricValue();
                                                for (int k=prevVert + 1; k< j; k++)
                                                {
                                                        double val = startVal + (k-prevVert) * (endVal-startVal) / (j-prevVert);
-                                                       if (altitudes[k * _gridSize + i] > 0.0) {
-                                                               altitudes[k * _gridSize + i] = (altitudes[k * _gridSize + i] + val) / 2.0;
-                                                       }
-                                                       else {
-                                                               altitudes[k * _gridSize + i] = val;
-                                                       }
+                                                       patch.addAltitude(k * _gridSize + i, val, k-prevVert, j-prevVert);
                                                }
                                        }
                                        prevVert = j;
                                }
                        }
                }
-               // Now the doubles have been set and/or averaged, we can set the values in the points
+               // Smooth the patch to reduce the blocky effect from the voids
+               patch.smooth();
+
+               // Now the doubles have been set and averaged, we can set the values in the points
                for (int i=0; i<inTerrainTrack.getNumPoints(); i++)
                {
                        DataPoint p = inTerrainTrack.getPoint(i);
-                       if (!p.hasAltitude() && altitudes[i] > 0.0)
+                       if (!p.hasAltitude())
                        {
-                               p.setFieldValue(Field.ALTITUDE, "" + altitudes[i], false);
-                               p.getAltitude().reset(new Altitude((int) altitudes[i], UnitSetLibrary.UNITS_METRES));
+                               final double altitude = patch.getAltitude(i);
+                               p.setFieldValue(Field.ALTITUDE, "" + altitude, false);
+                               p.getAltitude().reset(new Altitude((int) altitude, UnitSetLibrary.UNITS_METRES));
                        }
                }
        }