protected void preCache(List<Position> grid, Position centerPosition)
        throws InterruptedException {
      // Pre-cache the tiles that will be needed for the intersection calculations.
      double n = 0;
      final long start = System.currentTimeMillis();
      for (Position gridPos : grid) // for each grid point.
      {
        final double progress = 100 * (n++ / grid.size());
        terrain.cacheIntersectingTiles(centerPosition, gridPos);

        SwingUtilities.invokeLater(
            new Runnable() {
              public void run() {
                progressBar.setValue((int) progress);
                progressBar.setString(null);
              }
            });
      }

      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              progressBar.setValue(100);
            }
          });

      long end = System.currentTimeMillis();
      System.out.printf(
          "Pre-caching time %d milliseconds, cache usage %f, tiles %d\n",
          end - start, terrain.getCacheUsage(), terrain.getNumCacheEntries());
    }
  protected static void generateReferenceValues(String filePath, Sector sector)
      throws FileNotFoundException {
    HighResolutionTerrain hrt = new HighResolutionTerrain(new Earth(), sector, null, 1.0);

    ArrayList<Position> referenceLocations = generateReferenceLocations(hrt.getSector(), 5, 5);
    ArrayList<Position> referencePositions = computeElevations(referenceLocations);
    writeReferencePositions(filePath, referencePositions);
  }
  protected static ArrayList<Position> computeElevations(ArrayList<Position> locations) {
    Sector sector = Sector.boundingSector(locations);
    HighResolutionTerrain hrt = new HighResolutionTerrain(new Earth(), sector, null, 1.0);

    ArrayList<Position> computedPositions = new ArrayList<Position>();
    for (LatLon latLon : locations) {
      Double elevation = hrt.getElevation(latLon);
      computedPositions.add(new Position(latLon, Math.round(elevation * 10000.0) / 10000.0));
    }

    return computedPositions;
  }
    /**
     * Performs one line of sight calculation between the reference position and a specified grid
     * position.
     *
     * @param gridPosition the grid position.
     * @throws InterruptedException if the operation is interrupted.
     */
    protected void performIntersection(Position gridPosition) throws InterruptedException {
      // Intersect the line between this grid point and the selected position.
      Intersection[] intersections = this.terrain.intersect(this.referencePosition, gridPosition);
      if (intersections == null || intersections.length == 0) {
        // No intersection, so the line goes from the center to the grid point.
        this.sightLines.add(new Position[] {this.referencePosition, gridPosition});
        return;
      }

      // Only the first intersection is shown.
      Vec4 iPoint = intersections[0].getIntersectionPoint();
      Vec4 gPoint =
          terrain.getSurfacePoint(
              gridPosition.getLatitude(), gridPosition.getLongitude(), gridPosition.getAltitude());

      // Check to see whether the intersection is beyond the grid point.
      if (iPoint.distanceTo3(this.referencePoint) >= gPoint.distanceTo3(this.referencePoint)) {
        // Intersection is beyond the grid point; the line goes from the center to the grid point.
        this.addSightLine(this.referencePosition, gridPosition);
        return;
      }

      // Compute the position corresponding to the intersection.
      Position iPosition = this.terrain.getGlobe().computePositionFromPoint(iPoint);

      // The sight line goes from the user-selected position to the intersection position.
      this.addSightLine(this.referencePosition, new Position(iPosition, 0));

      // Keep track of the intersection positions.
      this.addIntersectionPosition(iPosition);

      this.updateProgress();
    }
    protected void performIntersectionTests(final Position curPos) throws InterruptedException {
      // Clear the results lists when the user selects a new location.
      this.firstIntersectionPositions.clear();
      this.sightLines.clear();

      // Raise the selected location and the grid points a little above ground just to show we can.
      final double height = 5; // meters

      // Form the grid.
      double gridRadius = GRID_RADIUS.degrees;
      Sector sector =
          Sector.fromDegrees(
              curPos.getLatitude().degrees - gridRadius, curPos.getLatitude().degrees + gridRadius,
              curPos.getLongitude().degrees - gridRadius,
                  curPos.getLongitude().degrees + gridRadius);

      this.grid = buildGrid(sector, height, GRID_DIMENSION, GRID_DIMENSION);
      this.numGridPoints = grid.size();

      // Compute the position of the selected location (incorporate its height).
      this.referencePosition = new Position(curPos.getLatitude(), curPos.getLongitude(), height);
      this.referencePoint =
          terrain.getSurfacePoint(curPos.getLatitude(), curPos.getLongitude(), height);

      //            // Pre-caching is unnecessary and is useful only when it occurs before the
      // intersection
      //            // calculations. It will incur extra overhead otherwise. The normal intersection
      // calculations
      //            // cause the same caching, making subsequent calculations on the same area
      // faster.
      //            this.preCache(grid, this.referencePosition);

      // On the EDT, show the grid.
      SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              progressBar.setValue(0);
              progressBar.setString(null);
              clearLayers();
              showGrid(grid, referencePosition);
              getWwd().redraw();
            }
          });

      // Perform the intersection calculations.
      this.startTime = System.currentTimeMillis();
      for (Position gridPos : this.grid) // for each grid point.
      {
        //noinspection ConstantConditions
        if (NUM_THREADS > 0) this.threadPool.execute(new Intersector(gridPos));
        else performIntersection(gridPos);
      }
    }