package edu.oregonstate.cartography.simplefeatures; import edu.oregonstate.cartography.grid.Grid; import edu.oregonstate.cartography.grid.operators.PlanObliqueOperator; import java.util.ArrayList; /** * Applies plan oblique shearing to a set of lines * * @author Bernhard Jenny, Cartography and Geovisualization Group, Oregon State * University */ public class PlanObliqueShearing { /** * shearing factor */ final double k; /** * elevation model */ final Grid grid; /** * sheared elevation model */ final Grid shearedGrid; /** * reference elevation that will not be sheared. Usually the lowest * elevation in the elevation model. */ final float refElevation; /** * if true occluded line segments are clipped */ final boolean clipOccluded; /** * Constructor * @param inclinationAngleDeg Shearing angle in degrees. * @param grid Elevation model to shear. * @param refElevation Elevation which is not sheared. Usually the lowest * elevation in the elevation model. * @param clipOccluded If true occluded line segments are clipped */ public PlanObliqueShearing(double inclinationAngleDeg, Grid grid, float refElevation, boolean clipOccluded) { k = 1d / Math.tan(Math.toRadians(inclinationAngleDeg)); this.grid = grid; this.refElevation = refElevation; PlanObliqueOperator op = new PlanObliqueOperator(inclinationAngleDeg, refElevation); shearedGrid = op.operate(grid); this.clipOccluded = clipOccluded; } /** * Returns true if the location at x/y in the original terrain model is * occluded in the sheared terrain. * @param x Horizontal location * @param y Vertical location * @return True if x/y is occluded */ public boolean isOccluded(double x, double y) { // tolerance to avoid z-fighting final double TOL = grid.getCellSize() / 2; float z = grid.getBilinearInterpol(x, y); double shearedY = y + (z - refElevation) * k; return shearedGrid.getBilinearInterpol(x, shearedY) > z + TOL; } /** * Shear a line in vertical direction. * * @param line The line to shear. * @return Sheared lines */ private ArrayList<LineString> shear(LineString line) { int nPoints = line.getNumPoints(); LineString shearedLine = new LineString(); ArrayList<LineString> sheared = new ArrayList(); for (int ptID = 0; ptID < nPoints; ptID++) { Point pt = line.getPointN(ptID); double x = pt.getX(); double y = pt.getY(); if (x < grid.getWest()) { x = grid.getWest(); } if (x > grid.getEast()) { x = grid.getEast(); } if (y > grid.getNorth()) { y = grid.getNorth(); } if (y < grid.getSouth()) { y = grid.getSouth(); } if (clipOccluded && isOccluded(x, y)) { if (shearedLine.getNumPoints() > 1) { sheared.add(shearedLine); } if (shearedLine.getNumPoints() != 0) { shearedLine = new LineString(); } } else { float z = grid.getBilinearInterpol(x, y); Point shearedPt = new Point(x, y + (z - refElevation) * k); if (!Double.isNaN(shearedPt.getX()) && !Double.isNaN(shearedPt.getY())) { shearedLine.addPoint(shearedPt); } } } if (shearedLine.getNumPoints() > 1) { sheared.add(shearedLine); } return sheared; } /** * Construct a line along the border of a grid. One point per grid vertex * along the upper and lower border of the grid. A single line segment along * the two vertical borders. * * @param grid * @return */ private LineString boundingLine() { double cellSize = grid.getCellSize(); double west = grid.getWest(); LineString line = new LineString(); int nCols = grid.getCols(); // lower border from left to right for (int c = 0; c < nCols; c++) { double x = west + c * cellSize; line.addPoint(new Point(x, grid.getSouth() + cellSize)); } // vertical line from lower right to upper right corner line.addPoint(new Point(grid.getEast(), grid.getNorth() - cellSize)); // upper border from right to left for (int c = nCols - 1; c >= 0; c--) { double x = west + c * cellSize; line.addPoint(new Point(x, grid.getNorth() - cellSize)); } // vertical line from upper left to lower left corner line.addPoint(new Point(grid.getWest(), grid.getSouth() + cellSize)); return line; } public GeometryCollection shear(GeometryCollection lines) { GeometryCollection shearedLines = new GeometryCollection(); int nLines = lines.getNumGeometries(); for (int lineID = 0; lineID < nLines; lineID++) { LineString line = (LineString) lines.getGeometryN(lineID); ArrayList<LineString> sheared = shear(line); for (LineString l : sheared) { shearedLines.addGeometry(l); } } // add sheared line along border of the grid shearedLines.addGeometry(shear(boundingLine()).get(0)); return shearedLines; } }