use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class Normalizer method sort.
/**
* Sorts the specified axis in an attempt to create a right-handed system.
* The sorting is performed in place. This method returns {@code true} if
* at least one axis moved as result of this method call.
*
* @param axes the axes to sort.
* @param angularUnitOrder -1 for sorting angular units first, +1 for sorting them last, or 0 if neutral.
*/
static boolean sort(final CoordinateSystemAxis[] axes, final int angularUnitOrder) {
final Normalizer[] wrappers = new Normalizer[axes.length];
for (int i = 0; i < axes.length; i++) {
wrappers[i] = new Normalizer(axes[i], angularUnitOrder);
}
Arrays.sort(wrappers);
boolean changed = false;
for (int i = 0; i < axes.length; i++) {
final CoordinateSystemAxis a = wrappers[i].axis;
changed |= (axes[i] != a);
axes[i] = a;
}
return changed;
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class Normalizer method normalize.
/**
* Optionally normalizes and reorders the axes in an attempt to get a right-handed system.
* If no axis change is needed, then this method returns {@code null}.
*
* @param cs the coordinate system to normalize.
* @param changes the change to apply on axis direction and units.
* @param reorder {@code true} for reordering the axis for a right-handed coordinate system.
* @return the normalized coordinate system, or {@code null} if no normalization is needed.
*/
static AbstractCS normalize(final CoordinateSystem cs, final AxisFilter changes, final boolean reorder) {
boolean changed = false;
final int dimension = cs.getDimension();
CoordinateSystemAxis[] axes = new CoordinateSystemAxis[dimension];
int n = 0;
for (int i = 0; i < dimension; i++) {
CoordinateSystemAxis axis = cs.getAxis(i);
if (changes != null) {
if (!changes.accept(axis)) {
continue;
}
changed |= (axis != (axis = normalize(axis, changes)));
}
axes[n++] = axis;
}
axes = ArraysExt.resize(axes, n);
/*
* Sort the axes in an attempt to create a right-handed system.
* If nothing changed, return the given Coordinate System as-is.
*/
if (reorder) {
int angularUnitOrder = 0;
if (// (λ,φ,h) order
cs instanceof EllipsoidalCS || cs instanceof SphericalCS)
// (λ,φ,h) order
angularUnitOrder = -1;
else // (r,θ) order
if (cs instanceof CylindricalCS || cs instanceof PolarCS)
angularUnitOrder = +1;
changed |= sort(axes, angularUnitOrder);
if (angularUnitOrder == 1) {
/*
* Change (r,z,θ) to (r,θ,z) order in CylindricalCS. The check on unit of
* measurements should be always true, but we verify as a paranoiac check.
*/
if (axes.length == 3 && isLengthAndAngle(axes, 1)) {
ArraysExt.swap(axes, 1, 2);
}
/*
* If we were not allowed to normalize the axis direction, we may have a
* left-handed coordinate system here. If so, make it right-handed.
*/
if (AxisDirections.CLOCKWISE.equals(axes[1].getDirection()) && isLengthAndAngle(axes, 0)) {
ArraysExt.swap(axes, 0, 1);
}
}
}
if (!changed && n == dimension) {
return null;
}
/*
* Create a new coordinate system of the same type than the given one, but with the given axes.
* We need to change the Coordinate System name, since it is likely to not be valid anymore.
*/
final AbstractCS impl = castOrCopy(cs);
final StringBuilder buffer = (StringBuilder) CharSequences.camelCaseToSentence(impl.getInterface().getSimpleName());
return impl.createForAxes(singletonMap(AbstractCS.NAME_KEY, AxisDirections.appendTo(buffer, axes)), axes);
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class DefaultCoordinateSystemAxis method equals.
/**
* Compares the specified object with this axis for equality.
* The strictness level is controlled by the second argument.
* This method compares the following properties in every cases:
*
* <ul>
* <li>{@link #getName()}</li>
* <li>{@link #getDirection()}</li>
* <li>{@link #getUnit()}</li>
* </ul>
*
* In the particular case where {@link #getRangeMeaning()} is {@code WRAPAROUND}, then {@link #getMinimumValue()}
* and {@link #getMaximumValue()} are considered non-ignorable metadata and will be compared for every modes.
* All other properties are compared only for modes stricter than {@link ComparisonMode#IGNORE_METADATA}.
*
* @param object the object to compare to {@code this}.
* @param mode {@link ComparisonMode#STRICT STRICT} for performing a strict comparison, or
* {@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} for comparing only
* properties relevant to coordinate transformations.
* @return {@code true} if both objects are equal.
*/
@Override
public boolean equals(final Object object, final ComparisonMode mode) {
if (object == this) {
// Slight optimization.
return true;
}
if (!super.equals(object, mode)) {
return false;
}
switch(mode) {
case STRICT:
{
final DefaultCoordinateSystemAxis that = (DefaultCoordinateSystemAxis) object;
return Objects.equals(unit, that.unit) && Objects.equals(direction, that.direction) && Objects.equals(abbreviation, that.abbreviation) && Objects.equals(rangeMeaning, that.rangeMeaning) && doubleToLongBits(minimumValue) == doubleToLongBits(that.minimumValue) && doubleToLongBits(maximumValue) == doubleToLongBits(that.maximumValue);
}
case BY_CONTRACT:
{
final CoordinateSystemAxis that = (CoordinateSystemAxis) object;
return equalsIgnoreMetadata(that, mode, true) && Objects.equals(getAbbreviation(), that.getAbbreviation()) && Objects.equals(getRangeMeaning(), that.getRangeMeaning());
}
}
/*
* At this point the comparison is in "ignore metadata" mode. We compare the axis range
* only if the range meaning is "wraparound" for both axes, because only in such case a
* coordinate operation may shift some ordinate values (typically ±360° on longitudes).
*/
final CoordinateSystemAxis that = (CoordinateSystemAxis) object;
if (!equalsIgnoreMetadata(that, mode, RangeMeaning.WRAPAROUND.equals(this.getRangeMeaning()) && RangeMeaning.WRAPAROUND.equals(that.getRangeMeaning()))) {
return false;
}
Identifier name = that.getName();
if (name != UNNAMED) {
/*
* Checking the abbreviation is not sufficient. For example the polar angle and the
* spherical latitude have the same abbreviation (θ). Legacy names like "Longitude"
* (in addition to ISO 19111 "Geodetic longitude") bring more potential confusion.
* Furthermore, not all implementors use the greek letters. For example most CRS in
* WKT format use the "Lat" abbreviation instead of the greek letter φ.
* For comparisons 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 metadata are not ignored,
* because in this case a stricter check has already been performed by
* the 'equals' method in the superclass.
*/
final String thatCode = name.getCode();
if (!isHeuristicMatchForName(thatCode)) {
name = getName();
if (name != UNNAMED) {
/*
* The above test checked for special cases ("Lat" / "Lon" aliases, etc.).
* The next line may repeat the same check, so we may have a partial waste
* of CPU. But we do it anyway for checking the 'that' aliases, and also
* because the user may have overridden 'that.isHeuristicMatchForName(…)'.
*/
final String thisCode = name.getCode();
if (!IdentifiedObjects.isHeuristicMatchForName(that, thisCode)) {
// Check for the special case of "x" and "y" axis names.
if (!isHeuristicMatchForNameXY(thatCode, thisCode) && !isHeuristicMatchForNameXY(thisCode, thatCode)) {
return false;
}
}
}
}
}
return true;
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class AxisDirections method appendTo.
/**
* Builds a coordinate system name from the given array of axes.
* This method expects a {@code StringBuilder} pre-filled with the coordinate system name.
* The axis directions and abbreviations will be appended after the CS name.
* Examples:
*
* <ul>
* <li>Ellipsoidal CS: North (°), East (°).</li>
* <li>Cartesian CS: East (km), North (km).</li>
* <li>Compound CS: East (km), North (km), Up (m).</li>
* </ul>
*
* @param buffer a buffer pre-filled with the name header.
* @param axes the axes to append in the given buffer.
* @return a name for the given coordinate system type and axes.
*
* @since 0.6
*/
public static String appendTo(final StringBuilder buffer, final CoordinateSystemAxis[] axes) {
String separator = ": ";
for (final CoordinateSystemAxis axis : axes) {
buffer.append(separator).append(Types.getCodeLabel(axis.getDirection()));
separator = ", ";
final Unit<?> unit = axis.getUnit();
if (unit != null) {
final String symbol = unit.toString();
if (symbol != null && !symbol.isEmpty()) {
buffer.append(" (").append(symbol).append(')');
}
}
}
return buffer.append('.').toString();
}
use of org.opengis.referencing.cs.CoordinateSystemAxis in project sis by apache.
the class EllipsoidalHeightCombiner method createCompoundCRS.
/**
* Creates a compound CRS, but we special processing for (two-dimensional Geographic + ellipsoidal heights) tupples.
* If any such tupple is found, a three-dimensional geographic CRS is created instead than the compound CRS.
*
* @param properties name and other properties to give to the new object.
* @param components ordered array of {@code CoordinateReferenceSystem} objects.
* @return the coordinate reference system for the given properties.
* @throws FactoryException if the object creation failed.
*/
public final CoordinateReferenceSystem createCompoundCRS(final Map<String, ?> properties, CoordinateReferenceSystem... components) throws FactoryException {
for (int i = 0; i < components.length; i++) {
final CoordinateReferenceSystem vertical = components[i];
if (vertical instanceof VerticalCRS) {
final VerticalDatum datum = ((VerticalCRS) vertical).getDatum();
if (datum != null && datum.getVerticalDatumType() == VerticalDatumTypes.ELLIPSOIDAL) {
int axisPosition = 0;
CoordinateSystem cs = null;
CoordinateReferenceSystem crs = null;
if (i == 0 || (cs = getCsIfHorizontal2D(crs = components[i - 1])) == null) {
/*
* GeographicCRS are normally before VerticalCRS. But Apache SIS is tolerant to the
* opposite order (note however that such ordering is illegal according ISO 19162).
*/
if (i + 1 >= components.length || (cs = getCsIfHorizontal2D(crs = components[i + 1])) == null) {
continue;
}
axisPosition = 1;
}
/*
* At this point we have the horizontal and vertical components. The horizontal component
* begins at 'axisPosition', which is almost always zero. Create the three-dimensional CRS.
* If the result is the CRS to be returned directly by this method (components.length == 2),
* use the properties given in argument. Otherwise we need to use other properties; current
* implementation recycles the properties of the existing two-dimensional CRS.
*/
final CoordinateSystemAxis[] axes = new CoordinateSystemAxis[3];
axes[axisPosition++] = cs.getAxis(0);
axes[axisPosition++] = cs.getAxis(1);
axes[axisPosition %= 3] = vertical.getCoordinateSystem().getAxis(0);
final ReferencingServices referencing = ReferencingServices.getInstance();
final Map<String, ?> csProps = referencing.getProperties(cs, false);
final Map<String, ?> crsProps = (components.length == 2) ? properties : referencing.getProperties(crs, false);
if (crs instanceof GeodeticCRS) {
initialize(CS | CRS);
cs = csFactory.createEllipsoidalCS(csProps, axes[0], axes[1], axes[2]);
crs = crsFactory.createGeographicCRS(crsProps, ((GeodeticCRS) crs).getDatum(), (EllipsoidalCS) cs);
} else {
initialize(CS | CRS | OPERATION);
final ProjectedCRS proj = (ProjectedCRS) crs;
GeographicCRS base = proj.getBaseCRS();
if (base.getCoordinateSystem().getDimension() == 2) {
base = (GeographicCRS) createCompoundCRS(referencing.getProperties(base, false), base, vertical);
}
/*
* In Apache SIS implementation, the Conversion contains the source and target CRS together with
* a MathTransform2D. We need to recreate the same conversion, but without CRS and MathTransform
* for letting SIS create or associate new ones, which will be three-dimensional now.
*/
Conversion fromBase = proj.getConversionFromBase();
fromBase = opFactory.createDefiningConversion(referencing.getProperties(fromBase, true), fromBase.getMethod(), fromBase.getParameterValues());
cs = csFactory.createCartesianCS(csProps, axes[0], axes[1], axes[2]);
crs = crsFactory.createProjectedCRS(crsProps, base, fromBase, (CartesianCS) cs);
}
/*
* Remove the VerticalCRS and store the three-dimensional GeographicCRS in place of the previous
* two-dimensional GeographicCRS. Then let the loop continues in case there is other CRS to merge
* (should never happen, but we are paranoiac).
*/
components = ArraysExt.remove(components, i, 1);
// GeographicCRS before VerticalCRS (usual case).
if (axisPosition != 0)
i--;
components[i] = crs;
}
}
}
switch(components.length) {
case 0:
return null;
case 1:
return components[0];
default:
initialize(CRS);
return crsFactory.createCompoundCRS(properties, components);
}
}
Aggregations