SimpleTile.java
- /* Copyright 2013-2022 CS GROUP
- * Licensed to CS GROUP (CS) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * CS licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package org.orekit.rugged.raster;
- import java.util.Arrays;
- import org.hipparchus.geometry.euclidean.threed.Vector3D;
- import org.hipparchus.util.FastMath;
- import org.hipparchus.util.Precision;
- import org.orekit.rugged.errors.DumpManager;
- import org.orekit.rugged.errors.RuggedException;
- import org.orekit.rugged.errors.RuggedMessages;
- import org.orekit.rugged.utils.MaxSelector;
- import org.orekit.rugged.utils.MinSelector;
- import org.orekit.rugged.utils.NormalizedGeodeticPoint;
- /** Simple implementation of a {@link Tile}.
- * @see SimpleTileFactory
- * @author Luc Maisonobe
- * @author Guylaine Prat
- */
- public class SimpleTile implements Tile {
- /** Tolerance used to interpolate points slightly out of tile (in cells). */
- private static final double TOLERANCE = 1.0 / 8.0;
- /** Minimum latitude. */
- private double minLatitude;
- /** Minimum longitude. */
- private double minLongitude;
- /** Step in latitude (size of one raster element). */
- private double latitudeStep;
- /** Step in longitude (size of one raster element). */
- private double longitudeStep;
- /** Number of latitude rows. */
- private int latitudeRows;
- /** Number of longitude columns. */
- private int longitudeColumns;
- /** Minimum elevation. */
- private double minElevation;
- /** Latitude index of min elevation. */
- private int minElevationLatitudeIndex;
- /** Longitude index of min elevation. */
- private int minElevationLongitudeIndex;
- /** Maximum elevation. */
- private double maxElevation;
- /** Latitude index of max elevation. */
- private int maxElevationLatitudeIndex;
- /** Longitude index of max elevation. */
- private int maxElevationLongitudeIndex;
- /** Elevation array. */
- private double[] elevations;
- /** Simple constructor.
- * <p>
- * Creates an empty tile.
- * </p>
- */
- protected SimpleTile() {
- }
- /** {@inheritDoc} */
- @Override
- public void setGeometry(final double newMinLatitude, final double newMinLongitude,
- final double newLatitudeStep, final double newLongitudeStep,
- final int newLatitudeRows, final int newLongitudeColumns) {
- this.minLatitude = newMinLatitude;
- this.minLongitude = newMinLongitude;
- this.latitudeStep = newLatitudeStep;
- this.longitudeStep = newLongitudeStep;
- this.latitudeRows = newLatitudeRows;
- this.longitudeColumns = newLongitudeColumns;
- this.minElevation = Double.POSITIVE_INFINITY;
- this.minElevationLatitudeIndex = -1;
- this.minElevationLongitudeIndex = -1;
- this.maxElevation = Double.NEGATIVE_INFINITY;
- this.maxElevationLatitudeIndex = -1;
- this.maxElevationLongitudeIndex = -1;
- if (newLatitudeRows < 1 || newLongitudeColumns < 1) {
- throw new RuggedException(RuggedMessages.EMPTY_TILE, newLatitudeRows, newLongitudeColumns);
- }
- this.elevations = new double[newLatitudeRows * newLongitudeColumns];
- Arrays.fill(elevations, Double.NaN);
- }
- /** {@inheritDoc} */
- @Override
- public void tileUpdateCompleted() {
- processUpdatedElevation(elevations);
- }
- /** Process elevation array at completion.
- * <p>
- * This method is called at tile update completion, it is
- * expected to be overridden by subclasses. The default
- * implementation does nothing.
- * </p>
- * @param elevationsArray elevations array
- */
- protected void processUpdatedElevation(final double[] elevationsArray) {
- // do nothing by default
- }
- /** {@inheritDoc} */
- @Override
- public double getMinimumLatitude() {
- return getLatitudeAtIndex(0);
- }
- /** {@inheritDoc} */
- @Override
- public double getLatitudeAtIndex(final int latitudeIndex) {
- return minLatitude + latitudeStep * latitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public double getMaximumLatitude() {
- return getLatitudeAtIndex(latitudeRows - 1);
- }
- /** {@inheritDoc} */
- @Override
- public double getMinimumLongitude() {
- return getLongitudeAtIndex(0);
- }
- /** {@inheritDoc} */
- @Override
- public double getLongitudeAtIndex(final int longitudeIndex) {
- return minLongitude + longitudeStep * longitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public double getMaximumLongitude() {
- return getLongitudeAtIndex(longitudeColumns - 1);
- }
- /** {@inheritDoc} */
- @Override
- public double getLatitudeStep() {
- return latitudeStep;
- }
- /** {@inheritDoc} */
- @Override
- public double getLongitudeStep() {
- return longitudeStep;
- }
- /** {@inheritDoc} */
- @Override
- public int getLatitudeRows() {
- return latitudeRows;
- }
- /** {@inheritDoc} */
- @Override
- public int getLongitudeColumns() {
- return longitudeColumns;
- }
- /** {@inheritDoc} */
- @Override
- public double getMinElevation() {
- return minElevation;
- }
- /** {@inheritDoc} */
- @Override
- public int getMinElevationLatitudeIndex() {
- return minElevationLatitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public int getMinElevationLongitudeIndex() {
- return minElevationLongitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public double getMaxElevation() {
- return maxElevation;
- }
- /** {@inheritDoc} */
- @Override
- public int getMaxElevationLatitudeIndex() {
- return maxElevationLatitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public int getMaxElevationLongitudeIndex() {
- return maxElevationLongitudeIndex;
- }
- /** {@inheritDoc} */
- @Override
- public void setElevation(final int latitudeIndex, final int longitudeIndex, final double elevation) {
- if (latitudeIndex < 0 || latitudeIndex > (latitudeRows - 1) ||
- longitudeIndex < 0 || longitudeIndex > (longitudeColumns - 1)) {
- throw new RuggedException(RuggedMessages.OUT_OF_TILE_INDICES,
- latitudeIndex, longitudeIndex,
- latitudeRows - 1, longitudeColumns - 1);
- }
- if (MinSelector.getInstance().selectFirst(elevation, minElevation)) {
- minElevation = elevation;
- minElevationLatitudeIndex = latitudeIndex;
- minElevationLongitudeIndex = longitudeIndex;
- }
- if (MaxSelector.getInstance().selectFirst(elevation, maxElevation)) {
- maxElevation = elevation;
- maxElevationLatitudeIndex = latitudeIndex;
- maxElevationLongitudeIndex = longitudeIndex;
- }
- elevations[latitudeIndex * getLongitudeColumns() + longitudeIndex] = elevation;
- }
- /** {@inheritDoc} */
- @Override
- public double getElevationAtIndices(final int latitudeIndex, final int longitudeIndex) {
- final double elevation = elevations[latitudeIndex * getLongitudeColumns() + longitudeIndex];
- DumpManager.dumpTileCell(this, latitudeIndex, longitudeIndex, elevation);
- return elevation;
- }
- /** {@inheritDoc}
- * <p>
- * This classes uses an arbitrary 1/8 cell tolerance for interpolating
- * slightly out of tile points.
- * </p>
- */
- @Override
- public double interpolateElevation(final double latitude, final double longitude) {
- final double doubleLatitudeIndex = getDoubleLatitudeIndex(latitude);
- final double doubleLongitudeIndex = getDoubleLontitudeIndex(longitude);
- if (doubleLatitudeIndex < -TOLERANCE || doubleLatitudeIndex >= (latitudeRows - 1 + TOLERANCE) ||
- doubleLongitudeIndex < -TOLERANCE || doubleLongitudeIndex >= (longitudeColumns - 1 + TOLERANCE)) {
- throw new RuggedException(RuggedMessages.OUT_OF_TILE_ANGLES,
- FastMath.toDegrees(latitude),
- FastMath.toDegrees(longitude),
- FastMath.toDegrees(getMinimumLatitude()),
- FastMath.toDegrees(getMaximumLatitude()),
- FastMath.toDegrees(getMinimumLongitude()),
- FastMath.toDegrees(getMaximumLongitude()));
- }
- final int latitudeIndex = FastMath.max(0,
- FastMath.min(latitudeRows - 2,
- (int) FastMath.floor(doubleLatitudeIndex)));
- final int longitudeIndex = FastMath.max(0,
- FastMath.min(longitudeColumns - 2,
- (int) FastMath.floor(doubleLongitudeIndex)));
- // bi-linear interpolation
- final double dLat = doubleLatitudeIndex - latitudeIndex;
- final double dLon = doubleLongitudeIndex - longitudeIndex;
- final double e00 = getElevationAtIndices(latitudeIndex, longitudeIndex);
- final double e10 = getElevationAtIndices(latitudeIndex, longitudeIndex + 1);
- final double e01 = getElevationAtIndices(latitudeIndex + 1, longitudeIndex);
- final double e11 = getElevationAtIndices(latitudeIndex + 1, longitudeIndex + 1);
- return (e00 * (1.0 - dLon) + dLon * e10) * (1.0 - dLat) +
- (e01 * (1.0 - dLon) + dLon * e11) * dLat;
- }
- /** {@inheritDoc} */
- @Override
- public NormalizedGeodeticPoint cellIntersection(final NormalizedGeodeticPoint p, final Vector3D los,
- final int latitudeIndex, final int longitudeIndex) {
- // ensure neighboring cells to not fall out of tile
- final int iLat = FastMath.max(0, FastMath.min(latitudeRows - 2, latitudeIndex));
- final int jLong = FastMath.max(0, FastMath.min(longitudeColumns - 2, longitudeIndex));
- // Digital Elevation Mode coordinates at cell vertices
- final double x00 = getLongitudeAtIndex(jLong);
- final double y00 = getLatitudeAtIndex(iLat);
- final double z00 = getElevationAtIndices(iLat, jLong);
- final double z01 = getElevationAtIndices(iLat + 1, jLong);
- final double z10 = getElevationAtIndices(iLat, jLong + 1);
- final double z11 = getElevationAtIndices(iLat + 1, jLong + 1);
- // normalize back to tile coordinates
- final NormalizedGeodeticPoint tileP = new NormalizedGeodeticPoint(p.getLatitude(),
- p.getLongitude(),
- p.getAltitude(),
- x00);
- // line-of-sight coordinates at close points
- final double dxA = (tileP.getLongitude() - x00) / longitudeStep;
- final double dyA = (tileP.getLatitude() - y00) / latitudeStep;
- final double dzA = tileP.getAltitude();
- final double dxB = dxA + los.getX() / longitudeStep;
- final double dyB = dyA + los.getY() / latitudeStep;
- final double dzB = dzA + los.getZ();
- // points along line-of-sight can be defined as a linear progression
- // along the line depending on free variable t: p(t) = p + t * los.
- // As the point latitude and longitude are linear with respect to t,
- // and as Digital Elevation Model is quadratic with respect to latitude
- // and longitude, the altitude of DEM at same horizontal position as
- // point is quadratic in t:
- // z_DEM(t) = u t² + v t + w
- final double u = (dxA - dxB) * (dyA - dyB) * (z00 - z10 - z01 + z11);
- final double v = ((dxA - dxB) * (1 - dyA) + (dyA - dyB) * (1 - dxA)) * z00 +
- (dxA * (dyA - dyB) - (dxA - dxB) * (1 - dyA)) * z10 +
- (dyA * (dxA - dxB) - (dyA - dyB) * (1 - dxA)) * z01 +
- ((dxB - dxA) * dyA + (dyB - dyA) * dxA) * z11;
- final double w = (1 - dxA) * ((1 - dyA) * z00 + dyA * z01) +
- dxA * ((1 - dyA) * z10 + dyA * z11);
- // subtract linear z from line-of-sight
- // z_DEM(t) - z_LOS(t) = a t² + b t + c
- final double a = u;
- final double b = v + dzA - dzB;
- final double c = w - dzA;
- // solve the equation
- final double t1;
- final double t2;
- if (FastMath.abs(a) <= Precision.EPSILON * FastMath.abs(c)) {
- // the equation degenerates to a linear (or constant) equation
- final double t = -c / b;
- t1 = Double.isNaN(t) ? 0.0 : t;
- t2 = Double.POSITIVE_INFINITY;
- } else {
- // the equation is quadratic
- final double b2 = b * b;
- final double fac = 4 * a * c;
- if (b2 < fac) {
- // no intersection at all
- return null;
- }
- final double s = FastMath.sqrt(b2 - fac);
- t1 = (b < 0) ? (s - b) / (2 * a) : -2 * c / (b + s);
- t2 = c / (a * t1);
- }
- final NormalizedGeodeticPoint p1 = interpolate(t1, tileP, dxA, dyA, los, x00);
- final NormalizedGeodeticPoint p2 = interpolate(t2, tileP, dxA, dyA, los, x00);
- // select the first point along line-of-sight
- if (p1 == null) {
- return p2;
- } else if (p2 == null) {
- return p1;
- } else {
- return t1 <= t2 ? p1 : p2;
- }
- }
- /** Interpolate point along a line.
- * @param t abscissa along the line
- * @param tileP start point, normalized to tile area
- * @param dxP relative coordinate of the start point with respect to current cell
- * @param dyP relative coordinate of the start point with respect to current cell
- * @param los direction of the line-of-sight, in geodetic space
- * @param centralLongitude reference longitude lc such that the point longitude will
- * be normalized between lc-π and lc+π
- * @return interpolated point along the line
- */
- private NormalizedGeodeticPoint interpolate(final double t, final NormalizedGeodeticPoint tileP,
- final double dxP, final double dyP,
- final Vector3D los, final double centralLongitude) {
- if (Double.isInfinite(t)) {
- return null;
- }
- final double dx = dxP + t * los.getX() / longitudeStep;
- final double dy = dyP + t * los.getY() / latitudeStep;
- if (dx >= -TOLERANCE && dx <= 1 + TOLERANCE && dy >= -TOLERANCE && dy <= 1 + TOLERANCE) {
- return new NormalizedGeodeticPoint(tileP.getLatitude() + t * los.getY(),
- tileP.getLongitude() + t * los.getX(),
- tileP.getAltitude() + t * los.getZ(),
- centralLongitude);
- } else {
- return null;
- }
- }
- /** {@inheritDoc} */
- @Override
- public int getFloorLatitudeIndex(final double latitude) {
- return (int) FastMath.floor(getDoubleLatitudeIndex(latitude));
- }
- /** {@inheritDoc} */
- @Override
- public int getFloorLongitudeIndex(final double longitude) {
- return (int) FastMath.floor(getDoubleLontitudeIndex(longitude));
- }
- /** Get the latitude index of a point.
- * @param latitude geodetic latitude (rad)
- * @return latitude index (it may lie outside of the tile!)
- */
- private double getDoubleLatitudeIndex(final double latitude) {
- return (latitude - minLatitude) / latitudeStep;
- }
- /** Get the longitude index of a point.
- * @param longitude geodetic longitude (rad)
- * @return longitude index (it may lie outside of the tile!)
- */
- private double getDoubleLontitudeIndex(final double longitude) {
- return (longitude - minLongitude) / longitudeStep;
- }
- /** {@inheritDoc} */
- @Override
- public Location getLocation(final double latitude, final double longitude) {
- final int latitudeIndex = getFloorLatitudeIndex(latitude);
- final int longitudeIndex = getFloorLongitudeIndex(longitude);
- if (longitudeIndex < 0) {
- if (latitudeIndex < 0) {
- return Location.SOUTH_WEST;
- } else if (latitudeIndex <= (latitudeRows - 2)) {
- return Location.WEST;
- } else {
- return Location.NORTH_WEST;
- }
- } else if (longitudeIndex <= (longitudeColumns - 2)) {
- if (latitudeIndex < 0) {
- return Location.SOUTH;
- } else if (latitudeIndex <= (latitudeRows - 2)) {
- return Location.HAS_INTERPOLATION_NEIGHBORS;
- } else {
- return Location.NORTH;
- }
- } else {
- if (latitudeIndex < 0) {
- return Location.SOUTH_EAST;
- } else if (latitudeIndex <= (latitudeRows - 2)) {
- return Location.EAST;
- } else {
- return Location.NORTH_EAST;
- }
- }
- }
- }