/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * 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. * * This package contains documentation from OpenGIS specifications. * OpenGIS consortium's work is fully acknowledged here. */ package org.geotools.referencing.cs; import java.util.Map; import java.util.Arrays; import java.util.HashMap; import java.util.Collections; import javax.measure.unit.SI; import javax.measure.unit.Unit; import javax.measure.converter.UnitConverter; import javax.measure.converter.ConversionException; import org.opengis.referencing.cs.AxisDirection; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.cs.CoordinateSystemAxis; import org.opengis.referencing.operation.Matrix; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.util.InternationalString; import org.geotools.util.Utilities; import org.geotools.measure.Measure; import org.geotools.referencing.AbstractIdentifiedObject; import org.geotools.referencing.operation.matrix.GeneralMatrix; import org.geotools.referencing.wkt.Formatter; import org.geotools.resources.Classes; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; import org.geotools.resources.i18n.Vocabulary; /** * The set of coordinate system axes that spans a given coordinate space. A coordinate system (CS) * is derived from a set of (mathematical) rules for specifying how coordinates in a given space * are to be assigned to points. The coordinate values in a coordinate tuple shall be recorded in * the order in which the coordinate system axes are recorded, whenever those * coordinates use a coordinate reference system that uses this coordinate system. *
* This class is conceptually abstract, even if it is technically possible to
* instantiate it. Typical applications should create instances of the most specific subclass with
* {@code Default} prefix instead. An exception to this rule may occurs when it is not possible to
* identify the exact type. For example it is not possible to infer the exact coordinate system from
* Well
* Known Text is some cases (e.g. in a {@code LOCAL_CS} element). In such exceptional
* situation, a plain {@code AbstractCS} object may be instantiated.
*
* @since 2.1
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*
* @see DefaultCoordinateSystemAxis
* @see javax.measure.unit.Unit
* @see org.geotools.referencing.datum.AbstractDatum
* @see org.geotools.referencing.crs.AbstractCRS
*/
public class AbstractCS extends AbstractIdentifiedObject implements CoordinateSystem {
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = 6757665252533744744L;
/**
* Base axis to use for checking directions. This is used in order to trap
* inconsistency like an axis named "Northing" with South direction.
*/
private static final DefaultCoordinateSystemAxis[] DIRECTION_CHECKS = {
DefaultCoordinateSystemAxis.NORTHING,
DefaultCoordinateSystemAxis.EASTING,
DefaultCoordinateSystemAxis.SOUTHING,
DefaultCoordinateSystemAxis.WESTING
};
/**
* The axis for this coordinate system at the specified dimension.
*/
private final CoordinateSystemAxis[] axis;
/**
* The unit for measuring distance in this coordinate system, or {@code null} if none.
* Will be computed only when first needed.
*/
private transient Unit> distanceUnit;
/**
* Constructs a new coordinate system with the same values than the specified one.
* This copy constructor provides a way to wrap an arbitrary implementation into a
* Geotools one or a user-defined one (as a subclass), usually in order to leverage
* some implementation-specific API. This constructor performs a shallow copy,
* i.e. the properties are not cloned.
*
* @param cs The coordinate system to copy.
*
* @since 2.2
*/
public AbstractCS(final CoordinateSystem cs) {
super(cs);
if (cs instanceof AbstractCS) {
axis = ((AbstractCS) cs).axis;
} else {
axis = new CoordinateSystemAxis[cs.getDimension()];
for (int i=0; i
* This method is typically used together with {@link #swapAndScaleAxis swapAndScaleAxis}
* for the creation of a transformation step before some
* {@linkplain org.opengis.referencing.operation.MathTransform math transform}.
* Example:
*
*
* This method should not be public because current implementation is not fully consistent
* for every pair of CS. It tries to check the opposite direction in addition of the usual
* one, but only a few pre-defined axis declare their opposite. This method should be okay
* when invoked on pre-defined CS declared in this package. {@link PredefinedCS} uses this
* method only that way.
*/
final boolean axisColinearWith(final CoordinateSystem userCS) {
if (userCS.getDimension() != getDimension()) {
return false;
}
final DefaultCoordinateSystemAxis[] axis0 = getDefaultAxis(this);
final DefaultCoordinateSystemAxis[] axis1 = getDefaultAxis(userCS);
next: for (int i=0; i
*
* @param sourceCS The source coordinate system.
* @param targetCS The target coordinate system.
* @return The conversion from {@code sourceCS} to {@code targetCS} as
* an affine transform. Only axis direction and units are taken in account.
* @throws IllegalArgumentException if axis doesn't matches, or the CS doesn't have the
* same geometry.
* @throws ConversionException if the unit conversion is non-linear.
*/
public static Matrix swapAndScaleAxis(final CoordinateSystem sourceCS,
final CoordinateSystem targetCS)
throws IllegalArgumentException, ConversionException
{
if (!Classes.sameInterfaces(sourceCS.getClass(), targetCS.getClass(), CoordinateSystem.class)) {
throw new IllegalArgumentException(Errors.format(
ErrorKeys.INCOMPATIBLE_COORDINATE_SYSTEM_TYPE));
}
final AxisDirection[] sourceAxis = getAxisDirections(sourceCS);
final AxisDirection[] targetAxis = getAxisDirections(targetCS);
final GeneralMatrix matrix = new GeneralMatrix(sourceAxis, targetAxis);
assert Arrays.equals(sourceAxis, targetAxis) == matrix.isIdentity() : matrix;
/*
* The previous code computed a matrix for swapping axis. Usually, this
* matrix contains only 0 and 1 values with only one "1" value by row.
* For example, the matrix operation for swapping x and y axis is:
*
* [y] [ 0 1 0 ] [x]
* [x] = [ 1 0 0 ] [y]
* [1] [ 0 0 1 ] [1]
*
* Now, take in account units conversions. Each matrix's element (j,i)
* is multiplied by the conversion factor from sourceCS.getUnit(i) to
* targetCS.getUnit(j). This is an element-by-element multiplication,
* not a matrix multiplication. The last column is processed in a special
* way, since it contains the offset values.
*/
final int sourceDim = matrix.getNumCol()-1;
final int targetDim = matrix.getNumRow()-1;
assert sourceDim == sourceCS.getDimension() : sourceCS;
assert targetDim == targetCS.getDimension() : targetCS;
for (int j=0; j
* [-y(cm)] [ 0 -100 0 ] [x(m)]
* [ x(cm)] = [ 100 0 0 ] [y(m)]
* [ 1 ] [ 0 0 1 ] [1 ]
*
*
* A rational for standard axis order and units is explained in the Axis units and
* direction section in the {@linkplain org.geotools.referencing.operation.projection
* description of map projection package}.
*
* @param cs The coordinate system.
* @return A constant similar to the specified {@code cs} with "standard" axis.
* @throws IllegalArgumentException if the specified coordinate system is unknow to this method.
*
* @since 2.2
*/
public static CoordinateSystem standard(final CoordinateSystem cs)
throws IllegalArgumentException
{
return PredefinedCS.standard(cs);
}
/**
* Suggests an unit for measuring distances in this coordinate system. The default
* implementation scans all {@linkplain CoordinateSystemAxis#getUnit axis units},
* ignoring angular ones (this also implies ignoring {@linkplain Unit#ONE dimensionless} ones).
* If more than one non-angular unit is found, the default implementation returns the "largest"
* one (e.g. kilometers instead of meters).
*
* @return Suggested distance unit.
* @throws ConversionException if some non-angular units are incompatibles.
*/
final Unit> getDistanceUnit() throws ConversionException {
Unit> unit = distanceUnit; // Avoid the need for synchronization.
if (unit == null) {
for (int i=0; i
* Matrix step1 = swapAndScaleAxis(sourceCS, standard(sourceCS));
* Matrix step2 = ... some transform operating on standard axis ...
* Matrix step3 = swapAndScaleAxis(standard(targetCS), targetCS);
*