/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2006-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2012, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.referencing.operation.transform;
import java.util.Arrays;
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.TransformException;
import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.referencing.operation.transform.IterationStrategy;
/**
* Base class for transformations from a <cite>height above the ellipsoid</cite> to a
* <cite>height above the geoid</cite>. This transform expects three-dimensional geographic
* coordinates in (<var>longitude</var>, <var>latitude</var>, <var>height</var>) order. The
* transformations are usually backed by some ellipsoid-dependent database.
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @version 3.20
*
* @since 2.3
* @module
*/
public abstract class VerticalTransform extends AbstractMathTransform {
/**
* The input and output dimension.
*/
private static final int DIMENSION = 3;
/**
* Creates a new instance of {@code VerticalTransform}.
*/
protected VerticalTransform() {
}
/**
* Gets the dimension of input points, which is 3.
*/
@Override
public final int getSourceDimensions() {
return DIMENSION;
}
/**
* Gets the dimension of output points, which is 3.
*/
@Override
public final int getTargetDimensions() {
return DIMENSION;
}
/**
* Returns the value to add to a <cite>height above the ellipsoid</cite> in order to get a
* <cite>height above the geoid</cite> for the specified geographic coordinate.
*
* @param longitude The geodetic longitude, in decimal degrees.
* @param latitude The geodetic latitude, in decimal degrees.
* @param height The height above the ellipsoid in metres.
* @return The value to add in order to get the height above the geoid (in metres).
* @throws TransformException if the offset can't be computed for the specified coordinates.
*/
protected abstract double heightOffset(double longitude, double latitude, double height)
throws TransformException;
/**
* Transforms a single coordinate point in a list of ordinal values,
* and optionally computes the derivative at that location.
*
* @throws TransformException If the point can't be transformed.
*
* @since 3.20 (derived from 3.00)
*/
@Override
public Matrix transform(final double[] srcPts, final int srcOff,
final double[] dstPts, final int dstOff,
final boolean derivate) throws TransformException
{
if (dstPts != null) {
final double x = srcPts[srcOff ];
final double y = srcPts[srcOff+1];
final double z = srcPts[srcOff+2];
dstPts[dstOff ] = x;
dstPts[dstOff+1] = y;
dstPts[dstOff+2] = z + heightOffset(x,y,z);
}
return derivate ? Matrices.createDiagonal(getTargetDimensions(), getSourceDimensions()) : null;
}
/**
* Transforms a list of coordinate point ordinal values.
*/
@Override
public void transform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)
throws TransformException
{
boolean reverse = false;
if (srcPts == dstPts) {
switch (IterationStrategy.suggest(srcOff, DIMENSION, dstOff, DIMENSION, numPts)) {
case ASCENDING: {
break;
}
case DESCENDING: {
final int offset = (numPts-1) * DIMENSION;
srcOff += offset;
dstOff += offset;
reverse = true;
break;
}
default: {
srcPts = Arrays.copyOfRange(srcPts, srcOff, srcOff + numPts*DIMENSION);
srcOff = 0;
break;
}
}
}
while (--numPts >= 0) {
final double x = srcPts[srcOff++];
final double y = srcPts[srcOff++];
final double z = srcPts[srcOff++];
dstPts[dstOff++] = x;
dstPts[dstOff++] = y;
dstPts[dstOff++] = z + heightOffset(x,y,z);
if (reverse) {
srcOff -= 2*DIMENSION;
dstOff -= 2*DIMENSION;
}
}
}
/**
* Transforms a list of coordinate point ordinal values.
*/
@Override
public void transform(float[] srcPts, int srcOff, float[] dstPts, int dstOff, int numPts)
throws TransformException
{
boolean reverse = false;
if (srcPts == dstPts) {
switch (IterationStrategy.suggest(srcOff, DIMENSION, dstOff, DIMENSION, numPts)) {
case ASCENDING: {
break;
}
case DESCENDING: {
final int offset = (numPts-1) * DIMENSION;
srcOff += offset;
dstOff += offset;
reverse = true;
break;
}
default: {
srcPts = Arrays.copyOfRange(srcPts, srcOff, srcOff + numPts*DIMENSION);
srcOff = 0;
break;
}
}
}
while (--numPts >= 0) {
final float x = srcPts[srcOff++];
final float y = srcPts[srcOff++];
final double z = srcPts[srcOff++];
dstPts[dstOff++] = x;
dstPts[dstOff++] = y;
dstPts[dstOff++] = (float) (z + heightOffset(x,y,z));
if (reverse) {
srcOff -= 2*DIMENSION;
dstOff -= 2*DIMENSION;
}
}
}
/**
* Transforms a list of coordinate point ordinal values.
*/
@Override
public void transform(float [] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts)
throws TransformException
{
while (--numPts >= 0) {
final double x,y,z;
dstPts[dstOff++] = (x = srcPts[srcOff++]);
dstPts[dstOff++] = (y = srcPts[srcOff++]);
dstPts[dstOff++] = ((z = srcPts[srcOff++]) + heightOffset(x,y,z));
}
}
/**
* Transforms a list of coordinate point ordinal values.
*/
@Override
public void transform(double[] srcPts, int srcOff, float [] dstPts, int dstOff, int numPts)
throws TransformException
{
while (--numPts >= 0) {
final double x,y,z;
dstPts[dstOff++] = (float) (x = srcPts[srcOff++]);
dstPts[dstOff++] = (float) (y = srcPts[srcOff++]);
dstPts[dstOff++] = (float) ((z = srcPts[srcOff++]) + heightOffset(x,y,z));
}
}
}