# Class CoordinateSystems

Object
CoordinateSystems

public final class CoordinateSystems extends Object
Utility methods working on `CoordinateSystem` objects and their axes. Those methods allow for example to estimate an angle between two axes or determining the change of axis directions and units between two coordinate systems.

Since:
  0.4

- ## Method Summary

  Modifier and Type
  Method
  Description
  `static Angle`
  `angle(AxisDirection source, AxisDirection target)`
  Returns the arithmetic (counterclockwise) angle from the first axis direction to the second direction.
  `static AxisDirection`
  `directionAlongMeridian(AxisDirection baseDirection, double meridian)`
  Returns an axis direction from a pole along a meridian.
  `static AxisDirection[]`
  `getAxisDirections(CoordinateSystem cs)`
  Returns the axis directions for the specified coordinate system.
  `static Integer`
  `getEpsgCode(Class<? extends CoordinateSystem> type, CoordinateSystemAxis... axes)`
  Returns the EPSG code of a coordinate system using the units and directions of given axes.
  `static Integer`
  `getEpsgCode(Unit<?> unit, AxisDirection... directions)`
  Returns the EPSG code of a coordinate system using the given unit and axis directions.
  `static String`
  `getShortName(CoordinateSystemAxis axis, Locale locale)`
  Returns a short (if possible) localized name for the given axis.
  `static AxisDirection[]`
  `getSimpleAxisDirections(CoordinateSystem cs)`
  Returns the axis directions, replacing "North/South along meridian" by a cardinal direction.
  `static List<CoordinateSystem>`
  `getSingleComponents(CoordinateSystem cs)`
  Returns all components of the given coordinate system.
  `static boolean`
  `isAlongMeridian(AxisDirection direction)`
  Returns `true` if the given axis direction seems to be a direction along a meridian.
  `static boolean`
  `isGeodetic(CoordinateSystem cs)`
  Returns whether the given coordinate system can be associated to a `GeodeticCRS`.
  `static AxisDirection`
  `parseAxisDirection(String name)`
  Returns an axis direction code from the given direction name.
  `static CoordinateSystem`
  `replaceAngularUnit(CoordinateSystem cs, Unit<Angle> newUnit)`
  Returns a coordinate system derived from the given one but with all angular units replaced by the given unit.
  `static CoordinateSystem`
  `replaceAxes(CoordinateSystem cs, AxisFilter filter)`
  Returns a coordinate system derived from the given one but with a modified list of axes.
  `static CoordinateSystem`
  `replaceLinearUnit(CoordinateSystem cs, Unit<Length> newUnit)`
  Returns a coordinate system derived from the given one but with all linear units replaced by the given unit.
  `static Matrix`
  `swapAndScaleAxes(CoordinateSystem sourceCS, CoordinateSystem targetCS)`
  Returns an affine transform between two coordinate systems.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait`

- ## Method Details

  - ### isGeodetic

    public static boolean isGeodetic(CoordinateSystem cs)
    Returns whether the given coordinate system can be associated to a `GeodeticCRS`. This is true for instances of `EllipsoidalCS`, `CartesianCS` and `SphericalCS`, and false for all other types of coordinate system.
    Parameters:
      `cs` - the coordinate system to test (can be `null`).
    Returns:
      whether the given coordinate system can be associated to a geodetic CRS.
    Since:
      1.3
    See Also:
      - `getSingleComponents(CoordinateSystem)`

  - ### parseAxisDirection

    public static AxisDirection parseAxisDirection(String name) throws IllegalArgumentException
    Returns an axis direction code from the given direction name. Names are case-insensitive. They may be:
    - Cardinal directions like north and east.
    - Inter-cardinal directions north-east and south-south-east, using either `'-'`, `'_'` or spaces as separator between the cardinal points.
    - Directions from a pole like South along 180 deg and South along 90° East, using either the `"deg"` or `"°"` symbol. Note that the meridian is not necessarily relative to Greenwich (see `directionAlongMeridian(…)` for more information).
    Parameters:
      `name` - the direction name (e.g. "north", "north-east", etc.).
    Returns:
      the axis direction for the given name.
    Throws:
      `IllegalArgumentException` - if the given name is not a known axis direction.

  - ### directionAlongMeridian

    public static AxisDirection directionAlongMeridian(AxisDirection baseDirection, double meridian)
    Returns an axis direction from a pole along a meridian. The given meridian is usually, but not necessarily, relative to the Greenwich meridian.

    #### Reference meridian

    The reference meridian depends on the context. It is usually the prime meridian of the geodetic reference frame of the geographic CRS instance that contains (through its coordinate system) the axes having those directions. This policy is consistent with WKT 2 specification.

    #### Example

    `directionAlongMeridian(AxisDirection​.SOUTH, -90)` returns an axis direction for South along 90°W.
    Parameters:
      `baseDirection` - the base direction, which must be `AxisDirection​.NORTH` or `AxisDirection​.SOUTH`.
      `meridian` - the meridian in degrees, relative to a unspecified (usually Greenwich) prime meridian. Meridians in the East hemisphere are positive and meridians in the West hemisphere are negative.
    Returns:
      the axis direction along the given meridian.
    Since:
      0.6

  - ### isAlongMeridian

    public static boolean isAlongMeridian(AxisDirection direction)
    Returns `true` if the given axis direction seems to be a direction along a meridian.

    Examples
    Axis name | Return value
    --- | ---
    North along 90 deg East | `true`
    South along 90 deg East | `true`
    South | `false`
    East | `false`

    Note that `true` is not a guarantee that `parseAxisDirection(String)` will succeed. But it means that there is reasonable chances of success based on brief inspection of axis name.
    Parameters:
      `direction` - the direction to test. Can be null.
    Returns:
      if the given direction is non-null and seems to be a direction along a meridian.
    Since:
      1.2

  - ### angle

    public static Angle angle(AxisDirection source, AxisDirection target)
    Returns the arithmetic (counterclockwise) angle from the first axis direction to the second direction. This method returns a value between -180° and +180°, or `null` if no angle can be computed.

    #### Horizontal directions

    For any pair of compass directions which are not opposite directions, a positive angle denotes a right-handed system while a negative angle denotes a left-handed system. Examples:
    - The angle from `EAST` to `NORTH` is 90°
    - The angle from `SOUTH` to `WEST` is -90°
    - The angle from North along 90° East to North along 0° is 90°.
    In the case of directions like South along 90°W, the caller is responsible to make sure that the meridians are relative to the same prime meridian. This is the case if the axes are part of the same `CoordinateSystem` instance.

    #### Horizontal and vertical directions

    By convention this method defines the angle from any compass direction to the `UP` vertical direction as 90°, and the angle of any compass direction to the `DOWN` vertical direction as -90°. The sign of those angles gives no indication about whether the coordinate system is right-handed or left-handed. Those angles are returned as instances of `ElevationAngle`.

    All angles are approximations since this method does not take the Earth ellipsoidal or geoidal shape in account.

    #### Invariants

    For any non-null return value:
    - `angle(A, A) = 0°`
    - `angle(A, opposite(A)) = ±180°`
    - `angle(A, B) = -angle(B, A)`
    Parameters:
      `source` - the source axis direction.
      `target` - the target axis direction.
    Returns:
      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 `null` if this value cannot be computed.

  - ### swapAndScaleAxes

    public static Matrix swapAndScaleAxes(CoordinateSystem sourceCS, CoordinateSystem targetCS) throws IllegalArgumentException, IncommensurableException
    Returns an affine transform between two coordinate systems. Only units and axes order (e.g. transforming from (North, West) to (East, North) are taken in account by this method.

    #### Conditions

    The two coordinate systems must implement the same GeoAPI coordinate system interface. For example if `sourceCS` is a `CartesianCS`, then `targetCS` must be a `CartesianCS` too.

    #### Example

    If coordinates in `sourceCS` are (x,y) tuples in metres and coordinates in `targetCS` are (-y,x) tuples in centimetres, then the transformation can be performed as below:

            ┌      ┐   ┌                ┐ ┌     ┐
            │-y(cm)│   │   0  -100    0 │ │ x(m)│
            │ x(cm)│ = │ 100     0    0 │ │ y(m)│
            │ 1    │   │   0     0    1 │ │ 1   │
            └      ┘   └                ┘ └     ┘

    Parameters:
      `sourceCS` - the source coordinate system.
      `targetCS` - the target coordinate system.
    Returns:
      the conversion from `sourceCS` to `targetCS` as an affine transform. Only axis direction and units are taken in account.
    Throws:
      `IllegalArgumentException` - if the CS are not of the same type, or axes do not match.
      `UnderdeterminedMatrixException` - if `targetCS` has an axis that `sourceCS` does not have.
      `IncommensurableException` - if the units are not compatible, or the conversion is non-linear.
    See Also:
      - `Matrices​.createTransform(AxisDirection[], AxisDirection[])`

  - ### replaceAxes

    public static CoordinateSystem replaceAxes(CoordinateSystem cs, AxisFilter filter)
    Returns a coordinate system derived from the given one but with a modified list of axes. The axes may be filtered (excluding some axes), reordered or have their unit and direction modified.

    #### Example

    for replacing all angular units of a coordinate system to degrees (regardless what the original angular units were) while leaving other kinds of units unchanged, one can write:

        CoordinateSystem cs = ...;
        cs = CoordinateSystems.replaceAxes(cs, new AxisFilter() {
            @Override
            public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
                if (Units.isAngular(unit)) {
                    unit = Units.DEGREE;
                }
                return unit;
            }
        });

    #### Coordinate system normalization

    This method is often used together with `swapAndScaleAxes(…)` for normalizing the coordinate values given to a math transform. For example:

        CoordinateSystem sourceCS = ...;
        CoordinateSystem targetCS = ...;
        Matrix step1 = swapAndScaleAxes(sourceCS, replaceAxes(sourceCS, AxisConvention.NORMALIZED));
        Matrix step2 = ...; // some transform working on coordinates with standard axis order and unit.
        Matrix step3 = swapAndScaleAxes(replaceAxes(targetCS, AxisConvention.NORMALIZED), targetCS);

    Parameters:
      `cs` - the coordinate system, or `null`.
      `filter` - the modifications to apply on coordinate system axes.
    Returns:
      the modified coordinate system as a new instance, or `cs` if the given coordinate system was null or does not need any change.
    Throws:
      `IllegalArgumentException` - if the specified coordinate system cannot be filtered. It may be because the coordinate system would contain an illegal number of axes, or because an axis would have an unexpected direction or unexpected unit of measurement.
    Since:
      0.6
    See Also:
      - `AxesConvention​.NORMALIZED`

  - ### replaceLinearUnit

    public static CoordinateSystem replaceLinearUnit(CoordinateSystem cs, Unit<Length> newUnit)
    Returns a coordinate system derived from the given one but with all linear units replaced by the given unit. Non-linear units (e.g. angular or scale units) are left unchanged.

    This convenience method is equivalent to the following code:

        return CoordinateSystems.replaceAxes(cs, new AxisFilter() {
            @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
                return Units.isLinear(unit) ? newUnit : unit;
            }
        });

    Parameters:
      `cs` - the coordinate system in which to replace linear units, or `null`.
      `newUnit` - the new linear unit.
    Returns:
      the modified coordinate system as a new instance, or `null` if the given `cs` was null, or `cs` if all linear units were already equal to the given one.
    Since:
      0.7
    See Also:
      - `Units​.isLinear(Unit)`

  - ### replaceAngularUnit

    public static CoordinateSystem replaceAngularUnit(CoordinateSystem cs, Unit<Angle> newUnit)
    Returns a coordinate system derived from the given one but with all angular units replaced by the given unit. Non-angular units (e.g. linear or scale units) are left unchanged.

    This convenience method is equivalent to the following code:

        return CoordinateSystems.replaceAxes(cs, new AxisFilter() {
            @Override public Unit<?> getUnitReplacement(CoordinateSystemAxis axis, Unit<?> unit) {
                return Units.isAngular(unit) ? newUnit : unit;
            }
        });

    Parameters:
      `cs` - the coordinate system in which to replace angular units, or `null`.
      `newUnit` - the new angular unit.
    Returns:
      the modified coordinate system as a new instance, or `null` if the given `cs` was null, or `cs` if all angular units were already equal to the given one.
    Since:
      0.7
    See Also:
      - `Units​.isAngular(Unit)`

  - ### getSingleComponents

    public static List<CoordinateSystem> getSingleComponents(CoordinateSystem cs)
    Returns all components of the given coordinate system. If the given coordinate system (CS) is null, then this method returns an empty list. Otherwise, if the given CS is not an instance of `DefaultCompoundCS`, then this method adds that CS as the singleton element of the returned list. Otherwise, this method returns all components. If a component is itself a `DefaultCompoundCS`, its is recursively decomposed into its singleton component.

    #### Implementation note

    This method always returns a modifiable list. Callers can freely modify that list without impacting the coordinate system.
    Parameters:
      `cs` - the coordinate system to decompose into singleton components, or `null`.
    Returns:
      the components, or an empty list if `cs` is null.
    Since:
      1.5
    See Also:
      - `isGeodetic(CoordinateSystem)`

  - ### getAxisDirections

    public static AxisDirection[] getAxisDirections(CoordinateSystem cs)
    Returns the axis directions for the specified coordinate system. This method guarantees that the returned array is non-null and does not contain any null direction.
    Parameters:
      `cs` - the coordinate system.
    Returns:
      the axis directions for the specified coordinate system.
    Throws:
      `NullPointerException` - if `cs` is null, or one of its axes is null, or a value returned by `CoordinateSystemAxis​.getDirection()` is null.
    Since:
      0.8

  - ### getSimpleAxisDirections

    public static AxisDirection[] getSimpleAxisDirections(CoordinateSystem cs)
    Returns the axis directions, replacing "North/South along meridian" by a cardinal direction. When a direction along a meridian is detected, this method uses the axis abbreviation for the direction by East or North.

    #### Example

    The WGS 84 / UPS South (E,N) coordinate reference system has two axis oriented toward North: North along 90°E and North along 0°E. Those axes are conventionally named Easting (E) and Northing (N). This method uses those conventional names for returning (east, north) directions.
    Parameters:
      `cs` - the coordinate system.
    Returns:
      the simple axis directions for the specified coordinate system.
    Throws:
      `NullPointerException` - if `cs` is null, or one of its axes or directions is null.
    Since:
      1.5

  - ### getShortName

    public static String getShortName(CoordinateSystemAxis axis, Locale locale)
    Returns a short (if possible) localized name for the given axis. This method replaces names such as "Geodetic latitude" or "Geocentric latitude" by a simple "Latitude" word. This method can be used for example in column or row headers when the context is known and the space is rare.
    Parameters:
      `axis` - the axis for which to get a short label.
      `locale` - desired locale for the label, or `null` for the default.
    Returns:
      a relatively short axis label, in the desired locale if possible.
    Since:
      1.3

  - ### getEpsgCode

    public static Integer getEpsgCode(Class<? extends CoordinateSystem> type, CoordinateSystemAxis... axes)
    Returns the EPSG code of a coordinate system using the units and directions of given axes. This method ignores axis metadata (names, abbreviation, identifiers, remarks, etc.). The axis minimum and maximum values are checked only if the range meaning is "wraparound". If no suitable coordinate system is known to Apache SIS, then this method returns `null`.

    Current implementation uses a hard-coded list of known coordinate systems; it does not yet scan the EPSG database (this may change in future Apache SIS version). The current list of known coordinate systems is given below.

    Known coordinate systems (CS)
    EPSG | CS type | Axis directions | Horizontal unit
    --- | --- | --- | ---
    6424 | Ellipsoidal | east | north |  | degree
    6422 | Ellipsoidal | north | east |  | degree
    6425 | Ellipsoidal | east | north |  | grads
    6403 | Ellipsoidal | north | east |  | grads
    6429 | Ellipsoidal | east | north |  | radian
    6428 | Ellipsoidal | north | east |  | radian
    6426 | Ellipsoidal | east | north | up | degree
    6423 | Ellipsoidal | north | east | up | degree
    6427 | Ellipsoidal | east | north | up | grads
    6421 | Ellipsoidal | north | east | up | grads
    6431 | Ellipsoidal | east | north | up | radian
    6430 | Ellipsoidal | north | east | up | radian
    4400 | Cartesian | east | north |  | metre
    4500 | Cartesian | north | east |  | metre
    4491 | Cartesian | west | north |  | metre
    4501 | Cartesian | north | west |  | metre
    6503 | Cartesian | west | south |  | metre
    6501 | Cartesian | south | west |  | metre
    1039 | Cartesian | east | north |  | foot
    1029 | Cartesian | north | east |  | foot
    4403 | Cartesian | east | north |  | Clarke’s foot
    4502 | Cartesian | north | east |  | Clarke’s foot
    4497 | Cartesian | east | north |  | US survey foot

    Parameters:
      `type` - the type of coordinate system for which an EPSG code is desired, as a GeoAPI interface.
      `axes` - axes for which a coordinate system EPSG code is desired.
    Returns:
      EPSG codes for a coordinate system using the given axes (ignoring metadata), or `null` if unknown to this method. Note that a null value does not mean that a more extensive search in the EPSG database would not find a matching coordinate system.
    Since:
      1.0
    See Also:
      - `GeodeticAuthorityFactory​.createCoordinateSystem(String)`

  - ### getEpsgCode

    public static Integer getEpsgCode(Unit<?> unit, AxisDirection... directions)
    Returns the EPSG code of a coordinate system using the given unit and axis directions. This convenience method performs the work documented in `getEpsgCode(Class, CoordinateSystemAxis...)`, but requiring only a frequently used subset of information. If no suitable coordinate system is known to Apache SIS, then this method returns `null`.

    Current implementation uses a hard-coded list of known coordinate systems; it does not yet scan the EPSG database (this may change in future Apache SIS version). The current list of known coordinate systems is documented above.

    Parameters:
      `unit` - desired unit of measurement. For three-dimensional ellipsoidal coordinate system, this is the unit for the horizontal axes only; the vertical axis is in metres.
      `directions` - desired axis directions.
    Returns:
      EPSG codes for a coordinate system using the given axis directions and unit of measurement, or `null` if unknown to this method. Note that a null value does not mean that a more extensive search in the EPSG database would not find a matching coordinate system.
    Since:
      0.8
    See Also:
      - `Units​.getEpsgCode(Unit, boolean)`
      - `GeodeticAuthorityFactory​.createCoordinateSystem(String)`