You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2015/12/07 10:52:59 UTC
svn commit: r1718271 [1/2] - in /sis/branches/JDK8/core:
sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/
sis-referencing/src/main/java/org/apache/sis/referencing/datum/
sis-referencing/src/main/java/org/apache/sis/referencin...
Author: desruisseaux
Date: Mon Dec 7 09:52:59 2015
New Revision: 1718271
URL: http://svn.apache.org/viewvc?rev=1718271&view=rev
Log:
Redesign DatumShiftGrid API in an attempt to make it safer:
- Unit of measurement are explicitely declared.
- Input units are no longer required to be radians.
- Separated the interpolation method in two methods:
1) one expecting "real world" coordinate (only a convenience method which delegate to the method below),
2) one expecting a coordinate in grid units. This is the method really used by InterpolatedGeocentricTransform.
Modified:
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransform.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java
sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolationTest.java
sis/branches/JDK8/core/sis-referencing/src/test/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransformTest.java
sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/measure/Units.java
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridCompressed.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -17,6 +17,7 @@
package org.apache.sis.internal.referencing.provider;
import java.util.Arrays;
+import javax.measure.quantity.Quantity;
import org.apache.sis.math.DecimalFunctions;
@@ -26,18 +27,28 @@ import org.apache.sis.math.DecimalFuncti
* increase the precision in the common case where the shifts are specified with no more than
* 5 digits in base 10 in ASCII files.
*
+ * @param <C> Dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
+ * @param <T> Dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
+ * or {@link javax.measure.quantity.Length}).
+ *
* @author Martin Desruisseaux (Geomatys)
* @since 0.7
* @version 0.7
* @module
*/
-final class DatumShiftGridCompressed extends DatumShiftGridFile {
+final class DatumShiftGridCompressed<C extends Quantity, T extends Quantity> extends DatumShiftGridFile<C,T> {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 4847888093457104917L;
/**
+ * Maximal grid index along the <var>y</var> axis.
+ * This is the number of grid cells minus 2.
+ */
+ private final int ymax;
+
+ /**
* An "average" value for the offset in each dimension.
*/
private final double[] averages;
@@ -56,10 +67,11 @@ final class DatumShiftGridCompressed ext
/**
* Creates a new datum shift grid for the same geometry than the given grid but different data.
*/
- private DatumShiftGridCompressed(final DatumShiftGridFile grid, final double[] averages,
+ private DatumShiftGridCompressed(final DatumShiftGridFile<C,T> grid, final double[] averages,
final short[][] data, final double scale)
{
super(grid);
+ this.ymax = getGridSize()[1] - 2;
this.averages = averages;
this.data = data;
this.scale = scale;
@@ -74,7 +86,9 @@ final class DatumShiftGridCompressed ext
* @param scale The factor by which to multiply each compressed value before to add to the average value.
* @return The grid to use (may or may not be compressed).
*/
- static DatumShiftGridFile compress(final DatumShiftGridFile.Float grid, double[] averages, final double scale) {
+ static <C extends Quantity, T extends Quantity> DatumShiftGridFile<C,T> compress(
+ final DatumShiftGridFile.Float<C,T> grid, double[] averages, final double scale)
+ {
final short[][] data = new short[grid.offsets.length][];
final boolean computeAverages = (averages == null);
if (computeAverages) {
@@ -83,7 +97,7 @@ final class DatumShiftGridCompressed ext
for (int dim = 0; dim < data.length; dim++) {
final double average;
if (computeAverages) {
- average = Math.rint(grid.getAverageOffset(dim) / scale);
+ average = Math.rint(grid.getCellMean(dim) / scale);
averages[dim] = average * scale;
} else {
average = averages[dim] / scale;
@@ -102,15 +116,15 @@ final class DatumShiftGridCompressed ext
}
data[dim] = compressed;
}
- return new DatumShiftGridCompressed(grid, averages, data, scale);
+ return new DatumShiftGridCompressed<>(grid, averages, data, scale);
}
/**
* Returns a new grid with the same geometry than this grid but different data arrays.
*/
@Override
- final DatumShiftGridFile setData(final Object[] other) {
- return new DatumShiftGridCompressed(this, averages, (short[][]) other, scale);
+ final DatumShiftGridFile<C,T> setData(final Object[] other) {
+ return new DatumShiftGridCompressed<>(this, averages, (short[][]) other, scale);
}
/**
@@ -126,7 +140,7 @@ final class DatumShiftGridCompressed ext
* Returns the number of shift dimensions.
*/
@Override
- public final int getShiftDimensions() {
+ public final int getTranslationDimensions() {
return data.length;
}
@@ -137,7 +151,7 @@ final class DatumShiftGridCompressed ext
* @return A value close to the average for the given dimension.
*/
@Override
- public double getAverageOffset(final int dim) {
+ public double getCellMean(final int dim) {
return averages[dim];
}
@@ -145,7 +159,7 @@ final class DatumShiftGridCompressed ext
* Returns the cell value at the given grid index.
*/
@Override
- protected double getCellValue(final int dim, final int gridX, final int gridY) {
+ public double getCellValue(final int dim, final int gridX, final int gridY) {
return data[dim][gridX + gridY*nx] * scale + averages[dim];
}
@@ -154,20 +168,32 @@ final class DatumShiftGridCompressed ext
* reduce the number of arithmetic operations for efficiency reasons.
*/
@Override
- public void offsetAt(double x, double y, double[] offsets) {
- final int gridX = Math.max(0, Math.min(nx - 2, (int) Math.floor(x = (x - x0) * scaleX)));
- final int gridY = Math.max(0, Math.min(ny - 2, (int) Math.floor(y = (y - y0) * scaleY)));
- x -= gridX;
- y -= gridY;
- final int p0 = nx*gridY + gridX;
+ public void interpolateInCell(double gridX, double gridY, double[] vector) {
+ int ix = (int) gridX; gridX -= ix;
+ int iy = (int) gridY; gridY -= iy;
+ if (ix < 0) {
+ ix = 0;
+ gridX = -1;
+ } else if (ix >= nx - 1) {
+ ix = nx - 2;
+ gridX = +1;
+ }
+ if (iy < 0) {
+ iy = 0;
+ gridY = -1;
+ } else if (iy > ymax) { // Subtraction of 2 already done by the constructor.
+ iy = ymax;
+ gridY = +1;
+ }
+ final int p0 = nx*iy + ix;
final int p1 = nx + p0;
for (int dim = 0; dim < data.length; dim++) {
final short[] values = data[dim];
double r0 = values[p0];
double r1 = values[p1];
- r0 += x * (values[p0+1] - r0);
- r1 += x * (values[p1+1] - r1);
- offsets[dim] = (y * (r1 - r0) + r0) * scale + averages[dim];
+ r0 += gridX * (values[p0+1] - r0);
+ r1 += gridX * (values[p1+1] - r1);
+ vector[dim] = (gridY * (r1 - r0) + r0) * scale + averages[dim];
}
}
@@ -181,7 +207,7 @@ final class DatumShiftGridCompressed ext
@Override
public boolean equals(final Object other) {
if (super.equals(other)) {
- final DatumShiftGridCompressed that = (DatumShiftGridCompressed) other;
+ final DatumShiftGridCompressed<?,?> that = (DatumShiftGridCompressed<?,?>) other;
return Double.doubleToLongBits(scale) == Double.doubleToLongBits(that.scale)
&& Arrays.equals(averages, that.averages);
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/DatumShiftGridFile.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -18,9 +18,12 @@ package org.apache.sis.internal.referenc
import java.util.Arrays;
import java.lang.reflect.Array;
+import javax.measure.unit.Unit;
+import javax.measure.quantity.Quantity;
import org.apache.sis.math.DecimalFunctions;
import org.apache.sis.util.collection.Cache;
import org.apache.sis.referencing.datum.DatumShiftGrid;
+import org.apache.sis.internal.referencing.j2d.AffineTransform2D;
// Branch-specific imports
import java.nio.file.Path;
@@ -39,12 +42,16 @@ import java.nio.file.Path;
* from {@code float} to {@code double} performed by the {@link #getCellValue(int, int, int)} method.</li>
* </ul>
*
+ * @param <C> Dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
+ * @param <T> Dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
+ * or {@link javax.measure.quantity.Length}).
+ *
* @author Martin Desruisseaux (Geomatys)
* @since 0.7
* @version 0.7
* @module
*/
-public abstract class DatumShiftGridFile extends DatumShiftGrid {
+public abstract class DatumShiftGridFile<C extends Quantity, T extends Quantity> extends DatumShiftGrid<C,T> {
/**
* Serial number for inter-operability with different versions.
*/
@@ -55,8 +62,8 @@ public abstract class DatumShiftGridFile
* data exceed 32768 (about 128 kilobytes if the values use the {@code float} type). in which case
* the oldest grids will be replaced by weak references.
*/
- static final Cache<Path, DatumShiftGridFile> CACHE = new Cache<Path, DatumShiftGridFile>(4, 32*1024, true) {
- @Override protected int cost(final DatumShiftGridFile grid) {
+ static final Cache<Path, DatumShiftGridFile<?,?>> CACHE = new Cache<Path, DatumShiftGridFile<?,?>>(4, 32*1024, true) {
+ @Override protected int cost(final DatumShiftGridFile<?,?> grid) {
int p = 1;
for (final Object array : grid.getData()) {
p *= Array.getLength(array);
@@ -73,9 +80,13 @@ public abstract class DatumShiftGridFile
public final Path file;
/**
+ * Number of grid cells along the <var>x</var> axis.
+ */
+ final int nx;
+
+ /**
* Creates a new datum shift grid for the given grid geometry.
* The actual offset values need to be provided by subclasses.
- * All {@code double} values given to this constructor will be converted from degrees to radians.
*
* @param x0 Longitude in degrees of the center of the cell at grid index (0,0).
* @param y0 Latitude in degrees of the center of the cell at grid index (0,0).
@@ -84,17 +95,18 @@ public abstract class DatumShiftGridFile
* @param nx Number of cells along the <var>x</var> axis in the grid.
* @param ny Number of cells along the <var>y</var> axis in the grid.
*/
- DatumShiftGridFile(final double x0, final double y0,
+ DatumShiftGridFile(final Unit<C> coordinateUnit,
+ final Unit<T> translationUnit,
+ final boolean isCellValueRatio,
+ final double x0, final double y0,
final double Δx, final double Δy,
final int nx, final int ny,
final Path file)
{
- super(Math.toRadians(x0),
- Math.toRadians(y0),
- Math.toRadians(Δx),
- Math.toRadians(Δy),
- nx, ny);
+ super(coordinateUnit, new AffineTransform2D(1/Δx, 0, 0, 1/Δy, -x0/Δx, -y0/Δy),
+ new int[] {nx, ny}, isCellValueRatio, translationUnit);
this.file = file;
+ this.nx = nx;
}
/**
@@ -102,18 +114,19 @@ public abstract class DatumShiftGridFile
*
* @param other The other datum shift grid from which to copy the grid geometry.
*/
- DatumShiftGridFile(final DatumShiftGridFile other) {
+ DatumShiftGridFile(final DatumShiftGridFile<C,T> other) {
super(other);
this.file = other.file;
+ this.nx = other.nx;
}
/**
* If a grid exists in the cache for the same data, returns a new grid sharing the same data arrays.
* Otherwise returns {@code this}.
*/
- final DatumShiftGridFile useSharedData() {
+ final DatumShiftGridFile<C,T> useSharedData() {
final Object[] data = getData();
- for (final DatumShiftGridFile grid : CACHE.values()) {
+ for (final DatumShiftGridFile<?,?> grid : CACHE.values()) {
final Object[] other = grid.getData();
if (Arrays.deepEquals(data, other)) {
return setData(other);
@@ -129,7 +142,7 @@ public abstract class DatumShiftGridFile
* reference the same grid (e.g. symbolic link, lower case versus upper case in a case-insensitive file
* system).
*/
- abstract DatumShiftGridFile setData(Object[] other);
+ abstract DatumShiftGridFile<C,T> setData(Object[] other);
/**
* Returns the data for each shift dimensions.
@@ -137,6 +150,18 @@ public abstract class DatumShiftGridFile
abstract Object[] getData();
/**
+ * Returns {@code this} casted to the given type, after verification that those types are valid.
+ */
+ @SuppressWarnings("unchecked")
+ final <NC extends Quantity, NT extends Quantity> DatumShiftGridFile<NC,NT> castTo(
+ final Class<NC> coordinateType, final Class<NT> translationType)
+ {
+ super.getCoordinateUnit() .asType(coordinateType);
+ super.getTranslationUnit().asType(translationType);
+ return (DatumShiftGridFile<NC,NT>) this;
+ }
+
+ /**
* Returns {@code true} if the given object is a grid containing the same data than this grid.
*
* @param other The other object to compare with this datum shift grid.
@@ -149,7 +174,7 @@ public abstract class DatumShiftGridFile
return true;
}
if (super.equals(other)) {
- final DatumShiftGridFile that = (DatumShiftGridFile) other;
+ final DatumShiftGridFile<?,?> that = (DatumShiftGridFile<?,?>) other;
return file.equals(that.file) && Arrays.deepEquals(getData(), that.getData());
}
return false;
@@ -186,7 +211,7 @@ public abstract class DatumShiftGridFile
* @version 0.7
* @module
*/
- static final class Float extends DatumShiftGridFile {
+ static final class Float<C extends Quantity, T extends Quantity> extends DatumShiftGridFile<C,T> {
/**
* Serial number for inter-operability with different versions.
*/
@@ -201,12 +226,15 @@ public abstract class DatumShiftGridFile
* Creates a new datum shift grid with the given grid geometry, filename and number of shift dimensions.
* All {@code double} values given to this constructor will be converted from degrees to radians.
*/
- Float(final double x0, final double y0,
+ Float(final Unit<C> coordinateUnit,
+ final Unit<T> translationUnit,
+ final boolean isCellValueRatio,
+ final double x0, final double y0,
final double Δx, final double Δy,
final int nx, final int ny,
final Path file, final int dim)
{
- super(x0, y0, Δx, Δy, nx, ny, file);
+ super(coordinateUnit, translationUnit, isCellValueRatio, x0, y0, Δx, Δy, nx, ny, file);
offsets = new float[dim][];
final int size = Math.multiplyExact(nx, ny);
for (int i=0; i<dim; i++) {
@@ -217,7 +245,7 @@ public abstract class DatumShiftGridFile
/**
* Creates a new grid of the same geometry than the given grid but using a different data array.
*/
- private Float(final DatumShiftGridFile grid, final float[][] offsets) {
+ private Float(final DatumShiftGridFile<C,T> grid, final float[][] offsets) {
super(grid);
this.offsets = offsets;
}
@@ -226,8 +254,8 @@ public abstract class DatumShiftGridFile
* Returns a new grid with the same geometry than this grid but different data arrays.
*/
@Override
- final DatumShiftGridFile setData(final Object[] other) {
- return new Float(this, (float[][]) other);
+ final DatumShiftGridFile<C,T> setData(final Object[] other) {
+ return new Float<>(this, (float[][]) other);
}
/**
@@ -243,7 +271,7 @@ public abstract class DatumShiftGridFile
* Returns the number of shift dimension.
*/
@Override
- public final int getShiftDimensions() {
+ public final int getTranslationDimensions() {
return offsets.length;
}
@@ -259,7 +287,7 @@ public abstract class DatumShiftGridFile
* @return The offset at the given dimension in the grid cell at the given index.
*/
@Override
- protected final double getCellValue(final int dim, final int gridX, final int gridY) {
+ public final double getCellValue(final int dim, final int gridX, final int gridY) {
return DecimalFunctions.floatToDouble(offsets[dim][gridX + gridY*nx]);
}
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/FranceGeocentricInterpolation.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -27,6 +27,9 @@ import java.io.EOFException;
import java.io.IOException;
import javax.xml.bind.annotation.XmlTransient;
import javax.measure.unit.SI;
+import javax.measure.unit.NonSI;
+import javax.measure.quantity.Angle;
+import javax.measure.quantity.Length;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
@@ -294,7 +297,7 @@ public final class FranceGeocentricInter
default: throw new InvalidParameterValueException(Errors.format(Errors.Keys.IllegalArgumentValue_2, "dim", dim), "dim", dim);
}
final Path file = pg.getValue(FILE);
- final DatumShiftGridFile grid = getOrLoad(file, !isRecognized(file) ? null : new double[] {TX, TY, TZ}, PRECISION);
+ final DatumShiftGridFile<Angle,Length> grid = getOrLoad(file, !isRecognized(file) ? null : new double[] {TX, TY, TZ}, PRECISION);
MathTransform tr = InterpolatedGeocentricTransform.createGeodeticTransformation(factory,
createEllipsoid(pg, Molodensky.TGT_SEMI_MAJOR,
Molodensky.TGT_SEMI_MINOR, CommonCRS.ETRS89.ellipsoid()), withHeights, // GRS 1980 ellipsoid
@@ -317,17 +320,17 @@ public final class FranceGeocentricInter
* @param averages An "average" value for the offset in each dimension, or {@code null} if unknown.
* @param scale The factor by which to multiply each compressed value before to add to the average value.
*/
- static DatumShiftGridFile getOrLoad(final Path file, final double[] averages, final double scale)
+ static DatumShiftGridFile<Angle,Length> getOrLoad(final Path file, final double[] averages, final double scale)
throws FactoryException
{
- DatumShiftGridFile grid = DatumShiftGridFile.CACHE.peek(file);
+ DatumShiftGridFile<?,?> grid = DatumShiftGridFile.CACHE.peek(file);
if (grid == null) {
- final Cache.Handler<DatumShiftGridFile> handler = DatumShiftGridFile.CACHE.lock(file);
+ final Cache.Handler<DatumShiftGridFile<?,?>> handler = DatumShiftGridFile.CACHE.lock(file);
try {
grid = handler.peek();
if (grid == null) {
try (final BufferedReader in = Files.newBufferedReader(file)) {
- final DatumShiftGridFile.Float g = load(in, file);
+ final DatumShiftGridFile.Float<Angle,Length> g = load(in, file);
grid = DatumShiftGridCompressed.compress(g, averages, scale);
} catch (IOException | RuntimeException e) {
// NumberFormatException, ArithmeticException, NoSuchElementException, possibly other.
@@ -339,7 +342,7 @@ public final class FranceGeocentricInter
handler.putAndUnlock(grid);
}
}
- return grid;
+ return grid.castTo(Angle.class, Length.class);
}
/**
@@ -353,10 +356,10 @@ public final class FranceGeocentricInter
* @throws FactoryException if an problem is found with the file content.
* @throws ArithmeticException if the width or the height exceed the integer capacity.
*/
- static DatumShiftGridFile.Float load(final BufferedReader in, final Path file)
+ static DatumShiftGridFile.Float<Angle,Length> load(final BufferedReader in, final Path file)
throws IOException, FactoryException
{
- DatumShiftGridFile.Float grid = null;
+ DatumShiftGridFile.Float<Angle,Length> grid = null;
double x0 = 0;
double xf = 0;
double y0 = 0;
@@ -409,7 +412,9 @@ public final class FranceGeocentricInter
Δy = gridGeometry[5];
nx = Math.toIntExact(Math.round((xf - x0) / Δx + 1));
ny = Math.toIntExact(Math.round((yf - y0) / Δy + 1));
- grid = new DatumShiftGridFile.Float(x0, y0, Δx, Δy, nx, ny, file, 3);
+ grid = new DatumShiftGridFile.Float<>(
+ NonSI.DEGREE_ANGLE, SI.METRE, false,
+ x0, y0, Δx, Δy, nx, ny, file, 3);
}
break;
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/datum/DatumShiftGrid.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -16,13 +16,29 @@
*/
package org.apache.sis.referencing.datum;
+import java.util.Arrays;
+import java.io.IOException;
import java.io.Serializable;
+import java.io.ObjectInputStream;
+import javax.measure.unit.Unit;
+import javax.measure.quantity.Quantity;
+import javax.measure.converter.UnitConverter;
+import javax.measure.converter.LinearConverter;
import org.opengis.geometry.Envelope;
-import org.apache.sis.geometry.Envelope2D;
+import org.opengis.geometry.MismatchedDimensionException;
+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.LinearTransform;
+import org.apache.sis.geometry.GeneralEnvelope;
+import org.apache.sis.geometry.Envelopes;
import org.apache.sis.internal.util.DoubleDouble;
-import org.apache.sis.internal.util.Numerics;
+import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
+// Branch-dependent imports
+import java.util.Objects;
+
/**
* Small but non-constant translations to apply on coordinates for datum shifts or other transformation process.
@@ -30,29 +46,44 @@ import org.apache.sis.util.ArgumentCheck
* like NTv2, NADCON or RGF93. But this class could also be used for other kind of transformations,
* provided that the shifts are <strong>small</strong> (otherwise algorithms may not converge).
*
- * <p>{@linkplain DefaultGeodeticDatum Geodetic datum} changes impact directly two kinds of coordinates:
- * geographic and geocentric. Translations given by {@code DatumShiftGrid} instances are often, but not always,
+ * <p>{@linkplain DefaultGeodeticDatum Geodetic datum} changes can be implemented by translations in geographic
+ * or geocentric coordinates. Translations given by {@code DatumShiftGrid} instances are often, but not always,
* applied directly on geographic coordinates (<var>λ</var>,<var>φ</var>). But some algorithms rather apply the
* translations in geocentric coordinates (<var>X</var>,<var>Y</var>,<var>Z</var>). This {@code DatumShiftGrid}
* class can describe both cases, but will be used with different {@code MathTransform} implementations.</p>
*
+ * <p>Steps for calculation of a translation vector:</p>
+ * <ol>
+ * <li>Coordinates are given in some "real world" unit.
+ * The expected unit is given by {@link #getCoordinateUnit()}.</li>
+ * <li>Above coordinates are converted to grid indices including fractional parts.
+ * That conversion is given by {@link #getCoordinateToGrid()}.</li>
+ * <li>Translation vectors are interpolated at the position of above grid indices.
+ * That interpolation is done by {@link #interpolateInCell interpolateInCell(…)}.</li>
+ * <li>If the above translations were given as a ratio of the real translation divided by the size of grid cells, apply
+ * the inverse of the conversion given at step 2. This information is given by {@link #isCellValueRatio()}.</li>
+ * <li>The resulting translation vectors are in the unit given by {@link #getTranslationUnit()}.</li>
+ * </ol>
+ *
+ * The {@link #interpolateAt interpolateAt(…)} method performs all those steps.
+ * But that method is provided only for convenience; it is not used by Apache SIS.
+ * For performance reasons SIS {@code MathTransform} implementations perform all the above-cited steps themselves,
+ * and apply the interpolated translations on coordinate values in their own step between above steps 3 and 4.
+ *
* <div class="note"><b>Use cases:</b>
- * NADCON, NTv2 and other grids are used in the contexts described below. But be aware of units of measurement!
- * In particular, input geographic coordinates (λ,φ) need to be in <strong>radians</strong> for use with SIS
- * {@link org.apache.sis.referencing.operation.transform.InterpolatedGeocentricTransform} implementation.
* <ul>
* <li><p><b>Datum shift by geographic translations</b><br>
- * NADCON and NTv2 grids are defined with longitude (λ) and latitude (φ) inputs in angular <em>degrees</em>
- * and give (<var>Δλ</var>, <var>Δφ</var>) translations in angular <em>seconds</em>.
- * However Apache SIS needs all those values to be converted to <strong>radians</strong>.
+ * NADCON and NTv2 grids are defined with longitude (<var>λ</var>) and latitude (<var>φ</var>) inputs in angular
+ * <em>degrees</em> and give (<var>Δλ</var>, <var>Δφ</var>) translations in angular <em>seconds</em>.
+ * However SIS stores the translation values in units of grid cell rather than angular seconds.
* The translations will be applied by {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform}
* directly on the given (<var>λ</var>,<var>φ</var>) coordinates.
* </p></li>
*
* <li><p><b>Datum shift by geocentric translations</b><br>
- * France interpolation grid is defined with longitude (λ) and latitude (φ) inputs in angular <em>degrees</em>
- * and gives (<var>ΔX</var>, <var>ΔY</var>, <var>ΔZ</var>) geocentric translations in <em>metres</em>.
- * Those offsets will not be added directly to the given (<var>λ</var>,<var>φ</var>) coordinates since there is
+ * France interpolation grid is defined with longitude (<var>λ</var>) and latitude (<var>φ</var>) inputs in angular
+ * <em>degrees</em> and gives (<var>ΔX</var>, <var>ΔY</var>, <var>ΔZ</var>) geocentric translations in <em>metres</em>.
+ * Those translations will not be added directly to the given (<var>λ</var>,<var>φ</var>) coordinates since there is
* a geographic/geocentric conversion in the middle
* (see {@link org.apache.sis.referencing.operation.transform.InterpolatedGeocentricTransform}).
* </p></li>
@@ -68,119 +99,419 @@ import org.apache.sis.util.ArgumentCheck
* </p></li>
* </ul></div>
*
- * <p>Implementations of this class shall be immutable and thread-safe.</p>
+ * Implementations of this class shall be immutable and thread-safe.
+ *
+ * <div class="section">Serialization</div>
+ * Serialized objects of this class are not guaranteed to be compatible with future Apache SIS releases.
+ * Serialization support is appropriate for short term storage or RMI between applications running the
+ * same version of Apache SIS. But for long term storage, an established datum shift grid format like
+ * NTv2 should be preferred.
+ *
+ * @param <C> Dimension of the coordinate unit (usually {@link javax.measure.quantity.Angle}).
+ * @param <T> Dimension of the translation unit (usually {@link javax.measure.quantity.Angle}
+ * or {@link javax.measure.quantity.Length}).
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.7
* @version 0.7
* @module
*/
-public abstract class DatumShiftGrid implements Serializable {
+public abstract class DatumShiftGrid<C extends Quantity, T extends Quantity> implements Serializable {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 8405276545243175808L;
/**
- * Coordinate of the center of the cell at grid index (0,0).
- * This is usually a geographic coordinate (<var>λ₀</var>,<var>φ₀</var>) in <strong>radians</strong>,
- * but other usages are allowed for users who instantiate
- * {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform} themselves.
- * The (<var>x₀</var>, <var>y₀</var>) coordinate is often the minimal (<var>x</var>,<var>y</var>) value
- * of the grid, but not necessarily if the <var>Δx</var> or <var>Δy</var> increments are negative.
+ * The unit of measurements of input values, before conversion to grid indices by {@link #coordinateToGrid}.
+ * The coordinate unit is typically {@link javax.measure.unit.NonSI#DEGREE_ANGLE}.
+ *
+ * @see #getCoordinateUnit()
*/
- protected final double x0, y0;
+ private final Unit<C> coordinateUnit;
/**
- * Multiplication factor for converting a coordinate into grid index.
- * The (<var>gridX</var>, <var>gridY</var>) indices of a cell in the grid are given by:
+ * Conversion from the "real world" coordinates to grid indices including fractional parts.
+ * This is the conversion that needs to be applied before to interpolate.
*
- * <ul>
- * <li><var>gridX</var> = (<var>x</var> - <var>x₀</var>) ⋅ {@code scaleX}</li>
- * <li><var>gridY</var> = (<var>y</var> - <var>y₀</var>) ⋅ {@code scaleY}</li>
- * </ul>
+ * @see #getCoordinateToGrid()
*/
- protected final double scaleX, scaleY;
+ private final LinearTransform coordinateToGrid;
/**
- * Number of cells in the grid along the <var>x</var> and <var>y</var> axes.
+ * The unit of measurement of output values, as interpolated by the {@link #interpolateAt} method.
+ *
+ * @see #getTranslationUnit()
*/
- protected final int nx, ny;
+ private final Unit<T> translationUnit;
/**
- * Creates a new datum shift grid for the given grid geometry.
- * The actual offset values need to be provided by subclasses.
+ * {@code true} if the translation interpolated by {@link #interpolateInCell interpolateInCell(…)}
+ * are divided by grid cell size. If {@code true}, then the inverse of {@link #coordinateToGrid}
+ * needs to be applied on the interpolated values as a delta transform.
+ * Such conversion is applied (if needed) by the {@link #interpolateAt} method.
*
- * @param x0 First ordinate (often longitude in radians) of the center of the cell at grid index (0,0).
- * @param y0 Second ordinate (often latitude in radians) of the center of the cell at grid index (0,0).
- * @param Δx Increment in <var>x</var> value between cells at index <var>gridX</var> and <var>gridX</var> + 1.
- * @param Δy Increment in <var>y</var> value between cells at index <var>gridY</var> and <var>gridY</var> + 1.
- * @param nx Number of cells along the <var>x</var> axis in the grid.
- * @param ny Number of cells along the <var>y</var> axis in the grid.
+ * @see #isCellValueRatio()
+ */
+ private final boolean isCellValueRatio;
+
+ /**
+ * Number of grid cells in along each dimension. This is usually an array of length 2 containing
+ * the number of grid cells along the <var>x</var> and <var>y</var> axes.
*/
- protected DatumShiftGrid(final double x0, final double y0,
- final double Δx, final double Δy,
- final int nx, final int ny)
+ private final int[] gridSize;
+
+ /**
+ * Conversion from (λ,φ) coordinates in radians to grid indices (x,y).
+ *
+ * <ul>
+ * <li>x = (λ - λ₀) ⋅ {@code scaleX} = λ ⋅ {@code scaleX} + x₀</li>
+ * <li>y = (φ - φ₀) ⋅ {@code scaleY} = φ ⋅ {@code scaleY} + y₀</li>
+ * </ul>
+ *
+ * Those factors are extracted from the {@link #coordinateToGrid} transform for performance purposes.
+ */
+ private transient double scaleX, scaleY, x0, y0;
+
+ /**
+ * Creates a new datum shift grid for the given size and units.
+ * The actual cell values need to be provided by subclasses.
+ *
+ * <p>Meaning of argument values is documented more extensively in {@link #getCoordinateUnit()},
+ * {@link #getCoordinateToGrid()}, {@link #isCellValueRatio()} and {@link #getTranslationUnit()}
+ * methods. The argument order is roughly the order in which they are used in the process of
+ * interpolating translation vectors.</p>
+ *
+ * @param coordinateUnit The unit of measurement of input values, before conversion to grid indices by {@code coordinateToGrid}.
+ * @param coordinateToGrid Conversion from the "real world" coordinates to grid indices including fractional parts.
+ * @param gridSize Number of cells along each axis in the grid. The length of this array shall be equal to {@code coordinateToGrid} target dimensions.
+ * @param isCellValueRatio {@code true} if results of {@link #interpolateInCell interpolateInCell(…)} are divided by grid cell size.
+ * @param translationUnit The unit of measurement of output values.
+ */
+ protected DatumShiftGrid(final Unit<C> coordinateUnit, final LinearTransform coordinateToGrid,
+ int[] gridSize, final boolean isCellValueRatio, final Unit<T> translationUnit)
{
- this.x0 = x0;
- this.y0 = y0;
- this.nx = nx;
- this.ny = ny;
- ArgumentChecks.ensureStrictlyPositive("nx", nx);
- ArgumentChecks.ensureStrictlyPositive("ny", ny);
- scaleX = 1 / Δx;
- scaleY = 1 / Δy;
+ ArgumentChecks.ensureNonNull("coordinateUnit", coordinateUnit);
+ ArgumentChecks.ensureNonNull("coordinateToGrid", coordinateToGrid);
+ ArgumentChecks.ensureNonNull("gridSize", gridSize);
+ ArgumentChecks.ensureNonNull("translationUnit", translationUnit);
+ int n = coordinateToGrid.getTargetDimensions();
+ if (n != gridSize.length) {
+ throw new MismatchedDimensionException(Errors.format(
+ Errors.Keys.MismatchedDimension_3, "gridSize", n, gridSize.length));
+ }
+ this.coordinateUnit = coordinateUnit;
+ this.coordinateToGrid = coordinateToGrid;
+ this.isCellValueRatio = isCellValueRatio;
+ this.translationUnit = translationUnit;
+ this.gridSize = gridSize = gridSize.clone();
+ for (int i=0; i<gridSize.length; i++) {
+ if ((n = gridSize[i]) < 2) {
+ throw new IllegalArgumentException(Errors.format(n <= 0
+ ? Errors.Keys.ValueNotGreaterThanZero_2
+ : Errors.Keys.IllegalArgumentValue_2, "gridSize[" + i + ']', n));
+ }
+ }
+ computeConversionFactors();
}
/**
- * Creates a new datum shift grid with the same grid geometry than the given grid.
+ * Computes the conversion factors needed by {@link #interpolateAtNormalized(double, double, double[])}.
+ * This method takes only the 2 first dimensions. If a conversion factor can not be computed, then it is
+ * set to NaN.
+ */
+ @SuppressWarnings("fallthrough")
+ private void computeConversionFactors() {
+ scaleX = Double.NaN;
+ scaleY = Double.NaN;
+ x0 = Double.NaN;
+ y0 = Double.NaN;
+ final UnitConverter c = coordinateUnit.toSI().getConverterTo(coordinateUnit);
+ if (c instanceof LinearConverter && c.convert(0) == 0) {
+ final Matrix m = coordinateToGrid.getMatrix();
+ if (Matrices.isAffine(m)) {
+ final int n = m.getNumCol() - 1;
+ final double toUnit = c.convert(1);
+ switch (m.getNumRow()) {
+ default: y0 = m.getElement(1,n); scaleY = diagonal(m, 1, n) * toUnit; // Fall through
+ case 1: x0 = m.getElement(0,n); scaleX = diagonal(m, 0, n) * toUnit;
+ case 0: break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the value on the diagonal of the given matrix, provided that all other non-translation terms are 0.
+ *
+ * @param m The matrix from which to get the scale factor on a row.
+ * @param j The row for which to get the scale factor.
+ * @param n Index of the last column.
+ * @return The scale factor on the diagonal, or NaN.
+ */
+ private static double diagonal(final Matrix m, final int j, int n) {
+ while (--n >= 0) {
+ if (j != n && m.getElement(j, n) != 0) {
+ return Double.NaN;
+ }
+ }
+ return m.getElement(j, j);
+ }
+
+ /**
+ * Creates a new datum shift grid with the same grid geometry (size and units) than the given grid.
*
* @param other The other datum shift grid from which to copy the grid geometry.
*/
- protected DatumShiftGrid(final DatumShiftGrid other) {
- x0 = other.x0;
- y0 = other.y0;
- nx = other.nx;
- ny = other.ny;
- scaleX = other.scaleX;
- scaleY = other.scaleY;
+ protected DatumShiftGrid(final DatumShiftGrid<C,T> other) {
+ ArgumentChecks.ensureNonNull("other", other);
+ coordinateUnit = other.coordinateUnit;
+ coordinateToGrid = other.coordinateToGrid;
+ isCellValueRatio = other.isCellValueRatio;
+ translationUnit = other.translationUnit;
+ gridSize = other.gridSize;
+ scaleX = other.scaleX;
+ scaleY = other.scaleY;
+ x0 = other.x0;
+ y0 = other.y0;
+ }
+
+ /**
+ * Returns the domain of validity of input coordinates that can be specified to the
+ * {@link #interpolateAt interpolateAt(…)} method. Coordinates outside that domain of
+ * validity will still be accepted, but the extrapolated results may be very wrong.
+ *
+ * <p>The unit of measurement for the coordinate values in the returned envelope is
+ * given by {@link #getCoordinateUnit()}. The complete CRS is undefined.</p>
+ *
+ * @return The domain covered by this grid.
+ * @throws TransformException if an error occurred while computing the envelope.
+ */
+ public Envelope getDomainOfValidity() throws TransformException {
+ final GeneralEnvelope env = new GeneralEnvelope(gridSize.length);
+ for (int i=0; i<gridSize.length; i++) {
+ env.setRange(i, -0.5, gridSize[i] - 0.5);
+ }
+ return Envelopes.transform(getCoordinateToGrid().inverse(), env);
+ }
+
+ /**
+ * Returns the unit of measurement of input values, before conversion to grid indices.
+ * The coordinate unit is usually {@link javax.measure.unit.NonSI#DEGREE_ANGLE}, but other units are allowed.
+ *
+ * @return The unit of measurement of input values before conversion to grid indices.
+ *
+ * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation#getInterpolationCRS()
+ */
+ public Unit<C> getCoordinateUnit() {
+ return coordinateUnit;
+ }
+
+ /**
+ * Returns the number of cells along each axis in the grid. The length of this array is equal to
+ * {@code coordinateToGrid} target dimensions.
+ *
+ * @return The number of cells along each axis in the grid.
+ */
+ public int[] getGridSize() {
+ return gridSize.clone();
+ }
+
+ /**
+ * Conversion from the "real world" coordinates to grid indices including fractional parts.
+ * The input points given to the {@code MathTransform} shall be in the unit of measurement
+ * given by {@link #getCoordinateUnit()}.
+ * The output points are grid indices with integer values in the center of grid cells.
+ *
+ * <p>This transform is usually two-dimensional and linear, in which case conversions from (<var>x</var>,<var>y</var>)
+ * coordinates to ({@code gridX}, {@code gridY}) indices can be done with the following formulas:</p>
+ * <ul>
+ * <li><var>gridX</var> = (<var>x</var> - <var>x₀</var>) / <var>Δx</var></li>
+ * <li><var>gridY</var> = (<var>y</var> - <var>y₀</var>) / <var>Δy</var></li>
+ * </ul>
+ *
+ * where:
+ * <ul>
+ * <li>(<var>x₀</var>, <var>y₀</var>) is the coordinate of the center of the cell at grid index (0,0).</li>
+ * <li><var>Δx</var> and <var>Δy</var> are the distances between two cells on the <var>x</var> and <var>y</var>
+ * axes respectively, in the unit of measurement given by {@link #getCoordinateUnit()}.</li>
+ * </ul>
+ *
+ * The {@code coordinateToGrid} transform for the above formulas can be represented by the following matrix:
+ *
+ * {@preformat math
+ * ┌ ┐
+ * │ 1/Δx 0 -x₀/Δx │
+ * │ 0 1/Δy -y₀/Δy │
+ * │ 0 0 1 │
+ * └ ┘
+ * }
+ *
+ * @return Conversion from the "real world" coordinates to grid indices including fractional parts.
+ */
+ public LinearTransform getCoordinateToGrid() {
+ return coordinateToGrid;
}
/**
* Returns the number of dimensions of the translation vectors interpolated by this datum shift grid.
- * The number of dimension is usually 2 or 3, but other values are allowed.
+ * This number of dimensions is not necessarily equals to the number of source or target dimensions
+ * of the "{@linkplain #getCoordinateToGrid() coordinate to grid}" transform.
+ * The number of translation dimensions is usually 2 or 3, but other values are allowed.
*
* @return Number of dimensions of translation vectors.
*/
- public abstract int getShiftDimensions();
+ public abstract int getTranslationDimensions();
/**
- * Returns the domain of validity of the (<var>x</var>,<var>y</var>) coordinate values that can be specified
- * to the {@link #offsetAt offsetAt(x, y, …)} method. Coordinates outside that domain of validity will still
- * be accepted, but the result of offset computation may be very wrong.
+ * Returns the unit of measurement of output values, as interpolated by the {@code interpolateAt(…)} method.
+ * Apache SIS {@code MathTransform} implementations restrict the translation units to the following values:
*
- * <p>In datum shift grids used by {@link org.apache.sis.referencing.operation.transform.InterpolatedGeocentricTransform},
- * the domain of validity is always expressed as longitudes and latitudes in <strong>radians</strong>.
- * The envelope is usually in radians for simpler (non-geocentric) interpolations too, for consistency reasons.</p>
+ * <ul>
+ * <li>For {@link org.apache.sis.referencing.operation.transform.InterpolatedTransform}, the translation
+ * unit shall be the same than the {@linkplain #getCoordinateUnit() coordinate unit}.</li>
+ * <li>For {@link org.apache.sis.referencing.operation.transform.InterpolatedGeocentricTransform},
+ * the translation unit shall be the same than the unit of source ellipsoid axis lengths.</li>
+ * </ul>
*
- * @return The domain covered by this grid.
+ * @return The unit of measurement of output values interpolated by {@code interpolateAt(…)}.
+ *
+ * @see #interpolateAt
+ */
+ public Unit<T> getTranslationUnit() {
+ return translationUnit;
+ }
+
+ /**
+ * Interpolates the translation to apply for the given coordinate.
+ * The input values are in the unit given by {@link #getCoordinateUnit()}.
+ * The output values are in the unit given by {@link #getTranslationUnit()}.
+ * The length of the returned array is given by {@link #getTranslationDimensions()}.
+ *
+ * <div class="section">Default implementation</div>
+ * The default implementation performs the following steps:
+ * <ol>
+ * <li>Convert the given coordinate into grid indices using the transform given by {@link #getCoordinateToGrid()}.</li>
+ * <li>Interpolate the translation vector at the above grid indices with a call to {@link #interpolateInCell}.</li>
+ * <li>If {@link #isCellValueRatio()} returns {@code true}, {@linkplain LinearTransform#deltaTransform delta transform}
+ * the translation vector by the inverse of the conversion given at step 1.</li>
+ * </ol>
+ *
+ * @param ordinates The "real world" ordinate (often longitude and latitude, but not necessarily)
+ * of the point for which to get the translation.
+ * @return The translation vector at the given position.
+ * @throws TransformException if an error occurred while computing the translation vector.
+ */
+ public double[] interpolateAt(final double... ordinates) throws TransformException {
+ final LinearTransform c = getCoordinateToGrid();
+ ArgumentChecks.ensureDimensionMatches("ordinates", c.getSourceDimensions(), ordinates);
+ final int dim = getTranslationDimensions();
+ double[] vector = new double[Math.max(dim, c.getTargetDimensions())];
+ c.transform(ordinates, 0, vector, 0, 1);
+ interpolateInCell(vector[0], vector[1], vector);
+ if (isCellValueRatio()) {
+ ((LinearTransform) c.inverse()).deltaTransform(vector, 0, vector, 0, 1);
+ }
+ if (vector.length != dim) {
+ vector = Arrays.copyOf(vector, dim);
+ }
+ return vector;
+ }
+
+ /**
+ * Interpolates the translation to apply for the given two-dimensional normalized coordinates.
+ * "Normalized coordinates" are coordinates in the unit of measurement given by {@link Unit#toSI()}.
+ * For angular coordinates, this is radians. For linear coordinates, this is metres.
+ *
+ * <p>The result is stored in the given {@code vector} array, which shall have a length of at least
+ * {@link #getTranslationDimensions()}. The output unit of measurement is the same than the one
+ * documented in {@link #getCellValue}.</p>
+ *
+ * @param x First "real world" ordinate (often longitude in radians) of the point for which to get the translation.
+ * @param y Second "real world" ordinate (often latitude in radians) of the point for which to get the translation.
+ * @param vector A pre-allocated array where to write the translation vector.
*/
- public Envelope getDomainOfValidity() {
- final Envelope2D domain = new Envelope2D(null, x0 - 0.5/scaleX, y0 - 0.5/scaleY, nx/scaleX, ny/scaleY);
- if (domain.width < 0) {
- domain.width = -domain.width;
- domain.x += domain.width;
+ public final void interpolateAtNormalized(final double x, final double y, final double[] vector) {
+ interpolateInCell(x * scaleX + x0, y * scaleY + y0, vector);
+ }
+
+ /**
+ * Interpolates the translation to apply for the given two-dimensional grid indices. The result is stored in
+ * the given {@code vector} array, which shall have a length of at least {@link #getTranslationDimensions()}.
+ * The output unit of measurement is the same than the one documented in {@link #getCellValue}.
+ *
+ * <div class="section">Default implementation</div>
+ * The default implementation performs the following steps for each dimension <var>dim</var>,
+ * where the number of dimension is determined by {@link #getTranslationDimensions()}.
+ *
+ * <ol>
+ * <li>Clamp the {@code gridX} index into the [0 … {@code gridSize[0]} - 2] range, inclusive.</li>
+ * <li>Clamp the {@code gridY} index into the [0 … {@code gridSize[1]} - 2] range, inclusive.</li>
+ * <li>Using {@link #getCellValue}, get the cell values around the given indices.</li>
+ * <li>Apply a bilinear interpolation and store the result in {@code vector[dim]}.</li>
+ * </ol>
+ *
+ * @param gridX First grid ordinate of the point for which to get the translation.
+ * @param gridY Second grid ordinate of the point for which to get the translation.
+ * @param vector A pre-allocated array where to write the translation vector.
+ */
+ public void interpolateInCell(double gridX, double gridY, final double[] vector) {
+ int ix = (int) gridX; gridX -= ix;
+ int iy = (int) gridY; gridY -= iy;
+ int n;
+ /*
+ * Because ((int) gridX) rounds toward 0, we know that (ix < 0) means that (gridX <= -1).
+ * With ix=0 we get (gridX-ix <= -1). So we set gridX = -1 for avoiding too far extrapolations.
+ * A similar reasoning apply to the gridX = +1 statement.
+ */
+ if (ix < 0) {
+ ix = 0;
+ gridX = -1;
+ } else if (ix > (n = gridSize[0] - 2)) {
+ ix = n;
+ gridX = +1;
}
- if (domain.height < 0) {
- domain.height = -domain.height;
- domain.y += domain.height;
+ if (iy < 0) {
+ iy = 0;
+ gridY = -1;
+ } else if (iy > (n = gridSize[1] - 2)) {
+ iy = n;
+ gridY = +1;
+ }
+ n = getTranslationDimensions();
+ for (int dim = 0; dim < n; dim++) {
+ double r0 = getCellValue(dim, ix, iy );
+ double r1 = getCellValue(dim, ix, iy+1);
+ r0 += gridX * (getCellValue(dim, ix+1, iy ) - r0);
+ r1 += gridX * (getCellValue(dim, ix+1, iy+1) - r1);
+ vector[dim] = gridY * (r1 - r0) + r0;
}
- return domain;
}
/**
- * Returns an average offset value for the given dimension.
- * Those average values shall provide a good "first guess" before to interpolate the actual offset value
+ * Returns the translation stored at the given grid indices for the given dimension.
+ * The returned value is considered representative of the value in the center of the grid cell.
+ * The output unit of measurement depends on the {@link #isCellValueRatio()} boolean:
+ *
+ * <ul>
+ * <li>If {@code false}, the value returned by this method shall be in the unit of measurement
+ * given by {@link #getTranslationUnit()}.</li>
+ * <li>If {@code true}, the value returned by this method is the ratio of the translation divided by the
+ * distance between grid cells in the <var>dim</var> dimension (<var>Δx</var> or <var>Δy</var> in the
+ * {@linkplain #DatumShiftGrid(Unit, LinearTransform, int[], boolean, Unit) constructor javadoc}).</li>
+ * </ul>
+ *
+ * @param dim The dimension of the translation vector component to get,
+ * from 0 inclusive to {@link #getTranslationDimensions()} exclusive.
+ * @param gridX The grid index on the <var>x</var> axis, from 0 inclusive to {@link #nx} exclusive.
+ * @param gridY The grid index on the <var>y</var> axis, from 0 inclusive to {@link #ny} exclusive.
+ * @return The translation for the given dimension in the grid cell at the given index.
+ */
+ public abstract double getCellValue(int dim, int gridX, int gridY);
+
+ /**
+ * Returns an average translation value for the given dimension.
+ * Those average values shall provide a good "first guess" before to interpolate the actual translation value
* at the (<var>x</var>,<var>y</var>) coordinate. This "first guess" is needed for inverse transform.
*
* <div class="section">Default implementation</div>
@@ -192,12 +523,14 @@ public abstract class DatumShiftGrid imp
* are fixed by definition to -168, -60 and +320 metres for dimensions 0, 1 and 2 respectively
* (geocentric <var>X</var>, <var>Y</var> and <var>Z</var>).</div>
*
- * @param dim The dimension for which to get an average value,
- * from 0 inclusive to {@link #getShiftDimensions()} exclusive.
- * @return A value close to the average for the given dimension.
+ * @param dim The dimension for which to get an average translation value,
+ * from 0 inclusive to {@link #getTranslationDimensions()} exclusive.
+ * @return A translation value close to the average for the given dimension.
*/
- public double getAverageOffset(final int dim) {
+ public double getCellMean(final int dim) {
final DoubleDouble sum = new DoubleDouble();
+ final int nx = gridSize[0];
+ final int ny = gridSize[1];
for (int gridY=0; gridY<ny; gridY++) {
for (int gridX=0; gridX<nx; gridX++) {
sum.add(getCellValue(dim, gridX, gridY));
@@ -207,52 +540,18 @@ public abstract class DatumShiftGrid imp
}
/**
- * Interpolates the translation to apply for the given coordinate. The result is stored in the
- * given {@code offsets} array, which shall have a length of at least {@link #getShiftDimensions()}.
- * The computed translation values are often for the same dimensions than the given <var>x</var> and <var>y</var>
- * values, but not necessarily.
- * See the class javadoc for use cases.
+ * Returns {@code true} if the translation values in the cells are divided by the cell size.
+ * If {@code true}, then the values returned by {@link #getCellValue getCellValue(…)},
+ * {@link #getCellMean getCellMean(…)} and {@link #interpolateInCell interpolateInCell(…)} methods
+ * are the ratio of the translation divided by the distance between grid cells in the requested
+ * dimension (<var>Δx</var> or <var>Δy</var> in the {@linkplain #DatumShiftGrid(Unit, LinearTransform,
+ * int[], boolean, Unit) constructor javadoc}).
*
- * <div class="section">Default implementation</div>
- * The default implementation performs the following steps for each dimension {@code dim},
- * where the number of dimension is determined by {@link #getShiftDimensions()}.
- * <ol>
- * <li>Convert the given (<var>x</var>,<var>y</var>) coordinate into grid coordinate
- * using the formula documented in {@link #scaleX} and {@link #scaleY} fields.</li>
- * <li>Clamp the grid coordinate into the [0 … {@link #nx} - 2] and [0 … {@link #ny} - 2] ranges, inclusive.</li>
- * <li>Using {@link #getCellValue(int, int, int)}, get the four cell values around the coordinate.</li>
- * <li>Apply a bilinear interpolation and store the result in {@code offsets[dim]}.</li>
- * </ol>
- *
- * @param x First ordinate (often longitude, but not necessarily) of the point for which to get the offset.
- * @param y Second ordinate (often latitude, but not necessarily) of the point for which to get the offset.
- * @param offsets A pre-allocated array where to write the translation vector.
- */
- public void offsetAt(double x, double y, double[] offsets) {
- final int gridX = Math.max(0, Math.min(nx - 2, (int) Math.floor(x = (x - x0) * scaleX)));
- final int gridY = Math.max(0, Math.min(ny - 2, (int) Math.floor(y = (y - y0) * scaleY)));
- x -= gridX;
- y -= gridY;
- final int n = getShiftDimensions();
- for (int dim = 0; dim < n; dim++) {
- double r0 = getCellValue(dim, gridX, gridY );
- double r1 = getCellValue(dim, gridX, gridY+1);
- r0 += x * (getCellValue(dim, gridX+1, gridY ) - r0);
- r1 += x * (getCellValue(dim, gridX+1, gridY+1) - r1);
- offsets[dim] = y * (r1 - r0) + r0;
- }
- }
-
- /**
- * Returns the offset stored at the given grid indices along the given dimension.
- *
- * @param dim The dimension of the offset component to get,
- * from 0 inclusive to {@link #getShiftDimensions()} exclusive.
- * @param gridX The grid index along the <var>x</var> axis, from 0 inclusive to {@link #nx} exclusive.
- * @param gridY The grid index along the <var>y</var> axis, from 0 inclusive to {@link #ny} exclusive.
- * @return The offset at the given dimension in the grid cell at the given index.
+ * @return {@code true} if the translation values in the cells are divided by the cell size.
*/
- protected abstract double getCellValue(int dim, int gridX, int gridY);
+ public boolean isCellValueRatio() {
+ return isCellValueRatio;
+ }
/**
* Returns {@code true} if the given object is a grid containing the same data than this grid.
@@ -264,13 +563,12 @@ public abstract class DatumShiftGrid imp
@Override
public boolean equals(final Object other) {
if (other != null && other.getClass() == getClass()) {
- final DatumShiftGrid that = (DatumShiftGrid) other;
- return nx == that.nx
- && ny == that.ny
- && Numerics.equals(x0, that.x0)
- && Numerics.equals(y0, that.y0)
- && Numerics.equals(scaleX, that.scaleX)
- && Numerics.equals(scaleY, that.scaleY);
+ final DatumShiftGrid<?,?> that = (DatumShiftGrid<?,?>) other;
+ return Arrays .equals(gridSize, that.gridSize) // Test first the value that are most likely to differ
+ && Objects.equals(coordinateToGrid, that.coordinateToGrid)
+ && Objects.equals(coordinateUnit, that.coordinateUnit)
+ && Objects.equals(translationUnit, that.translationUnit)
+ && isCellValueRatio == that.isCellValueRatio;
}
return false;
}
@@ -284,10 +582,14 @@ public abstract class DatumShiftGrid imp
*/
@Override
public int hashCode() {
- return Numerics.hashCode(Double.doubleToLongBits(x0)
- + 31 * (Double.doubleToLongBits(y0)
- + 31 * (Double.doubleToLongBits(scaleX)
- + 31 * Double.doubleToLongBits(scaleY))))
- + 37 * nx + ny;
+ return Objects.hashCode(coordinateToGrid) + 37 * Arrays.hashCode(gridSize);
+ }
+
+ /**
+ * Invoked after deserialization. This method computes the transient fields.
+ */
+ private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+ computeConversionFactors();
}
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/matrix/Matrices.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -223,7 +223,7 @@ public final class Matrices extends Stat
* Argument validity shall be verified by the caller.
*
* @param useEnvelopes {@code true} if source and destination envelopes shall be taken in account.
- * If {@code false}, then source and destination envelopes will be ignored and may be null.
+ * If {@code false}, then source and destination envelopes will be ignored and can be null.
*/
@SuppressWarnings("null")
private static MatrixSIS createTransform(final Envelope srcEnvelope, final AxisDirection[] srcAxes,
@@ -713,7 +713,7 @@ public final class Matrices extends Stat
*/
public static MatrixSIS unmodifiable(final Matrix matrix) {
if (matrix == null || matrix instanceof UnmodifiableMatrix) {
- return (UnmodifiableMatrix) matrix;
+ return (MatrixSIS) matrix;
} else {
return new UnmodifiableMatrix(matrix);
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/AbstractLinearTransform.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -16,6 +16,7 @@
*/
package org.apache.sis.referencing.operation.transform;
+import java.util.Arrays;
import java.io.Serializable;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
@@ -161,6 +162,63 @@ abstract class AbstractLinearTransform e
}
/**
+ * Transforms an array of relative distance vectors. Distance vectors are transformed without applying
+ * the translation components. The default implementation is not very efficient, but it should not be
+ * an issue since this method is not invoked often.
+ *
+ * @since 0.7
+ */
+ @Override
+ public void deltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) {
+ int offFinal = 0;
+ double[] dstFinal = null;
+ final int srcDim, dstDim;
+ int srcInc = srcDim = getSourceDimensions();
+ int dstInc = dstDim = getTargetDimensions();
+ if (srcPts == dstPts) {
+ switch (IterationStrategy.suggest(srcOff, srcDim, dstOff, dstDim, numPts)) {
+ case ASCENDING: {
+ break;
+ }
+ case DESCENDING: {
+ srcOff += (numPts - 1) * srcDim; srcInc = -srcInc;
+ dstOff += (numPts - 1) * dstDim; dstInc = -dstInc;
+ break;
+ }
+ default: {
+ srcPts = Arrays.copyOfRange(srcPts, srcOff, srcOff + numPts*srcDim);
+ srcOff = 0;
+ break;
+ }
+ case BUFFER_TARGET: {
+ dstFinal = dstPts; dstPts = new double[numPts * dstInc];
+ offFinal = dstOff; dstOff = 0;
+ break;
+ }
+ }
+ }
+ final double[] buffer = new double[dstDim];
+ while (--numPts >= 0) {
+ for (int j=0; j<dstDim; j++) {
+ double sum = 0;
+ for (int i=0; i<srcDim; i++) {
+ final double e = getElement(j, i);
+ if (e != 0) { // See the comment in ProjectiveTransform for the purpose of this test.
+ sum += srcPts[srcOff + i] * e;
+ }
+ }
+ buffer[j] = sum;
+ }
+ System.arraycopy(buffer, 0, dstPts, dstOff, dstDim);
+ srcOff += srcInc;
+ dstOff += dstInc;
+ }
+ if (dstFinal != null) {
+ System.arraycopy(dstPts, 0, dstFinal, offFinal, dstPts.length);
+ }
+ }
+
+ /**
* Compares this math transform with an object which is known to be of the same class.
* Implementors can safely cast the {@code object} argument to their subclass.
*
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransform.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/InterpolatedGeocentricTransform.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -18,6 +18,8 @@ package org.apache.sis.referencing.opera
import java.util.Arrays;
import javax.measure.unit.Unit;
+import javax.measure.quantity.Angle;
+import javax.measure.quantity.Length;
import org.opengis.util.FactoryException;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.parameter.ParameterDescriptorGroup;
@@ -69,7 +71,7 @@ import org.apache.sis.util.Debug;
* signs used in NTG_88 document. This is because NTG_88 grid defines shifts from target to source,
* while this class expects shifts from source to target.
*
- * <p>This algorithm is not the same as a (theoretical) {@link EllipsoidToCentricTransform} →
+ * <p><b>Note:</b> this algorithm is not the same as a (theoretical) {@link EllipsoidToCentricTransform} →
* {@link InterpolatedTransform} → (inverse of {@code EllipsoidToCentricTransform}) concatenation
* because the {@code DatumShiftGrid} inputs are geographic coordinates even if the interpolated
* grid values are in geocentric space.</p></div>
@@ -175,28 +177,31 @@ public class InterpolatedGeocentricTrans
* @param target The target ellipsoid.
* @param isTarget3D {@code true} if the target coordinates have a height.
* @param grid The grid of datum shifts from source to target datum.
- * The {@link DatumShiftGrid#offsetAt DatumShiftGrid.offsetAt(…)} method shall expect
- * input geographic coordinates in <strong>radians</strong> and computes (ΔX, ΔY, ΔZ)
- * shifts from <em>source</em> to <em>target</em> in the unit of source ellipsoid axes.
+ * The {@link DatumShiftGrid#interpolateInCell DatumShiftGrid.interpolateInCell(…)} method
+ * shall compute (ΔX, ΔY, ΔZ) translations from <em>source</em> to <em>target</em> in the
+ * unit of source ellipsoid axes.
*
* @see #createGeodeticTransformation(MathTransformFactory, Ellipsoid, boolean, Ellipsoid, boolean, DatumShiftGrid)
*/
protected InterpolatedGeocentricTransform(final Ellipsoid source, final boolean isSource3D,
final Ellipsoid target, final boolean isTarget3D,
- final DatumShiftGrid grid)
+ final DatumShiftGrid<Angle,Length> grid)
{
super(source, isSource3D,
target, isTarget3D,
- grid.getAverageOffset(0),
- grid.getAverageOffset(1),
- grid.getAverageOffset(2),
- grid, false, // Non-abridged Molodensky
- DESCRIPTOR);
+ grid.getCellMean(0),
+ grid.getCellMean(1),
+ grid.getCellMean(2),
+ grid, false, DESCRIPTOR);
- final int dim = grid.getShiftDimensions();
+ final int dim = grid.getTranslationDimensions();
if (dim != 3) {
throw new MismatchedDimensionException(Errors.format(Errors.Keys.MismatchedDimension_3, "grid", 3, dim));
}
+ Object unit = "ratio";
+ if (grid.isCellValueRatio() || (unit = grid.getTranslationUnit()) != source.getAxisUnit()) {
+ throw new IllegalArgumentException(Errors.format(Errors.Keys.IllegalUnitFor_2, "translation", unit));
+ }
if (isSource3D || isTarget3D) {
inverse = new Inverse(this, source, target);
} else {
@@ -224,16 +229,16 @@ public class InterpolatedGeocentricTrans
* @param target The target ellipsoid.
* @param isTarget3D {@code true} if the target coordinates have a height.
* @param grid The grid of datum shifts from source to target datum.
- * The {@link DatumShiftGrid#offsetAt DatumShiftGrid.offsetAt(…)} method shall expect
- * input geographic coordinates in <strong>radians</strong> and computes (ΔX, ΔY, ΔZ)
- * shifts from <em>source</em> to <em>target</em> in the unit of source ellipsoid axes.
+ * The {@link DatumShiftGrid#interpolateInCell DatumShiftGrid.interpolateInCell(…)} method
+ * shall compute (ΔX, ΔY, ΔZ) translations from <em>source</em> to <em>target</em> in the
+ * unit of source ellipsoid axes.
* @return The transformation between geographic coordinates in degrees.
* @throws FactoryException if an error occurred while creating a transform.
*/
public static MathTransform createGeodeticTransformation(final MathTransformFactory factory,
final Ellipsoid source, final boolean isSource3D,
final Ellipsoid target, final boolean isTarget3D,
- final DatumShiftGrid grid) throws FactoryException
+ final DatumShiftGrid<Angle,Length> grid) throws FactoryException
{
ArgumentChecks.ensureNonNull("grid", grid);
final InterpolatedGeocentricTransform tr;
@@ -258,12 +263,12 @@ public class InterpolatedGeocentricTrans
final void completeParameters(final Parameters pg, final double semiMinor, final Unit<?> unit, double Δf) {
super.completeParameters(pg, semiMinor, unit, Δf);
if (pg != context) {
- Δf = Δfmod / semiMajor;
+ Δf = Δfmod / semiMinor;
pg.getOrCreate(Molodensky.AXIS_LENGTH_DIFFERENCE).setValue(Δa, unit);
pg.getOrCreate(Molodensky.FLATTENING_DIFFERENCE) .setValue(Δf, Unit.ONE);
}
- if (grid instanceof DatumShiftGridFile) {
- pg.getOrCreate(FranceGeocentricInterpolation.FILE).setValue(((DatumShiftGridFile) grid).file);
+ if (grid instanceof DatumShiftGridFile<?,?>) {
+ pg.getOrCreate(FranceGeocentricInterpolation.FILE).setValue(((DatumShiftGridFile<?,?>) grid).file);
}
}
@@ -280,12 +285,12 @@ public class InterpolatedGeocentricTrans
final double[] dstPts, final int dstOff,
final boolean derivate) throws TransformException
{
- final double[] offset = new double[3];
+ final double[] vector = new double[3];
final double λ = srcPts[srcOff];
final double φ = srcPts[srcOff+1];
- grid.offsetAt(λ, φ, offset);
+ grid.interpolateAtNormalized(λ, φ, vector);
return transform(λ, φ, isSource3D ? srcPts[srcOff+2] : 0,
- dstPts, dstOff, offset[0], offset[1], offset[2], null, derivate);
+ dstPts, dstOff, vector[0], vector[1], vector[2], null, derivate);
}
/**
@@ -330,7 +335,7 @@ public class InterpolatedGeocentricTrans
while (--numPts >= 0) {
final double λ = srcPts[srcOff ];
final double φ = srcPts[srcOff+1];
- grid.offsetAt(λ, φ, offset);
+ grid.interpolateAtNormalized(λ, φ, offset);
transform(λ, φ, isSource3D ? srcPts[srcOff+2] : 0,
dstPts, dstOff, offset[0], offset[1], offset[2], null, false);
srcOff += srcInc;
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -18,6 +18,7 @@ package org.apache.sis.referencing.opera
import org.opengis.referencing.operation.Matrix;
import org.opengis.referencing.operation.MathTransform;
+import org.opengis.referencing.operation.TransformException;
/**
@@ -61,7 +62,7 @@ import org.opengis.referencing.operation
*
* @author Martin Desruisseaux (IRD, Geomatys)
* @since 0.4
- * @version 0.6
+ * @version 0.7
* @module
*
* @see org.apache.sis.referencing.operation.transform.MathTransforms#linear(Matrix)
@@ -93,4 +94,27 @@ public interface LinearTransform extends
* @see MathTransforms#getMatrix(MathTransform)
*/
Matrix getMatrix();
+
+ /**
+ * Transforms an array of relative distance vectors.
+ * Distance vectors are transformed without applying the translation components.
+ * The supplied array of distance values will contain packed values.
+ *
+ * <div class="note"><b>Example:</b> if the source dimension is 3, then the values will be packed in this order:
+ * (<var>Δx₀</var>,<var>Δy₀</var>,<var>Δz₀</var>,
+ * <var>Δx₁</var>,<var>Δy₁</var>,<var>Δz₁</var> …).
+ * </div>
+ *
+ * @param srcPts The array containing the source vectors.
+ * @param srcOff The offset to the first vector to be transformed in the source array.
+ * @param dstPts The array into which the transformed vectors are returned. Can be the same than {@code srcPts}.
+ * @param dstOff The offset to the location of the first transformed vector that is stored in the destination array.
+ * @param numPts The number of vector objects to be transformed.
+ * @throws TransformException if a vector can not be transformed.
+ *
+ * @see java.awt.geom.AffineTransform#deltaTransform(double[], int, double[], int, int)
+ *
+ * @since 0.7
+ */
+ void deltaTransform(double[] srcPts, int srcOff, double[] dstPts, int dstOff, int numPts) throws TransformException;
}
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/LinearTransform1D.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -239,7 +239,7 @@ class LinearTransform1D extends Abstract
public void transform(final double[] srcPts, int srcOff,
final double[] dstPts, int dstOff, int numPts)
{
- if (srcPts!=dstPts || srcOff>=dstOff) {
+ if (srcPts != dstPts || srcOff >= dstOff) {
while (--numPts >= 0) {
dstPts[dstOff++] = offset + scale * srcPts[srcOff++];
}
@@ -261,7 +261,7 @@ class LinearTransform1D extends Abstract
public void transform(final float[] srcPts, int srcOff,
final float[] dstPts, int dstOff, int numPts)
{
- if (srcPts!=dstPts || srcOff>=dstOff) {
+ if (srcPts != dstPts || srcOff >= dstOff) {
while (--numPts >= 0) {
dstPts[dstOff++] = (float) (offset + scale * srcPts[srcOff++]);
}
@@ -301,6 +301,29 @@ class LinearTransform1D extends Abstract
}
}
+ /**
+ * Transforms many distance vectors in a list of ordinal values.
+ * The default implementation computes the values from the {@link #scale} coefficient only.
+ *
+ * @since 0.7
+ */
+ @Override
+ public void deltaTransform(final double[] srcPts, int srcOff,
+ final double[] dstPts, int dstOff, int numPts)
+ {
+ if (srcPts != dstPts || srcOff >= dstOff) {
+ while (--numPts >= 0) {
+ dstPts[dstOff++] = scale * srcPts[srcOff++];
+ }
+ } else {
+ srcOff += numPts;
+ dstOff += numPts;
+ while (--numPts >= 0) {
+ dstPts[--dstOff] = scale * srcPts[--srcOff];
+ }
+ }
+ }
+
/**
* {@inheritDoc}
*/
Modified: sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java?rev=1718271&r1=1718270&r2=1718271&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] (original)
+++ sis/branches/JDK8/core/sis-referencing/src/main/java/org/apache/sis/referencing/operation/transform/MolodenskyFormula.java [UTF-8] Mon Dec 7 09:52:59 2015
@@ -154,7 +154,7 @@ abstract class MolodenskyFormula extends
* The grid of datum shifts from source datum to target datum.
* This can be non-null only for {@link InterpolatedGeocentricTransform}.
*/
- final DatumShiftGrid grid;
+ final DatumShiftGrid<?,?> grid;
/**
* Constructs the inverse of a Molodensky transform.
@@ -174,6 +174,7 @@ abstract class MolodenskyFormula extends
/**
* Creates a Molodensky transform from the specified parameters.
+ * If a non-null {@code grid} is specified, it is caller's responsibility to verify its validity.
*
* @param source The source ellipsoid.
* @param isSource3D {@code true} if the source coordinates have a height.
@@ -190,7 +191,7 @@ abstract class MolodenskyFormula extends
MolodenskyFormula(final Ellipsoid source, final boolean isSource3D,
final Ellipsoid target, final boolean isTarget3D,
final double tX, final double tY, final double tZ,
- final DatumShiftGrid grid, final boolean isAbridged,
+ final DatumShiftGrid<?,?> grid, final boolean isAbridged,
final ParameterDescriptorGroup descriptor)
{
ArgumentChecks.ensureNonNull("source", source);
@@ -402,7 +403,7 @@ abstract class MolodenskyFormula extends
if (offset == null) break;
// Following is executed only in InterpolatedGeocentricTransform case.
- grid.offsetAt(λt, φt, offset);
+ grid.interpolateAtNormalized(λt, φt, offset);
tX = -offset[0];
tY = -offset[1];
tZ = -offset[2];
@@ -514,14 +515,14 @@ abstract class MolodenskyFormula extends
return isSource3D == that.isSource3D
&& isTarget3D == that.isTarget3D
&& isAbridged == that.isAbridged
- && Objects .equals (grid, that.grid)
&& Numerics.epsilonEqual(tX, that.tX, mode)
&& Numerics.epsilonEqual(tY, that.tY, mode)
&& Numerics.epsilonEqual(tZ, that.tZ, mode)
&& Numerics.epsilonEqual(Δa, that.Δa, mode)
&& Numerics.epsilonEqual(Δfmod, that.Δfmod, mode)
&& Numerics.epsilonEqual(semiMajor, that.semiMajor, mode)
- && Numerics.epsilonEqual(eccentricitySquared, that.eccentricitySquared, mode);
+ && Numerics.epsilonEqual(eccentricitySquared, that.eccentricitySquared, mode)
+ && Objects .equals(grid, that.grid);
}
return false;
}