blob: a92486dfb94c4f62e075415afd6a66a980ddbe8f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF 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.apache.sis.referencing.datum;
import java.util.Date;
import java.util.Objects;
import java.time.Duration;
import java.time.temporal.Temporal;
import org.opengis.metadata.extent.Extent;
import org.opengis.referencing.datum.GeodeticDatum;
import org.opengis.referencing.datum.PrimeMeridian;
import org.apache.sis.util.privy.DoubleDouble;
import org.apache.sis.util.privy.Constants;
import static org.apache.sis.util.ArgumentChecks.*;
/**
* Parameters for a time-dependent geographic transformation between two datum.
* The {@link #tX tX}, {@link #tY tY}, {@link #tZ tZ}, {@link #rX rX}, {@link #rY rY}, {@link #rZ rZ}
* and {@link #dS dS} parameters inherited from the parent class are values at a point in time given
* by {@link #getTimeReference()}. Those values vary at a rate given by the parameters listed in the
* table below (codes, names and abbreviations are from the EPSG database):
*
* <table class="sis">
* <caption>Parameters defined by EPSG</caption>
* <tr><th>Code</th> <th>Name</th> <th>Abbr.</th></tr>
* <tr><td>1040</td> <td>Rate of change of X-axis translation</td> <td>{@link #dtX}</td></tr>
* <tr><td>1041</td> <td>Rate of change of Y-axis translation</td> <td>{@link #dtY}</td></tr>
* <tr><td>1042</td> <td>Rate of change of Z-axis translation</td> <td>{@link #dtZ}</td></tr>
* <tr><td>1043</td> <td>Rate of change of X-axis rotation</td> <td>{@link #drX}</td></tr>
* <tr><td>1044</td> <td>Rate of change of Y-axis rotation</td> <td>{@link #drY}</td></tr>
* <tr><td>1045</td> <td>Rate of change of Z-axis rotation</td> <td>{@link #drZ}</td></tr>
* <tr><td>1046</td> <td>Rate of change of scale difference</td> <td>{@link #ddS}</td></tr>
* </table>
*
* The numerical fields in this {@code TimeDependentBWP} class uses the EPSG abbreviations
* with 4 additional constraints compared to the EPSG definitions:
*
* <ul>
* <li>Unit of {@link #ddS} is fixed to <em>parts per million per year</em>.</li>
* <li>Unit of {@link #dtX}, {@link #dtY} and {@link #dtZ} is fixed to <em>millimetres per year</em>.</li>
* <li>Unit of {@link #drX}, {@link #drY} and {@link #drZ} is fixed to <em>milli arc-seconds per year</em>.</li>
* <li>Sign of rotation terms is fixed to the <em>Position Vector</em> convention (EPSG operation method 1053).
* This is the opposite sign than the <cite>Coordinate Frame Rotation</cite> (EPSG operation method 1056.
* The Position Vector convention is used by IAG and recommended by ISO 19111.</li>
* </ul>
*
* @author Martin Desruisseaux (Geomatys)
* @version 1.5
* @since 0.4
*/
@SuppressWarnings("CloneableImplementsClone") // Fields in this class do not need cloning.
public class TimeDependentBWP extends BursaWolfParameters {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 8404372938646871943L;
/**
* Rate of change of X-axis translation in millimetres per year (EPSG:1040).
*/
public double dtX;
/**
* Rate of change of Y-axis translation in millimetres per year (EPSG:1041).
*/
public double dtY;
/**
* Rate of change of Z-axis translation in millimetres per year (EPSG:1042).
*/
public double dtZ;
/**
* Rate of change of X-axis rotation in milli arc-seconds per year (EPSG:1043),
* sign following the <i>Position Vector</i> convention.
*/
public double drX;
/**
* Rate of change of Y-axis rotation in milli arc-seconds per year (EPSG:1044),
* sign following the <i>Position Vector</i> convention.
*/
public double drY;
/**
* Rate of change of Z-axis rotation in milli arc-seconds per year (EPSG:1045),
* sign following the <i>Position Vector</i> convention.
*/
public double drZ;
/**
* Rate of change of the scale difference in parts per million per year (EPSG:1046).
*/
public double ddS;
/**
* The reference epoch for time-dependent parameters (EPSG:1047).
*/
@SuppressWarnings("serial") // Most implementations are serializable.
private final Temporal timeReference;
/**
* Creates a new instance for the given target datum, domain of validity and time reference.
* All numerical parameters are initialized to 0, which correspond to an identity transform.
* Callers can assign numerical values to the public fields of interest after construction.
*
* @param targetDatum the target datum (usually WGS 84) for this set of parameters.
* @param domainOfValidity area or region in which a coordinate transformation based on those Bursa-Wolf parameters
* is valid, or {@code null} if unspecified.
* @param timeReference the reference epoch for time-dependent parameters.
*
* @since 1.5
*/
public TimeDependentBWP(final GeodeticDatum targetDatum, final Extent domainOfValidity, final Temporal timeReference) {
super(targetDatum, domainOfValidity);
this.timeReference = Objects.requireNonNull(timeReference);
}
/**
* Creates a new instance for the given target datum, domain of validity and time reference.
*
* @deprecated Replaced by {@link #TimeDependentBWP(GeodeticDatum, Extent, Temporal)}.
*/
@Deprecated(since="1.5", forRemoval=true)
public TimeDependentBWP(final GeodeticDatum targetDatum, final Extent domainOfValidity, final Date timeReference) {
this(targetDatum, domainOfValidity, timeReference.toInstant());
}
/**
* Verifies parameters validity after initialization.
*/
@Override
void verify(final PrimeMeridian pm) throws IllegalArgumentException {
super.verify(pm);
ensureFinite("dtX", dtX);
ensureFinite("dtY", dtY);
ensureFinite("dtZ", dtZ);
ensureFinite("drX", drX);
ensureFinite("drY", drY);
ensureFinite("drZ", drZ);
}
/**
* Returns the reference epoch for time-dependent parameters.
*
* @return the reference epoch for time-dependent parameters.
*/
public Temporal getTimeReference() {
return timeReference;
}
/**
* Returns the elapsed time from the reference time to the given date, or {@code null} if none.
* The reference time is given by {@link #getTimeReference()}.
*
* @return fractional number of tropical years since reference time, or {@code null}.
*/
@Override
final DoubleDouble period(final Temporal time) {
if (time != null) {
final Duration d = Duration.between(timeReference, time);
if (!d.isZero()) { // Returns null for 0 as an optimization.
return DoubleDouble.of(d).divide(Constants.MILLIS_PER_TROPICAL_YEAR * Constants.NANOS_PER_MILLISECOND);
}
}
return null;
}
/**
* Returns the parameter at the given index, taking rate of change in account.
* The given {@code factor} argument shall contain a conversion factor from mm/year to m/year
* except for parameter at index 6.
*
* @param index 0 for {@code tX}, 1 for {@code tY}, <i>etc.</i> in {@code TOWGS84[…]} order.
* @param factor factor by which to multiply the rate of change, or {@code null}.
*/
@Override
final DoubleDouble param(final int index, final DoubleDouble factor) {
DoubleDouble p = super.param(index, factor);
if (factor != null) {
final double d;
switch (index) {
case 0: d = dtX; break;
case 1: d = dtY; break;
case 2: d = dtZ; break;
case 3: d = drX; break;
case 4: d = drY; break;
case 5: d = drZ; break;
case 6: d = ddS; break;
default: throw new AssertionError(index);
}
p = p.add(factor.multiply(d, true));
}
return p;
}
/**
* Returns the parameter values. The first 14 elements are always {@link #tX tX}, {@link #tY tY}, {@link #tZ tZ},
* {@link #rX rX}, {@link #rY rY}, {@link #rZ rZ}, {@link #dS dS}, {@link #dtX}, {@link #dtY}, {@link #dtZ},
* {@link #drX}, {@link #drY}, {@link #drZ} and {@link #ddS} in that order.
*
* @return the parameter values as an array of length 14.
*
* @since 0.6
*/
@Override
public double[] getValues() {
return new double[] {tX, tY, tZ, rX, rY, rZ, dS, dtX, dtY, dtZ, drX, drY, drZ, ddS};
}
/**
* Sets the parameters to the given values. The given array can have any length. The first array elements will be
* assigned to the {@link #tX tX}, {@link #tY tY}, {@link #tZ tZ}, {@link #rX rX}, {@link #rY rY}, {@link #rZ rZ},
* {@link #dS dS}, {@link #dtX}, {@link #dtY}, {@link #dtZ}, {@link #drX}, {@link #drY}, {@link #drZ} and
* {@link #ddS} fields in that order.
*
* @param elements the new parameter values, as an array of any length.
*
* @since 0.6
*/
@Override
@SuppressWarnings("fallthrough")
public void setValues(final double... elements) {
if (elements.length >= 8) {
switch (elements.length) {
default: ddS = elements[13]; // Fallthrough everywhere.
case 13: drZ = elements[12];
case 12: drY = elements[11];
case 11: drX = elements[10];
case 10: dtZ = elements[ 9];
case 9: dtY = elements[ 8];
case 8: dtX = elements[ 7];
}
}
super.setValues(elements);
}
/**
* {@inheritDoc}
*
* @return {@code true} if the parameters describe no operation.
*/
@Override
public boolean isIdentity() {
return super.isIdentity() && dtX == 0 && dtY == 0 && dtZ == 0;
}
/**
* {@inheritDoc}
*
* @return {@code true} if the parameters describe a translation only.
*/
@Override
public boolean isTranslation() {
return super.isTranslation() && drX == 0 && drY == 0 && drZ == 0;
}
/**
* Inverts in-place the sign of rotation terms and their derivative.
* This method can be invoked for converting a <cite>Coordinate Frame Rotation</cite> transformation
* (EPSG operation method 9607) to a <em>Position Vector</em> transformation (EPSG operation method 9606).
* The latter convention is used by IAG and recommended by ISO 19111.
*/
@Override
public void reverseRotation() {
super.reverseRotation();
drX = -drX;
drY = -drY;
drZ = -drZ;
}
/**
* {@inheritDoc}
*
* @return {@code true} if the given object is equal to this {@code TimeDependentBWP}.
*/
@Override
public boolean equals(final Object object) {
return super.equals(object) && timeReference == ((TimeDependentBWP) object).timeReference;
}
/**
* {@inheritDoc}
*
* @return the hash code value. This value does not need to be the same in past or future versions of this class.
*/
@Override
public int hashCode() {
return super.hashCode() ^ timeReference.hashCode();
}
}