package com.revolsys.elevation.gridded; import com.revolsys.awt.WebColors; import com.revolsys.geometry.model.BoundingBox; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.Triangle; import com.revolsys.geometry.model.impl.BoundingBoxDoubleGf; import com.revolsys.geometry.util.BoundingBoxUtil; import com.revolsys.properties.BaseObjectWithProperties; import com.revolsys.spring.resource.Resource; public abstract class AbstractGriddedElevationModel extends BaseObjectWithProperties implements GriddedElevationModel { protected double[] bounds = BoundingBoxUtil.newBounds(3); private int gridHeight; private int gridWidth; private double minColourMultiple; private double colourGreyMultiple; private int gridCellSize; private Resource resource; private GeometryFactory geometryFactory; private BoundingBox boundingBox; private boolean zBoundsUpdateRequired = true; private double scaleXY; public AbstractGriddedElevationModel() { } public AbstractGriddedElevationModel(final GeometryFactory geometryFactory, final BoundingBox boundingBox, final int gridWidth, final int gridHeight, final int gridCellSize) { this.gridWidth = gridWidth; this.gridHeight = gridHeight; this.gridCellSize = gridCellSize; setGeometryFactory(geometryFactory); final double minX = boundingBox.getMinX(); final double minY = boundingBox.getMinY(); final double minZ = boundingBox.getMinZ(); final double maxX = boundingBox.getMaxX(); final double maxY = boundingBox.getMaxY(); final double maxZ = boundingBox.getMaxZ(); this.bounds = new double[] { minX, minY, minZ, maxX, maxY, maxZ }; if (Double.isFinite(minZ)) { this.colourGreyMultiple = 1.0 / (maxZ - minZ); this.minColourMultiple = minZ * this.colourGreyMultiple; this.zBoundsUpdateRequired = false; } this.boundingBox = new BoundingBoxDoubleGf(geometryFactory, 3, this.bounds); } public AbstractGriddedElevationModel(final GeometryFactory geometryFactory, final double minX, final double minY, final int gridWidth, final int gridHeight, final int gridCellSize) { this.gridWidth = gridWidth; this.gridHeight = gridHeight; this.gridCellSize = gridCellSize; setGeometryFactory(geometryFactory); this.bounds = new double[] { minX, minY, Double.NaN, minX + gridWidth * gridCellSize, minY + gridHeight * gridCellSize, Double.NaN }; this.boundingBox = new BoundingBoxDoubleGf(geometryFactory, 3, this.bounds); } @Override public void clear() { clearCachedObjects(); }; protected void clearCachedObjects() { this.zBoundsUpdateRequired = true; } protected void expandZ() { this.bounds[2] = Double.NaN; this.bounds[5] = Double.NaN; final int gridWidth = this.gridWidth; final int gridHeight = this.gridHeight; for (int gridY = 0; gridY < gridHeight; gridY++) { for (int gridX = 0; gridX < gridWidth; gridX++) { final double elevation = getElevation(gridX, gridY); if (Double.isFinite(elevation)) { expandZ(elevation); } } } } protected void expandZ(final double elevation) { if (Double.isFinite(elevation)) { final double minZ = this.bounds[2]; if (elevation < minZ || !Double.isFinite(minZ)) { this.bounds[2] = elevation; } final double maxZ = this.bounds[5]; if (elevation > maxZ || !Double.isFinite(maxZ)) { this.bounds[5] = elevation; } } } @Override public BoundingBox getBoundingBox() { updateZBoundingBox(); return this.boundingBox; } @Override public int getColour(final int gridX, final int gridY) { final int colour; final double elevation = getElevation(gridX, gridY); if (Double.isNaN(elevation)) { colour = NULL_COLOUR; } else { final double elevationMultiple = elevation * this.colourGreyMultiple; final double elevationPercent = elevationMultiple - this.minColourMultiple; final int grey = (int)Math.round(elevationPercent * 255); colour = WebColors.colorToRGB(255, grey, grey, grey); } return colour; } @Override public double getElevation(int gridX, int gridY) { final int width = getGridWidth(); final int height = getGridHeight(); if (gridX < 0 || gridY < 0) { return Double.NaN; } else { if (gridX >= width) { if (gridX == width) { gridX--; } else { return Double.NaN; } } if (gridY >= height) { if (gridY == height) { gridY--; } else { return Double.NaN; } } return getElevationDo(gridX, gridY, width); } } protected abstract double getElevationDo(int gridX, int gridY, int gridWidth); @Override public double getElevationFast(final int gridX, final int gridY) { return getElevationDo(gridX, gridY, this.gridWidth); } @Override public GeometryFactory getGeometryFactory() { return this.geometryFactory; } @Override public int getGridCellSize() { return this.gridCellSize; } @Override public int getGridCellX(final double x) { final double minX = this.bounds[0]; final double deltaX = x - minX; final double cellDiv = deltaX / this.gridCellSize; final int gridX = (int)Math.floor(cellDiv); return gridX; } @Override public int getGridCellY(final double y) { final double minY = this.bounds[1]; final double deltaY = y - minY; final double cellDiv = deltaY / this.gridCellSize; final int gridY = (int)Math.floor(cellDiv); return gridY; } @Override public int getGridHeight() { return this.gridHeight; } @Override public int getGridWidth() { return this.gridWidth; } @Override public double getMaxX() { return this.bounds[3]; } @Override public double getMaxY() { return this.bounds[4]; } @Override public double getMaxZ() { updateZBoundingBox(); return this.bounds[5]; } @Override public double getMinX() { return this.bounds[0]; } @Override public double getMinY() { return this.bounds[1]; } @Override public double getMinZ() { updateZBoundingBox(); return this.bounds[2]; } @Override public Resource getResource() { return this.resource; } @Override public double getScaleXY() { return this.scaleXY; } @Override public boolean isEmpty() { getBoundingBox(); return Double.isNaN(this.bounds[2]); } public boolean isModified() { return this.zBoundsUpdateRequired; } @Override public void setBoundingBox(final BoundingBox boundingBox) { this.bounds = boundingBox.getMinMaxValues(3); } @Override public void setElevationsForTriangle(final double x1, final double y1, final double z1, final double x2, final double y2, final double z2, final double x3, final double y3, final double z3) { final double scaleXy = this.scaleXY; double minX = x1; double maxX = x1; if (x2 < minX) { minX = x2; } else if (x2 > maxX) { maxX = x2; } if (x2 < minX) { minX = x2; } else if (x2 > maxX) { maxX = x2; } if (x3 < minX) { minX = x3; } else if (x3 > maxX) { maxX = x3; } double minY = y1; double maxY = y1; if (y2 < minY) { minY = y2; } else if (y2 > maxY) { maxY = y2; } if (y3 < minY) { minY = y3; } else if (y3 > maxY) { maxY = y3; } final int gridCellSize = this.gridCellSize; final double[] bounds = this.bounds; final double gridMinX = bounds[0]; final double gridMaxX = bounds[3]; final double startX; if (minX < gridMinX) { startX = gridMinX; } else { startX = Math.ceil(minX / gridCellSize) * gridCellSize; } if (maxX > gridMaxX) { maxX = gridMaxX; } final double gridMinY = bounds[1]; final double gridMaxY = bounds[4]; final double startY; if (minY < gridMinY) { startY = gridMinY; } else { startY = Math.ceil(minY / gridCellSize) * gridCellSize; } if (maxY > gridMaxY) { maxY = gridMaxY; } for (double y = startY; y < maxY; y += gridCellSize) { for (double x = startX; x < maxX; x += gridCellSize) { if (Triangle.containsPoint(scaleXy, x1, y1, x2, y2, x3, y3, x, y)) { final double elevation = Triangle.getElevation(x1, y1, z1, x2, y2, z2, x3, y3, z3, x, y); if (Double.isFinite(elevation)) { setElevation(x, y, elevation); } } } } } protected void setGeometryFactory(final GeometryFactory geometryFactory) { this.geometryFactory = geometryFactory; this.scaleXY = geometryFactory.getScaleXY(); } public void setGridCellSize(final int gridCellSize) { this.gridCellSize = gridCellSize; } public void setGridHeight(final int gridHeight) { this.gridHeight = gridHeight; } public void setGridWidth(final int gridWidth) { this.gridWidth = gridWidth; } @Override public void setResource(final Resource resource) { this.resource = resource; } public void setZBoundsUpdateRequired(final boolean zBoundsUpdateRequired) { this.zBoundsUpdateRequired = zBoundsUpdateRequired; } protected void setZRange(final double minZ, final double maxZ) { final double oldMinZ = this.bounds[2]; if (minZ < oldMinZ || !Double.isFinite(oldMinZ)) { this.bounds[2] = minZ; } final double oldMaxZ = this.bounds[5]; if (maxZ < oldMaxZ || !Double.isFinite(oldMaxZ)) { this.bounds[5] = maxZ; } } @Override public String toString() { return getBoundingBox() + " " + this.gridWidth + "x" + this.gridHeight + " c=" + this.gridCellSize; } @Override public void updateZBoundingBox() { if (this.zBoundsUpdateRequired) { expandZ(); final double minZ = this.bounds[2]; final double maxZ = this.bounds[5]; if (Double.isFinite(minZ)) { this.colourGreyMultiple = 1.0 / (maxZ - minZ); this.minColourMultiple = minZ * this.colourGreyMultiple; } this.boundingBox = new BoundingBoxDoubleGf(this.geometryFactory, 3, this.bounds); this.zBoundsUpdateRequired = false; } } }