/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2010-2012, Open Source Geospatial Foundation (OSGeo)
* (C) 2010-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.cs;
import java.util.Set;
import java.util.Arrays;
import java.util.Collection;
import java.io.Serializable;
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
import org.opengis.coverage.grid.GridEnvelope;
import org.opengis.coverage.grid.GridGeometry;
import org.opengis.metadata.Identifier;
import org.opengis.referencing.cs.TimeCS;
import org.opengis.referencing.cs.VerticalCS;
import org.opengis.referencing.cs.CartesianCS;
import org.opengis.referencing.cs.EllipsoidalCS;
import org.opengis.referencing.cs.CoordinateSystem;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.Matrix;
import org.geotoolkit.lang.Decorator;
import org.geotoolkit.resources.Errors;
import org.geotoolkit.coverage.grid.GeneralGridEnvelope;
import org.apache.sis.referencing.operation.transform.MathTransforms;
/**
* An implementation of {@link CoordinateSystem} delegating every method calls to the wrapped CS,
* except the axes. The axes will be instances of {@link DiscreteCoordinateSystemAxis} built from
* the ordinate values given at construction time.
* <p>
* This class implements {@link GridGeometry}. But the <cite>grid to CRS</cite> transform
* returned by the later is correct only if every axes are regular. This is not verified
* because the threshold for determining if an axis is regular or not is at caller choice.
*
* @author Martin Desruisseaux (Geomatys)
* @version 3.20
*
* @since 3.15
* @module
*/
@Decorator(CoordinateSystem.class)
class DiscreteCS implements CoordinateSystem, GridGeometry, Serializable {
/**
* For cross-version compatibility.
*/
private static final long serialVersionUID = -6368259804288288946L;
/**
* The wrapped coordinate system.
*/
protected final CoordinateSystem cs;
/**
* The axes. Each elements in this array shall also implement the
* {@link CoordinateSystemAxis} interface.
*/
protected final DiscreteCoordinateSystemAxis<?>[] axes;
/**
* The grid envelope, created only when first needed.
*/
private transient GridEnvelope extent;
/**
* The grid to CRS transform, created only when first needed.
*/
private transient MathTransform gridToCRS;
/** A discrete Cartesian CS. */
static final class Cartesian extends DiscreteCS implements CartesianCS {
private static final long serialVersionUID = -6589829160460750116L;
Cartesian(final CartesianCS cs, final double[]... ordinates) throws IllegalArgumentException {
super(cs, ordinates);
}
}
/** A discrete ellipsoidal CS. */
static final class Ellipsoidal extends DiscreteCS implements EllipsoidalCS {
private static final long serialVersionUID = -847574453071413273L;
Ellipsoidal(final EllipsoidalCS cs, final double[]... ordinates) throws IllegalArgumentException {
super(cs, ordinates);
}
}
/** A discrete vertical CS. */
static final class Vertical extends DiscreteCS implements VerticalCS {
private static final long serialVersionUID = 2486019427140923781L;
Vertical(final VerticalCS cs, final double[]... ordinates) throws IllegalArgumentException {
super(cs, ordinates);
}
}
/** A discrete temporal CS. */
static final class Time extends DiscreteCS implements TimeCS {
private static final long serialVersionUID = -879676008647755725L;
Time(final TimeCS cs, final double[]... ordinates) throws IllegalArgumentException {
super(cs, ordinates);
}
}
/**
* Creates a new instance wrapping the given CS with the given ordinate values for each axis.
*
* @param cs The coordinate system to wrap.
* @param ordinates The ordinate values for each axis. The arrays are <strong>not</strong> cloned.
* @throws IllegalArgumentException If the length of the {@code ordinates} array is not equals
* to the {@linkplain CoordinateSystem#getDimension() dimension} of the given coordinate
* system.
*/
DiscreteCS(final CoordinateSystem cs, final double[]... ordinates) throws IllegalArgumentException {
this.cs = cs;
final int dimension = cs.getDimension();
if (dimension != ordinates.length) {
throw new IllegalArgumentException(Errors.format(
Errors.Keys.MismatchedDimension_3, "ordinates", ordinates.length, dimension));
}
axes = new DiscreteCoordinateSystemAxis<?>[dimension];
for (int i=0; i<dimension; i++) {
axes[i] = DiscreteReferencingFactory.createDiscreteAxis(cs.getAxis(i), ordinates[i]);
}
}
/**
* Creates a new instance wrapping the given CS with the given axes.
*
* @param cs The coordinate system to wrap.
* @param ordinates The axes. This array is <strong>not</strong> cloned.
*/
DiscreteCS(final CoordinateSystem cs, final DiscreteCoordinateSystemAxis<?>... axes) {
this.cs = cs;
this.axes = axes;
}
/**
* Returns the name of the wrapped CS.
*/
@Override
public final Identifier getName() {
return cs.getName();
}
/**
* Returns the alias of the wrapped CS.
*/
@Override
public final Collection<GenericName> getAlias() {
return cs.getAlias();
}
/**
* Returns the identifiers of the wrapped CS.
*/
@Override
public final Set<Identifier> getIdentifiers() {
return cs.getIdentifiers();
}
/**
* Returns the number of axes. This is the length of the array given at
* construction time.
*/
@Override
public final int getDimension() {
return axes.length;
}
/**
* Returns the axes at the given dimension. This is one of the axis given
* at construction time.
*/
@Override
public final CoordinateSystemAxis getAxis(final int dimension) throws IndexOutOfBoundsException {
return (CoordinateSystemAxis) axes[dimension];
}
/**
* Returns the grid range. The {@linkplain GridEnvelope#getLow() lower} values are
* always 0, and the {@linkplain GridEnvelope#getHigh() upper} values are determined
* by the number of discrete ordinates for each axes.
*
* @since 3.20 (derived from 3.15)
*/
@Override
public final synchronized GridEnvelope getExtent() {
if (extent == null) {
final int[] upper = new int[axes.length];
for (int i=0; i<upper.length; i++) {
upper[i] = axes[i].length();
}
extent = new GeneralGridEnvelope(new int[upper.length], upper, false);
}
return extent;
}
/**
* @deprecated Renamed {@link #getExtent()}.
*/
@Override
@Deprecated
public final GridEnvelope getGridRange() {
return getExtent();
}
/**
* Returns the transform from grid coordinates to CRS coordinates mapping pixel center.
* This method assumes that all axes are regular (this is not verified).
*/
@Override
public final synchronized MathTransform getGridToCRS() {
if (gridToCRS == null) {
gridToCRS = getGridToCRS(null);
}
return gridToCRS;
}
/**
* Computes the <cite>grid to CRS</cite> transform using the given CRS for converting dates
* to numerical values. This method assumes that all axes are regular (this is not verified).
*/
final MathTransform getGridToCRS(final CoordinateReferenceSystem crs) {
final Matrix matrix = DiscreteReferencingFactory.getAffineTransform(crs, axes);
if (matrix == null) {
throw new UnsupportedOperationException(Errors.format(Errors.Keys.NotAnAffineTransform));
}
return MathTransforms.linear(matrix);
}
/**
* Returns the remarks of the wrapped CS.
*/
@Override
public final InternationalString getRemarks() {
return cs.getRemarks();
}
/**
* Returns the WKT formatted by the wrapped CS. This is okay to delegate to the
* wrapped CS despite having different axes, because the WKT representation of
* those axes is not changed.
*/
@Override
public final String toWKT() throws UnsupportedOperationException {
return cs.toWKT();
}
/**
* Returns the string representation of the wrapped CS.
* This is usually the same than the WKT representation.
*/
@Override
public final String toString() {
return cs.toString();
}
/**
* Returns a hash code value for this CS.
*/
@Override
public final int hashCode() {
return cs.hashCode() + 31 * Arrays.hashCode(axes);
}
/**
* Compares this CS with the given object for equality.
*
* @param other The object to compare with this CS for equality.
*/
@Override
public final boolean equals(final Object other) {
if (other != null && other.getClass() == getClass()) {
final DiscreteCS that = (DiscreteCS) other;
return cs.equals(that.cs) && Arrays.equals(axes, that.axes);
}
return false;
}
}