/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2001-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.Collections;
import java.util.HashMap;
import java.util.Locale; // For javadoc
import java.util.Map;
import java.util.NoSuchElementException;
import javax.measure.converter.UnitConverter;
import javax.measure.unit.NonSI;
import javax.measure.unit.SI;
import javax.measure.unit.Unit;
import org.opengis.referencing.cs.AxisDirection;
import org.opengis.referencing.cs.CoordinateSystemAxis;
import org.opengis.referencing.cs.RangeMeaning;
import org.opengis.util.InternationalString;
import org.geotools.referencing.AbstractIdentifiedObject;
import org.geotools.referencing.wkt.Formatter;
import org.geotools.resources.i18n.Errors;
import org.geotools.resources.i18n.ErrorKeys;
import org.geotools.resources.i18n.Vocabulary;
import org.geotools.resources.i18n.VocabularyKeys;
import org.geotools.util.SimpleInternationalString;
import org.geotools.util.NameFactory;
import org.geotools.util.Utilities;
/**
* Definition of a coordinate system axis. This is used to label axes, and indicate the orientation.
* See {@linkplain org.opengis.referencing.cs#AxisNames axis name constraints}.
* <p>
* In some case, the axis name is constrained by ISO 19111 depending on the
* {@linkplain org.opengis.referencing.crs.CoordinateReferenceSystem coordinate reference system}
* type. These constraints are identified in the javadoc by "<cite>ISO 19111 name is...</cite>"
* sentences. This constraint works in two directions; for example the names
* "<cite>geodetic latitude</cite>" and "<cite>geodetic longitude</cite>" shall be used to
* designate the coordinate axis names associated with a
* {@linkplain org.opengis.referencing.crs.GeographicCRS geographic coordinate reference system}.
* Conversely, these names shall not be used in any other context.
*
* @since 2.1
*
* @source $URL$
* @version $Id$
* @author Martin Desruisseaux (IRD)
*
* @see AbstractCS
* @see Unit
*/
public class DefaultCoordinateSystemAxis extends AbstractIdentifiedObject
implements CoordinateSystemAxis
{
/**
* Serial number for interoperability with different versions.
*/
private static final long serialVersionUID = -7883614853277827689L;
/**
* Number of directions from "North", "North-North-East", "North-East", etc.
* This is verified by {@code DefaultCoordinateSystemAxisTest.testCompass}.
*/
static final int COMPASS_DIRECTION_COUNT = 16;
/**
* Number of items in {@link #PREDEFINED}. Should be considered
* as a constant after the class initialisation is completed.
*/
private static int PREDEFINED_COUNT = 0;
/**
* The list of predefined constants declared in this class,
* in declaration order. This order matter.
*/
private static final DefaultCoordinateSystemAxis[] PREDEFINED = new DefaultCoordinateSystemAxis[26];
/**
* Default axis info for geodetic longitudes in a
* {@linkplain org.opengis.referencing.crs.GeographicCRS geographic CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The ISO 19111 name is "<cite>geodetic longitude</cite>" and the abbreviation is "λ"
* (lambda).
*
* This axis is usually part of a {@link #GEODETIC_LONGITUDE}, {@link #GEODETIC_LATITUDE},
* {@link #ELLIPSOIDAL_HEIGHT} set.
*
* @see #LONGITUDE
* @see #SPHERICAL_LONGITUDE
* @see #GEODETIC_LATITUDE
*/
public static final DefaultCoordinateSystemAxis GEODETIC_LONGITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEODETIC_LONGITUDE, "\u03BB", AxisDirection.EAST, NonSI.DEGREE_ANGLE);
/**
* Default axis info for geodetic latitudes in a
* {@linkplain org.opengis.referencing.crs.GeographicCRS geographic CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The ISO 19111 name is "<cite>geodetic latitude</cite>" and the abbreviation is "φ" (phi).
*
* This axis is usually part of a {@link #GEODETIC_LONGITUDE}, {@link #GEODETIC_LATITUDE},
* {@link #ELLIPSOIDAL_HEIGHT} set.
*
* @see #LATITUDE
* @see #SPHERICAL_LATITUDE
* @see #GEODETIC_LONGITUDE
*/
public static final DefaultCoordinateSystemAxis GEODETIC_LATITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEODETIC_LATITUDE, "\u03C6", AxisDirection.NORTH, NonSI.DEGREE_ANGLE);
/**
* Default axis info for longitudes.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The abbreviation is "λ" (lambda).
*
* This axis is usually part of a {@link #LONGITUDE}, {@link #LATITUDE}, {@link #ALTITUDE} set.
*
* @see #GEODETIC_LONGITUDE
* @see #SPHERICAL_LONGITUDE
* @see #LATITUDE
*/
public static final DefaultCoordinateSystemAxis LONGITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.LONGITUDE, "\u03BB", AxisDirection.EAST, NonSI.DEGREE_ANGLE);
/**
* Default axis info for latitudes.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The abbreviation is "φ" (phi).
*
* This axis is usually part of a {@link #LONGITUDE}, {@link #LATITUDE}, {@link #ALTITUDE} set.
*
* @see #GEODETIC_LATITUDE
* @see #SPHERICAL_LATITUDE
* @see #LONGITUDE
*/
public static final DefaultCoordinateSystemAxis LATITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.LATITUDE, "\u03C6", AxisDirection.NORTH, NonSI.DEGREE_ANGLE);
/**
* The default axis for height values above the ellipsoid in a
* {@linkplain org.opengis.referencing.crs.GeographicCRS geographic CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#UP up}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>ellipsoidal heigt</cite>" and the abbreviation is lower case
* "<var>h</var>".
*
* This axis is usually part of a {@link #GEODETIC_LONGITUDE}, {@link #GEODETIC_LATITUDE},
* {@link #ELLIPSOIDAL_HEIGHT} set.
*
* @see #ALTITUDE
* @see #GEOCENTRIC_RADIUS
* @see #GRAVITY_RELATED_HEIGHT
* @see #DEPTH
*/
public static final DefaultCoordinateSystemAxis ELLIPSOIDAL_HEIGHT = new DefaultCoordinateSystemAxis(
VocabularyKeys.ELLIPSOIDAL_HEIGHT, "h", AxisDirection.UP, SI.METER);
/**
* The default axis for height values measured from gravity.
*
* Increasing ordinates values go {@linkplain AxisDirection#UP up}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>gravity-related height</cite>" and the abbreviation is lower
* case "<var>H</var>".
*
* @see #ALTITUDE
* @see #ELLIPSOIDAL_HEIGHT
* @see #GEOCENTRIC_RADIUS
* @see #DEPTH
*/
public static final DefaultCoordinateSystemAxis GRAVITY_RELATED_HEIGHT = new DefaultCoordinateSystemAxis(
VocabularyKeys.GRAVITY_RELATED_HEIGHT, "H", AxisDirection.UP, SI.METER);
/**
* The default axis for altitude values.
*
* Increasing ordinates values go {@linkplain AxisDirection#UP up}
* and units are {@linkplain SI#METER metres}.
*
* The abbreviation is lower case "<var>h</var>".
*
* This axis is usually part of a {@link #LONGITUDE}, {@link #LATITUDE}, {@link #ALTITUDE} set.
*
* @see #ELLIPSOIDAL_HEIGHT
* @see #GEOCENTRIC_RADIUS
* @see #GRAVITY_RELATED_HEIGHT
* @see #DEPTH
*/
public static final DefaultCoordinateSystemAxis ALTITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.ALTITUDE, "h", AxisDirection.UP, SI.METER);
/**
* The default axis for depth.
*
* Increasing ordinates values go {@linkplain AxisDirection#DOWN down}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>depth</cite>".
*
* @see #ALTITUDE
* @see #ELLIPSOIDAL_HEIGHT
* @see #GEOCENTRIC_RADIUS
* @see #GRAVITY_RELATED_HEIGHT
*/
public static final DefaultCoordinateSystemAxis DEPTH = new DefaultCoordinateSystemAxis(
VocabularyKeys.DEPTH, "d", AxisDirection.DOWN, SI.METER);
static {
ALTITUDE.opposite = DEPTH;
DEPTH.opposite = ALTITUDE;
}
/**
* Default axis info for radius in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.cs.SphericalCS spherical CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#UP up}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>geocentric radius</cite>" and the abbreviation is lower case
* "<var>r</var>".
*
* This axis is usually part of a {@link #SPHERICAL_LONGITUDE}, {@link #SPHERICAL_LATITUDE},
* {@link #GEOCENTRIC_RADIUS} set.
*
* @see #ALTITUDE
* @see #ELLIPSOIDAL_HEIGHT
* @see #GRAVITY_RELATED_HEIGHT
* @see #DEPTH
*/
public static final DefaultCoordinateSystemAxis GEOCENTRIC_RADIUS = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEOCENTRIC_RADIUS, "r", AxisDirection.UP, SI.METER);
/**
* Default axis info for longitudes in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.crs.SphericalCS spherical CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The ISO 19111 name is "<cite>spherical longitude</cite>" and the abbreviation is "Ω"
* (omega).
*
* This axis is usually part of a {@link #SPHERICAL_LONGITUDE}, {@link #SPHERICAL_LATITUDE},
* {@link #GEOCENTRIC_RADIUS} set.
*
* @see #LONGITUDE
* @see #GEODETIC_LONGITUDE
* @see #SPHERICAL_LATITUDE
*/
public static final DefaultCoordinateSystemAxis SPHERICAL_LONGITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.SPHERICAL_LONGITUDE, "\u03A9", AxisDirection.EAST, NonSI.DEGREE_ANGLE);
/**
* Default axis info for latitudes in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.cs.SphericalCS spherical CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain NonSI#DEGREE_ANGLE decimal degrees}.
*
* The ISO 19111 name is "<cite>spherical latitude</cite>" and the abbreviation is "Θ"
* (theta).
*
* This axis is usually part of a {@link #SPHERICAL_LONGITUDE}, {@link #SPHERICAL_LATITUDE},
* {@link #GEOCENTRIC_RADIUS} set.
*
* @see #LATITUDE
* @see #GEODETIC_LATITUDE
* @see #SPHERICAL_LONGITUDE
*/
public static final DefaultCoordinateSystemAxis SPHERICAL_LATITUDE = new DefaultCoordinateSystemAxis(
VocabularyKeys.SPHERICAL_LATITUDE, "\u03B8", AxisDirection.NORTH, NonSI.DEGREE_ANGLE);
/**
* Default axis info for <var>x</var> values in a
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain SI#METER metres}.
*
* The abbreviation is lower case "<var>x</var>".
*
* This axis is usually part of a {@link #X}, {@link #Y}, {@link #Z} set.
*
* @see #EASTING
* @see #WESTING
* @see #GEOCENTRIC_X
* @see #DISPLAY_X
* @see #COLUMN
*/
public static final DefaultCoordinateSystemAxis X = new DefaultCoordinateSystemAxis(
-1, "x", AxisDirection.EAST, SI.METER);
/**
* Default axis info for <var>y</var> values in a
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain SI#METER metres}.
*
* The abbreviation is lower case "<var>y</var>".
*
* This axis is usually part of a {@link #X}, {@link #Y}, {@link #Z} set.
*
* @see #NORTHING
* @see #SOUTHING
* @see #GEOCENTRIC_Y
* @see #DISPLAY_Y
* @see #ROW
*/
public static final DefaultCoordinateSystemAxis Y = new DefaultCoordinateSystemAxis(
-1, "y", AxisDirection.NORTH, SI.METER);
/**
* Default axis info for <var>z</var> values in a
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#UP up}
* and units are {@linkplain SI#METER metres}.
*
* The abbreviation is lower case "<var>z</var>".
*
* This axis is usually part of a {@link #X}, {@link #Y}, {@link #Z} set.
*/
public static final DefaultCoordinateSystemAxis Z = new DefaultCoordinateSystemAxis(
-1, "z", AxisDirection.UP, SI.METER);
/**
* Default axis info for <var>x</var> values in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go toward prime meridian
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>geocentric X</cite>" and the abbreviation is upper case
* "<var>X</var>".
*
* This axis is usually part of a {@link #GEOCENTRIC_X}, {@link #GEOCENTRIC_Y},
* {@link #GEOCENTRIC_Z} set.
*/
public static final DefaultCoordinateSystemAxis GEOCENTRIC_X = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEOCENTRIC_X, "X", AxisDirection.OTHER, SI.METER);
/**
* Default axis info for <var>y</var> values in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>geocentric Y</cite>" and the abbreviation is upper case
* "<var>Y</var>".
*
* This axis is usually part of a {@link #GEOCENTRIC_X}, {@link #GEOCENTRIC_Y},
* {@link #GEOCENTRIC_Z} set.
*/
public static final DefaultCoordinateSystemAxis GEOCENTRIC_Y = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEOCENTRIC_Y, "Y", AxisDirection.EAST, SI.METER);
/**
* Default axis info for <var>z</var> values in a
* {@linkplain org.opengis.referencing.crs.GeocentricCRS geocentric CRS} using
* {@linkplain org.opengis.referencing.cs.CartesianCS cartesian CS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>geocentric Z</cite>" and the abbreviation is upper case
* "<var>Z</var>".
*
* This axis is usually part of a {@link #GEOCENTRIC_X}, {@link #GEOCENTRIC_Y},
* {@link #GEOCENTRIC_Z} set.
*/
public static final DefaultCoordinateSystemAxis GEOCENTRIC_Z = new DefaultCoordinateSystemAxis(
VocabularyKeys.GEOCENTRIC_Z, "Z", AxisDirection.NORTH, SI.METER);
/**
* Default axis info for Easting values in a
* {@linkplain org.opengis.referencing.crs.ProjectedCRS projected CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#EAST East}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>easting</cite>" and the abbreviation is upper case
* "<var>E</var>".
*
* This axis is usually part of a {@link #EASTING}, {@link #NORTHING} set.
*
* @see #X
* @see #EASTING
* @see #WESTING
*/
public static final DefaultCoordinateSystemAxis EASTING = new DefaultCoordinateSystemAxis(
VocabularyKeys.EASTING, "E", AxisDirection.EAST, SI.METER);
/**
* Default axis info for Westing values in a
* {@linkplain org.opengis.referencing.crs.ProjectedCRS projected CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#WEST West}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>westing</cite>" and the abbreviation is upper case
* "<var>W</var>".
*
* @see #X
* @see #EASTING
* @see #WESTING
*/
public static final DefaultCoordinateSystemAxis WESTING = new DefaultCoordinateSystemAxis(
VocabularyKeys.WESTING, "W", AxisDirection.WEST, SI.METER);
static {
EASTING.opposite = WESTING;
WESTING.opposite = EASTING;
}
/**
* Default axis info for Northing values in a
* {@linkplain org.opengis.referencing.crs.ProjectedCRS projected CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#NORTH North}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>northing</cite>" and the abbreviation is upper case
* "<var>N</var>".
*
* This axis is usually part of a {@link #EASTING}, {@link #NORTHING} set.
*
* @see #Y
* @see #NORTHING
* @see #SOUTHING
*/
public static final DefaultCoordinateSystemAxis NORTHING = new DefaultCoordinateSystemAxis(
VocabularyKeys.NORTHING, "N", AxisDirection.NORTH, SI.METER);
/**
* Default axis info for Southing values in a
* {@linkplain org.opengis.referencing.crs.ProjectedCRS projected CRS}.
*
* Increasing ordinates values go {@linkplain AxisDirection#SOUTH South}
* and units are {@linkplain SI#METER metres}.
*
* The ISO 19111 name is "<cite>southing</cite>" and the abbreviation is upper case
* "<var>S</var>".
*
* @see #Y
* @see #NORTHING
* @see #SOUTHING
*/
public static final DefaultCoordinateSystemAxis SOUTHING = new DefaultCoordinateSystemAxis(
VocabularyKeys.SOUTHING, "S", AxisDirection.SOUTH, SI.METER);
static {
NORTHING.opposite = SOUTHING;
SOUTHING.opposite = NORTHING;
}
/**
* A default axis for time values in a {@linkplain org.opengis.referencing.cs.TimeCS time CS}.
*
* Increasing time go toward {@linkplain AxisDirection#FUTURE future}
* and units are {@linkplain NonSI#DAY days}.
*
* The abbreviation is lower case "<var>t</var>".
*/
public static final DefaultCoordinateSystemAxis TIME = new DefaultCoordinateSystemAxis(
VocabularyKeys.TIME, "t", AxisDirection.FUTURE, NonSI.DAY);
/**
* A default axis for column indices in a {@linkplain org.opengis.coverage.grid.GridCoverage
* grid coverage}. Increasing values go toward {@linkplain AxisDirection#COLUMN_POSITIVE
* positive column number}.
*
* The abbreviation is lower case "<var>i</var>".
*/
public static final DefaultCoordinateSystemAxis COLUMN = new DefaultCoordinateSystemAxis(
VocabularyKeys.COLUMN, "i", AxisDirection.COLUMN_POSITIVE, Unit.ONE);
/**
* A default axis for row indices in a {@linkplain org.opengis.coverage.grid.GridCoverage grid
* coverage}. Increasing values go toward {@linkplain AxisDirection#ROW_POSITIVE positive row
* number}.
*
* The abbreviation is lower case "<var>j</var>".
*/
public static final DefaultCoordinateSystemAxis ROW = new DefaultCoordinateSystemAxis(
VocabularyKeys.ROW, "j", AxisDirection.ROW_POSITIVE, Unit.ONE);
/**
* A default axis for <var>x</var> values in a display device. Increasing values go toward
* {@linkplain AxisDirection#DISPLAY_RIGHT display right}.
*
* The abbreviation is lower case "<var>x</var>".
*
* @since 2.2
*/
public static final DefaultCoordinateSystemAxis DISPLAY_X = new DefaultCoordinateSystemAxis(
-1, "x", AxisDirection.DISPLAY_RIGHT, Unit.ONE);
/**
* A default axis for <var>y</var> values in a display device. Increasing values go toward
* {@linkplain AxisDirection#DISPLAY_DOWN display down}.
*
* The abbreviation is lower case "<var>y</var>".
*
* @since 2.2
*/
public static final DefaultCoordinateSystemAxis DISPLAY_Y = new DefaultCoordinateSystemAxis(
-1, "y", AxisDirection.DISPLAY_DOWN, Unit.ONE);
/**
* Some names to be treated as equivalent. This is needed because axis names are the primary
* way to distinguish between {@link CoordinateSystemAxis} instances. Those names are strictly
* defined by ISO 19111 as "Geodetic latitude" and "Geodetic longitude" among others, but the
* legacy WKT specifications from OGC 01-009 defined the names as "Lon" and "Lat" for the same
* axis.
* <p>
* Keys in this map are names <strong>in lower cases</strong>. Values are the axis that the
* name is for. The actual axis instance doesn't matter (the algorithm using this map should
* work for any axis instance); it is just a way to differentiate latitude and longitude.
*/
private static final Map<String,CoordinateSystemAxis> ALIASES =
new HashMap<String,CoordinateSystemAxis>(12);
static {
ALIASES.put("lat", GEODETIC_LATITUDE);
ALIASES.put("latitude", GEODETIC_LATITUDE);
ALIASES.put("geodetic latitude", GEODETIC_LATITUDE);
ALIASES.put("lon", GEODETIC_LONGITUDE);
ALIASES.put("long", GEODETIC_LONGITUDE);
ALIASES.put("longitude", GEODETIC_LONGITUDE);
ALIASES.put("geodetic longitude", GEODETIC_LONGITUDE);
/*
* "x" and "y" are sometime used in WKT for meaning "Easting" and "Northing".
* We could be tempted to add them as alias in this map, but experience shows
* that such alias have a lot of indesirable side effet. "x" and "y" are used
* for too many things ("Easting", "Westing", "Geocentric X", "Display right",
* "Display left", etc.) and declaring them as alias introduces confusion in
* AbstractCS constructor (during the check of axis directions), in
* PredefinedCS.standard(CoordinateSystem), etc.
*/
}
/**
* Special cases for "x" and "y" names. "x" is considered equivalent to "Easting"
* or "Westing", but the converse is not true. Note: by avoiding to put "x" in the
* {@link #ALIASES} map, we avoid undesirable side effects like considering "Easting"
* as equivalent to "Westing".
*
* @param xy The name which may be "x" or "y".
* @param name The second name to compare with.
* @return {@code true} if the second name is equivalent to "x" or "y" (depending on
* the {@code xy} value), or {@code false} otherwise.
*/
private static boolean nameMatchesXY(String xy, final String name) {
xy = xy.trim();
if (xy.length() == 1) {
final DefaultCoordinateSystemAxis axis;
switch (Character.toLowerCase(xy.charAt(0))) {
case 'x': axis=EASTING; break;
case 'y': axis=NORTHING; break;
default : return false;
}
return axis.nameMatches(name) || axis.getOpposite().nameMatches(name);
}
return false;
}
/**
* The abbreviation used for this coordinate system axes. This abbreviation is also
* used to identify the ordinates in coordinate tuple. Examples are "<var>X</var>"
* and "<var>Y</var>".
*/
private final String abbreviation;
/**
* Direction of this coordinate system axis. In the case of Cartesian projected
* coordinates, this is the direction of this coordinate system axis locally.
*/
private final AxisDirection direction;
/**
* The unit of measure used for this coordinate system axis.
*/
private final Unit<?> unit;
/**
* Minimal and maximal value for this axis.
*/
private final double minimum, maximum;
/**
* The range meaning for this axis.
*/
private final RangeMeaning rangeMeaning;
/**
* The axis with opposite direction, or {@code null} if unknow.
* Not serialized because only used for the predefined constants.
*/
private transient DefaultCoordinateSystemAxis opposite;
/**
* Constructs a new coordinate system axis 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 axis The coordinate system axis to copy.
*
* @since 2.2
*/
public DefaultCoordinateSystemAxis(final CoordinateSystemAxis axis) {
super(axis);
abbreviation = axis.getAbbreviation();
direction = axis.getDirection();
unit = axis.getUnit();
minimum = axis.getMinimumValue();
maximum = axis.getMaximumValue();
rangeMeaning = axis.getRangeMeaning();
}
/**
* Constructs an axis from a set of properties. The properties map is given unchanged to the
* {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map) super-class constructor}.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param abbreviation The {@linkplain #getAbbreviation abbreviation} used for this
* coordinate system axes.
* @param direction The {@linkplain #getDirection direction} of this coordinate system axis.
* @param unit The {@linkplain #getUnit unit of measure} used for this coordinate
* system axis.
* @param minimum The minimum value normally allowed for this axis.
* @param maximum The maximum value normally allowed for this axis.
* @param rangeMeaning The meaning of axis value range specified by the minimum and
* maximum values.
*
* @since 2.3
*/
public DefaultCoordinateSystemAxis(final Map<String,?> properties,
final String abbreviation,
final AxisDirection direction,
final Unit<?> unit,
final double minimum,
final double maximum,
final RangeMeaning rangeMeaning)
{
super(properties);
this.abbreviation = abbreviation;
this.direction = direction;
this.unit = unit;
this.minimum = minimum;
this.maximum = maximum;
this.rangeMeaning = rangeMeaning;
ensureNonNull("abbreviation", abbreviation);
ensureNonNull("direction", direction);
ensureNonNull("unit", unit);
ensureNonNull("rangeMeaning", rangeMeaning);
if (!(minimum < maximum)) { // Use '!' for catching NaN
throw new IllegalArgumentException(Errors.format(ErrorKeys.BAD_RANGE_$2, minimum, maximum));
}
}
/**
* Constructs an unbounded axis from a set of properties. The properties map is given
* unchanged to the {@linkplain AbstractIdentifiedObject#AbstractIdentifiedObject(Map)
* super-class constructor}. The {@linkplain #getMinimumValue minimum} and
* {@linkplain #getMaximumValue maximum} values are inferred from the axis unit and
* direction.
*
* @param properties Set of properties. Should contains at least {@code "name"}.
* @param abbreviation The {@linkplain #getAbbreviation abbreviation} used for this
* coordinate system axes.
* @param direction The {@linkplain #getDirection direction} of this coordinate system axis.
* @param unit The {@linkplain #getUnit unit of measure} used for this coordinate
* system axis.
*/
public DefaultCoordinateSystemAxis(final Map<String,?> properties,
final String abbreviation,
final AxisDirection direction,
final Unit<?> unit)
{
// NOTE: we would invoke this(properties, abbreviation, ...) instead if Sun fixed
// RFE #4093999 ("Relax constraint on placement of this()/super() call in constructors").
super(properties);
this.abbreviation = abbreviation;
this.direction = direction;
this.unit = unit;
ensureNonNull("abbreviation", abbreviation);
ensureNonNull("direction", direction);
ensureNonNull("unit", unit);
if (unit.isCompatible(NonSI.DEGREE_ANGLE)) {
final UnitConverter fromDegrees = NonSI.DEGREE_ANGLE.getConverterTo(unit);
final AxisDirection dir = direction.absolute();
if (dir.equals(AxisDirection.NORTH)) {
final double range = Math.abs(fromDegrees.convert(90));
minimum = -range;
maximum = +range;
rangeMeaning = RangeMeaning.EXACT; // 90°N do not wraps to 90°S
return;
}
if (dir.equals(AxisDirection.EAST)) {
final double range = Math.abs(fromDegrees.convert(180));
minimum = -range;
maximum = +range;
rangeMeaning = RangeMeaning.WRAPAROUND; // 180°E wraps to 180°W
return;
}
}
minimum = Double.NEGATIVE_INFINITY;
maximum = Double.POSITIVE_INFINITY;
rangeMeaning = RangeMeaning.EXACT;
}
/**
* Constructs an axis with the same {@linkplain #getName name} as the abbreviation.
*
* @param abbreviation The {@linkplain #getAbbreviation abbreviation} used for this
* coordinate system axes.
* @param direction The {@linkplain #getDirection direction} of this coordinate system axis.
* @param unit The {@linkplain #getUnit unit of measure} used for this coordinate
* system axis.
*/
public DefaultCoordinateSystemAxis(final String abbreviation,
final AxisDirection direction,
final Unit<?> unit)
{
this(Collections.singletonMap(NAME_KEY, abbreviation), abbreviation, direction, unit);
}
/**
* Constructs an axis with a name as an {@linkplain InternationalString international string}
* and an abbreviation. The {@linkplain #getName name of this identified object} is set to the
* unlocalized version of the {@code name} argument, as given by
* <code>name.{@linkplain InternationalString#toString(Locale) toString}(null)</code>. The
* same {@code name} argument is also stored as an {@linkplain #getAlias alias}, which
* allows fetching localized versions of the name.
*
* @param name The name of this axis. Also stored as an alias for localization purpose.
* @param abbreviation The {@linkplain #getAbbreviation abbreviation} used for this
* coordinate system axis.
* @param direction The {@linkplain #getDirection direction} of this coordinate system axis.
* @param unit The {@linkplain #getUnit unit of measure} used for this coordinate
* system axis.
*/
public DefaultCoordinateSystemAxis(final InternationalString name,
final String abbreviation,
final AxisDirection direction,
final Unit<?> unit)
{
this(toMap(name), abbreviation, direction, unit);
}
/**
* Work around for RFE #4093999 in Sun's bug database
* ("Relax constraint on placement of this()/super() call in constructors").
*/
private static Map<String,Object> toMap(final InternationalString name) {
final Map<String,Object> properties = new HashMap<String,Object>(4);
if (name != null) {
// The "null" locale argument is required for getting the unlocalized version.
properties.put(NAME_KEY, name.toString(null));
properties.put(ALIAS_KEY, NameFactory.create(new InternationalString[] {name}));
}
return properties;
}
/**
* Constructs an axis with a name and an abbreviation as a resource bundle key.
* To be used for construction of pre-defined constants only.
*
* @param name The resource bundle key for the name.
* @param abbreviation The {@linkplain #getAbbreviation abbreviation} used for this
* coordinate system axes.
* @param direction The {@linkplain #getDirection direction} of this coordinate system axis.
* @param unit The {@linkplain #getUnit unit of measure} used for this coordinate
* system axis.
*/
private DefaultCoordinateSystemAxis(final int name,
final String abbreviation,
final AxisDirection direction,
final Unit<?> unit)
{
this(name>=0 ? Vocabulary.formatInternational(name) :
new SimpleInternationalString(abbreviation), abbreviation, direction, unit);
PREDEFINED[PREDEFINED_COUNT++] = this;
}
/**
* Returns one of the predefined axis for the given name and direction, or {@code null} if
* none. This method searchs only in predefined constants like {@link #GEODETIC_LATITUDE},
* not in any custom axis instantiated by a public constructor. The name of those constants
* match ISO 19111 names or some names commonly found in <cite>Well Known Text</cite> (WKT)
* formats.
* <p>
* This method first checks if the specified name matches the {@linkplain #getAbbreviation
* abbreviation} of a predefined axis. The comparaison is case-sensitive (for example the
* {@link #GEOCENTRIC_X} abbreviation is uppercase {@code "X"}, while the abbreviation for
* the generic {@link #X} axis is lowercase {@code "x"}).
* <p>
* If the specified name doesn't match any abbreviation, then this method compares the name
* against predefined axis {@linkplain #getName name} in a case-insensitive manner. Examples
* of valid names are "<cite>Geodetic latitude</cite>" and "<cite>Northing</cite>".
* <p>
* The direction argument is optional and can be used in order to resolve ambiguity like
* {@link #X} and {@link #DISPLAY_X} axis. If this argument is {@code null}, then the first
* axis with a matching name or abbreviation will be returned.
*
* @param name The axis name or abbreviation.
* @param direction An optional direction, or {@code null}.
* @return One of the constants declared in this class, or {@code null}.
*
* @since 2.4
*/
public static DefaultCoordinateSystemAxis getPredefined(String name, AxisDirection direction) {
ensureNonNull("name", name);
name = name.trim();
DefaultCoordinateSystemAxis found = null;
for (int i=0; i<PREDEFINED_COUNT; i++) {
final DefaultCoordinateSystemAxis candidate = PREDEFINED[i];
if (direction != null && !direction.equals(candidate.getDirection())) {
continue;
}
// Reminder: case matter for abbreviation, so 'equalsIgnoreCase' is not allowed.
if (candidate.abbreviation.equals(name)) {
return candidate;
}
if (found == null && candidate.nameMatches(name)) {
/*
* We need to perform a special check for Geodetic longitude and latitude.
* Because of the ALIAS map, the "Geodetic latitude" and "Latitude" names
* are considered equivalent, while they are two distinct predefined axis
* constants in Geotools. Because Geodetic longitude & latitude constants
* are declared first, they have precedence. So we prevent the selection
* of GEODETIC_LATITUDE if the user is likely to ask for LATITUDE.
*/
if (candidate == GEODETIC_LONGITUDE || candidate == GEODETIC_LATITUDE) {
if (!name.toLowerCase().startsWith("geodetic")) {
continue;
}
}
found = candidate;
}
}
return found;
}
/**
* Returns a predefined axis similar to the specified one except for units.
* Returns {@code null} if no predefined axis match.
*/
static DefaultCoordinateSystemAxis getPredefined(final CoordinateSystemAxis axis) {
return getPredefined(axis.getName().getCode(), axis.getDirection());
}
/**
* Returns the list of all predefined constants.
* Currently used for testing purpose only.
*/
static DefaultCoordinateSystemAxis[] values() {
return PREDEFINED.clone();
}
/**
* Returns an axis direction constants from its name.
*
* @param direction The direction name (e.g. "north", "east", etc.).
* @return The axis direction for the given name.
* @throws NoSuchElementException if the given name is not a know axis direction.
*/
public static AxisDirection getDirection(String direction) throws NoSuchElementException {
ensureNonNull("direction", direction);
direction = direction.trim();
AxisDirection candidate = DirectionAlongMeridian.findDirection(direction);
if (candidate != null) {
return candidate;
}
/*
* Some EPSG direction names are of the form "South along 180 deg". We check that the
* direction before "along" is valid and create a new axis direction if it is. We can
* not just replace "South along 180 deg" by "South" because the same CRS may use two
* of those directions. For example EPSG:32661 has the following axis direction:
*
* South along 180 deg
* South along 90 deg East
*/
final DirectionAlongMeridian meridian = DirectionAlongMeridian.parse(direction);
if (meridian != null) {
candidate = meridian.getDirection();
assert candidate == DirectionAlongMeridian.findDirection(meridian.toString());
return candidate;
}
throw new NoSuchElementException(
Errors.format(ErrorKeys.UNKNOW_AXIS_DIRECTION_$1, direction));
}
/**
* Direction of this coordinate system axis. In the case of Cartesian projected
* coordinates, this is the direction of this coordinate system axis locally.
* Examples:
* {@linkplain AxisDirection#NORTH north} or {@linkplain AxisDirection#SOUTH south},
* {@linkplain AxisDirection#EAST east} or {@linkplain AxisDirection#WEST west},
* {@linkplain AxisDirection#UP up} or {@linkplain AxisDirection#DOWN down}.
*
* <P>Within any set of coordinate system axes, only one of each pair of terms
* can be used. For earth-fixed coordinate reference systems, this direction is often
* approximate and intended to provide a human interpretable meaning to the axis. When a
* geodetic datum is used, the precise directions of the axes may therefore vary slightly
* from this approximate direction.</P>
*
* <P>Note that an {@link org.geotools.referencing.crs.DefaultEngineeringCRS} often requires
* specific descriptions of the directions of its coordinate system axes.</P>
*/
public AxisDirection getDirection() {
return direction;
}
/**
* The abbreviation used for this coordinate system axes. This abbreviation is also
* used to identify the ordinates in coordinate tuple. Examples are "<var>X</var>"
* and "<var>Y</var>".
*
* @return The coordinate system axis abbreviation.
*/
public String getAbbreviation() {
return abbreviation;
}
/**
* The unit of measure used for this coordinate system axis. The value of this
* coordinate in a coordinate tuple shall be recorded using this unit of measure,
* whenever those coordinates use a coordinate reference system that uses a
* coordinate system that uses this axis.
*/
public Unit<?> getUnit() {
return unit;
}
/**
* Returns the minimum value normally allowed for this axis, in the
* {@linkplain #getUnit unit of measure for the axis}. If there is no minimum value, then
* this method returns {@linkplain Double#NEGATIVE_INFINITY negative infinity}.
*
* @since 2.3
*/
public double getMinimumValue() {
return minimum;
}
/**
* Returns the maximum value normally allowed for this axis, in the
* {@linkplain #getUnit unit of measure for the axis}. If there is no maximum value, then
* this method returns {@linkplain Double#POSITIVE_INFINITY negative infinity}.
*
* @since 2.3
*/
public double getMaximumValue() {
return maximum;
}
/**
* Returns the meaning of axis value range specified by the {@linkplain #getMinimumValue
* minimum} and {@linkplain #getMaximumValue maximum} values. This element shall be omitted
* when both minimum and maximum values are omitted. It may be included when minimum and/or
* maximum values are included. If this element is omitted when minimum or maximum values are
* included, the meaning is unspecified.
*
* @since 2.3
*/
public RangeMeaning getRangeMeaning() {
return rangeMeaning;
}
/**
* Returns an axis with the opposite direction of this one, or {@code null} if unknown.
* This method is not public because only a few predefined constants have this information.
*/
final DefaultCoordinateSystemAxis getOpposite() {
return opposite;
}
/**
* Returns {@code true} if the specified direction is a compass direction.
* Compass directions include "<cite>North</cite>", "<cite>North-North-East</cite>",
* "<cite>North-East</cite>", <cite>etc.</cite>
*
* @param direction The axis direction to test.
* @return {@code true} if the given direction is a compass direction.
*
* @since 2.4
*/
public static boolean isCompassDirection(final AxisDirection direction) {
ensureNonNull("direction", direction);
final int n = direction.ordinal() - AxisDirection.NORTH.ordinal();
return n >= 0 && n < COMPASS_DIRECTION_COUNT;
}
/*
* Returns {@code true} if the specified direction is a direction along a meridian.
* Those directions are used in coordinate systems for polar area. Examples:
* "<cite>North along 90 deg East</cite>", "<cite>North along 0 deg</cite>".
*
* We do not provide such method yet. If we want this functionality, maybe we should
* consider making DirectionAlongMeridian a public class extending AxisDirection code
* list instead.
*/
// public static boolean isDirectionAlongMeridian(final AxisDirection direction);
/**
* Returns the arithmetic (counterclockwise) angle from the first direction to the second
* direction, in decimal <strong>degrees</strong>. This method returns a value between
* -180° and +180°, or {@link Double#NaN NaN} if no angle can be computed.
* <p>
* A positive angle denotes a right-handed system, while a negative angle denotes
* a left-handed system. Example:
* <p>
* <ul>
* <li>The angle from {@linkplain AxisDirection#EAST EAST} to
* {@linkplain AxisDirection#NORTH NORTH} is 90°</li>
* <li>The angle from {@linkplain AxisDirection#SOUTH SOUTH} to
* {@linkplain AxisDirection#WEST WEST} is -90°</li>
* <li>The angle from "<cite>North along 90 deg East</cite>" to
* "<cite>North along 0 deg</cite>" is 90°.</li>
* </ul>
*
* @param source The source axis direction.
* @param target The target axis direction.
* @return The arithmetic angle (in degrees) of the rotation to apply on a line pointing toward
* the source direction in order to make it point toward the target direction, or
* {@link Double#NaN} if this value can't be computed.
*
* @since 2.4
*/
public static double getAngle(final AxisDirection source, final AxisDirection target) {
ensureNonNull("source", source);
ensureNonNull("target", target);
// Tests for NORTH, SOUTH, EAST, EAST-NORTH-EAST, etc. directions.
final int compass = getCompassAngle(source, target);
if (compass != Integer.MIN_VALUE) {
return compass * (360.0 / COMPASS_DIRECTION_COUNT);
}
// Tests for "South along 90 deg East", etc. directions.
final DirectionAlongMeridian src = DirectionAlongMeridian.parse(source);
if (src != null) {
final DirectionAlongMeridian tgt = DirectionAlongMeridian.parse(target);
if (tgt != null) {
return src.getAngle(tgt);
}
}
return Double.NaN;
}
/**
* Tests for angle on compass only (do not tests angle between direction along meridians).
* Returns {@link Integer#MIN_VALUE} if the angle can't be computed.
*/
static int getCompassAngle(final AxisDirection source, final AxisDirection target) {
final int base = AxisDirection.NORTH.ordinal();
final int src = source.ordinal() - base;
if (src >= 0 && src < COMPASS_DIRECTION_COUNT) {
int tgt = target.ordinal() - base;
if (tgt >= 0 && tgt < COMPASS_DIRECTION_COUNT) {
tgt = src - tgt;
if (tgt < -COMPASS_DIRECTION_COUNT/2) {
tgt += COMPASS_DIRECTION_COUNT;
} else if (tgt > COMPASS_DIRECTION_COUNT/2) {
tgt -= COMPASS_DIRECTION_COUNT;
}
return tgt;
}
}
return Integer.MIN_VALUE;
}
/**
* Returns {@code true} if the specified directions are perpendicular.
*
* @param first The first axis direction to test.
* @param second The second axis direction to test.
* @return {@code true} if the given axis direction are perpendicular.
*
* @since 2.4
*/
public static boolean perpendicular(final AxisDirection first, final AxisDirection second) {
return Math.abs(Math.abs(getAngle(first, second)) - 90) <= DirectionAlongMeridian.EPS;
}
/**
* Returns a new axis with the same properties than current axis except for the units.
*
* @param newUnit The unit for the new axis.
* @return An axis using the specified unit.
* @throws IllegalArgumentException If the specified unit is incompatible with the expected one.
*/
final DefaultCoordinateSystemAxis usingUnit(final Unit<?> newUnit)
throws IllegalArgumentException
{
if (unit.equals(newUnit)) {
return this;
}
if (unit.isCompatible(newUnit)) {
return new DefaultCoordinateSystemAxis(getProperties(this, null),
abbreviation, direction, newUnit, minimum, maximum, rangeMeaning);
}
throw new IllegalArgumentException(Errors.format(ErrorKeys.INCOMPATIBLE_UNIT_$1, newUnit));
}
/**
* Returns {@code true} if either the {@linkplain #getName() primary name} or at least
* one {@linkplain #getAlias alias} matches the specified string. This method performs
* all the searh done by the {@linkplain AbstractIdentifiedObject#nameMatches(String)
* super-class}, with the addition of special processing for latitudes and longitudes:
* <p>
* <ul>
* <li>{@code "Lat"}, {@code "Latitude"} and {@code "Geodetic latitude"} are considered
* equivalent.</li>
* <li>{@code "Lon"}, {@code "Longitude"} and {@code "Geodetic longitude"} are considered
* equivalent.</li>
* </ul>
* <p>
* The above special cases are needed in order to workaround a conflict in specifications:
* ISO 19111 explicitly state that the latitude and longitude axis names shall be
* "Geodetic latitude" and "Geodetic longitude", will legacy OGC 01-009 (where WKT is defined)
* said that the default values shall be "Lat" and "Lon".
*
* @param name The name to compare.
* @return {@code true} if the primary name of at least one alias
* matches the specified {@code name}.
*/
@Override
public boolean nameMatches(final String name) {
if (super.nameMatches(name)) {
return true;
}
/*
* The standard comparaisons didn't worked. Check for the aliases. Note: we don't
* test for 'nameMatchesXY(...)' here because the "x" and "y" axis names are too
* generic. We test them only in the 'equals' method, which has the extra-safety
* of units comparaison (so less risk to treat incompatible axis as equivalent).
*
* TODO: replace Object by CoordinateSystemAxis when we will be allowed
* to compile for J2SE 1.5.
*/
final Object type = ALIASES.get(name.trim().toLowerCase());
return (type != null) && (type == ALIASES.get(getName().getCode().trim().toLowerCase()));
}
/**
* Compares the specified object with this axis for equality.
*
* @param object The object to compare to {@code this}.
* @param compareMetadata {@code true} for performing a strict comparaison, or
* {@code false} for comparing only properties relevant to transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final AbstractIdentifiedObject object, final boolean compareMetadata) {
if (object == this) {
return true; // Slight optimization.
}
if (super.equals(object, compareMetadata)) {
return equals((DefaultCoordinateSystemAxis) object, compareMetadata, true);
}
return false;
}
/**
* Compares the specified object with this axis for equality, with optional comparaison
* of units. Units should always be compared (they are not just metadata), except in the
* particular case of {@link AbstractCS#axisColinearWith}, which is used as a first step
* toward units conversions through {@link AbstractCS#swapAndScaleAxis}.
*/
final boolean equals(final DefaultCoordinateSystemAxis that,
final boolean compareMetadata, final boolean compareUnit)
{
if (compareMetadata) {
if (!Utilities.equals(this.abbreviation, that.abbreviation) ||
!Utilities.equals(this.rangeMeaning, that.rangeMeaning) ||
Double.doubleToLongBits(minimum) != Double.doubleToLongBits(that.minimum) ||
Double.doubleToLongBits(maximum) != Double.doubleToLongBits(that.maximum))
{
return false;
}
} else {
/*
* Checking the abbreviation is not suffisient. For example the polar angle and the
* spherical latitude have the same abbreviation (theta). Geotools extensions like
* "Longitude" (in addition of ISO 19111 "Geodetic longitude") bring more potential
* confusion. Furthermore, not all implementors will use the greek letters (even if
* they are part of ISO 19111). For example most CRS in WKT format use the "Lat"
* abbreviation instead of the greek letter phi. For comparaisons without metadata,
* we ignore the unreliable abbreviation and check the axis name instead. These
* names are constrained by ISO 19111 specification (see class javadoc), so they
* should be reliable enough.
*
* Note: there is no need to execute this block if 'compareMetadata' is true,
* because in this case a stricter check has already been performed by
* the 'equals' method in the superclass.
*/
final String thatName = that.getName().getCode();
if (!nameMatches(thatName)) {
// The above test checked for special cases ("Lat" / "Lon" aliases, etc.).
// The next line may not, but is tested anyway in case the user overrided
// the 'that.nameMatches(...)' method.
final String thisName = getName().getCode();
if (!nameMatches(that, thisName)) {
// For the needs of AbstractCS.axisColinearWith(...), we must stop here.
// In addition it may be safer to not test 'nameMatchesXY' when we don't
// have the extra-safety of units comparaison, because "x" and "y" names
// are too generic.
if (!compareUnit) {
return false;
}
// Last chance: check for the special case of "x" and "y" axis names.
if (!nameMatchesXY(thatName, thisName) && !nameMatchesXY(thisName, thatName)) {
return false;
}
}
}
}
return Utilities.equals(this.direction, that.direction) &&
(!compareUnit || Utilities.equals(this.unit, that.unit));
}
/**
* Returns a hash value for this axis. This value doesn't need to be the same
* in past or future versions of this class.
*/
@Override
public int hashCode() {
int code = (int)serialVersionUID;
code = code*37 + abbreviation.hashCode();
code = code*37 + direction .hashCode();
code = code*37 + unit .hashCode();
return code;
}
/**
* Format the inner part of a
* <A HREF="http://geoapi.sourceforge.net/snapshot/javadoc/org/opengis/referencing/doc-files/WKT.html"><cite>Well
* Known Text</cite> (WKT)</A> element. WKT is returned by the {@link #toString toString} method
* and looks like <code>AXIS["name",NORTH]</code>.
*
* @param formatter The formatter to use.
* @return The WKT element name, which is "AXIS".
*/
@Override
protected String formatWKT(final Formatter formatter) {
formatter.append(direction);
return "AXIS";
}
}