Search in sources :

Example 16 with AxisDirection

use of org.opengis.referencing.cs.AxisDirection in project sis by apache.

the class CoordinateSystems method swapAndScaleAxes.

/**
 * Returns an affine transform between two coordinate systems.
 * Only units and axes order (e.g. transforming from
 * ({@linkplain AxisDirection#NORTH North}, {@linkplain AxisDirection#WEST West}) to
 * ({@linkplain AxisDirection#EAST East}, {@linkplain AxisDirection#NORTH North})
 * are taken in account by this method.
 *
 * <div class="section">Conditions</div>
 * The two coordinate systems must implement the same GeoAPI coordinate system interface.
 * For example if {@code sourceCS} is a {@link org.opengis.referencing.cs.CartesianCS},
 * then {@code targetCS} must be a {@code CartesianCS} too.
 *
 * <div class="note"><b>Example:</b>
 * If coordinates in {@code sourceCS} are (<var>x</var>,<var>y</var>) tuples in metres
 * and coordinates in {@code targetCS} are (<var>-y</var>,<var>x</var>) tuples in centimetres,
 * then the transformation can be performed as below:
 *
 * {@preformat math
 *     ┌      ┐   ┌                ┐ ┌     ┐
 *     │-y(cm)│   │   0  -100    0 │ │ x(m)│
 *     │ x(cm)│ = │ 100     0    0 │ │ y(m)│
 *     │ 1    │   │   0     0    1 │ │ 1   │
 *     └      ┘   └                ┘ └     ┘
 * }
 * </div>
 *
 * @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 the CS are not of the same type, or axes do not match.
 * @throws IncommensurableException if the units are not compatible, or the conversion is non-linear.
 *
 * @see Matrices#createTransform(AxisDirection[], AxisDirection[])
 */
@SuppressWarnings("fallthrough")
public static Matrix swapAndScaleAxes(final CoordinateSystem sourceCS, final CoordinateSystem targetCS) throws IllegalArgumentException, IncommensurableException {
    ensureNonNull("sourceCS", sourceCS);
    ensureNonNull("targetCS", targetCS);
    if (!Classes.implementSameInterfaces(sourceCS.getClass(), targetCS.getClass(), CoordinateSystem.class)) {
        throw new IllegalArgumentException(Resources.format(Resources.Keys.IncompatibleCoordinateSystemTypes));
    }
    final AxisDirection[] srcAxes = getAxisDirections(sourceCS);
    final AxisDirection[] dstAxes = getAxisDirections(targetCS);
    final MatrixSIS matrix = Matrices.createTransform(srcAxes, dstAxes);
    assert Arrays.equals(srcAxes, dstAxes) == matrix.isIdentity() : matrix;
    /*
         * The previous code computed a matrix for swapping axes. 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 axes 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.
         */
    // == sourceCS.getDimension()
    final int sourceDim = matrix.getNumCol() - 1;
    // == targetCS.getDimension()
    final int targetDim = matrix.getNumRow() - 1;
    for (int j = 0; j < targetDim; j++) {
        final Unit<?> targetUnit = targetCS.getAxis(j).getUnit();
        for (int i = 0; i < sourceDim; i++) {
            if (matrix.getElement(j, i) == 0) {
                // (i.e. axes are orthogonal).
                continue;
            }
            final Unit<?> sourceUnit = sourceCS.getAxis(i).getUnit();
            if (Objects.equals(sourceUnit, targetUnit)) {
                // between source[i] and target[j].
                continue;
            }
            Number scale = 1;
            Number offset = 0;
            final Number[] coefficients = Units.coefficients(sourceUnit.getConverterToAny(targetUnit));
            switch(coefficients != null ? coefficients.length : -1) {
                // Fall through
                case 2:
                    scale = coefficients[1];
                // Fall through
                case 1:
                    offset = coefficients[0];
                case 0:
                    break;
                default:
                    throw new IncommensurableException(Resources.format(Resources.Keys.NonLinearUnitConversion_2, sourceUnit, targetUnit));
            }
            final DoubleDouble element = DoubleDouble.castOrCopy(matrix.getNumber(j, i));
            final DoubleDouble r = new DoubleDouble(element);
            r.multiply(scale);
            matrix.setNumber(j, i, r);
            r.setFrom(element);
            r.multiply(offset);
            r.add(matrix.getNumber(j, sourceDim));
            matrix.setNumber(j, sourceDim, r);
        }
    }
    return matrix;
}
Also used : IncommensurableException(javax.measure.IncommensurableException) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) AxisDirection(org.opengis.referencing.cs.AxisDirection) MatrixSIS(org.apache.sis.referencing.operation.matrix.MatrixSIS) DoubleDouble(org.apache.sis.internal.util.DoubleDouble)

Example 17 with AxisDirection

use of org.opengis.referencing.cs.AxisDirection in project sis by apache.

the class Normalizer method compareTo.

/**
 * Compares two axis for an order that try to favor right-handed coordinate systems.
 * Compass directions like North and East are first. Vertical directions like Up or Down are next.
 */
@Override
public int compareTo(final Normalizer that) {
    int d = unitOrder - that.unitOrder;
    if (d == 0) {
        final AxisDirection d1 = this.axis.getDirection();
        final AxisDirection d2 = that.axis.getDirection();
        d = AxisDirections.angleForCompass(d2, d1);
        if (d == Integer.MIN_VALUE) {
            if (meridian != null) {
                if (that.meridian != null) {
                    d = meridian.compareTo(that.meridian);
                } else {
                    d = -1;
                }
            } else if (that.meridian != null) {
                d = +1;
            } else {
                d = order(d1) - order(d2);
            }
        }
    }
    return d;
}
Also used : AxisDirection(org.opengis.referencing.cs.AxisDirection)

Example 18 with AxisDirection

use of org.opengis.referencing.cs.AxisDirection in project sis by apache.

the class DefaultCoordinateSystemAxis method formatTo.

/**
 * Formats this axis as a <cite>Well Known Text</cite> {@code Axis[…]} element.
 *
 * <div class="section">Constraints for WKT validity</div>
 * The ISO 19162 specification puts many constraints on axis names, abbreviations and directions allowed in WKT.
 * Most of those constraints are inherited from ISO 19111 — see {@link CoordinateSystemAxis} javadoc for some of
 * those. The current Apache SIS implementation does not verify whether this axis name and abbreviation are
 * compliant; we assume that the user created a valid axis.
 * The only actions (derived from ISO 19162 rules) taken by this method (by default) are:
 *
 * <ul>
 *   <li>Replace <cite>“Geodetic latitude”</cite> and <cite>“Geodetic longitude”</cite> names (case insensitive)
 *       by <cite>“latitude”</cite> and <cite>“longitude”</cite> respectively.</li>
 *   <li>For latitude and longitude axes, replace “φ” and “λ” abbreviations by <var>“B”</var> and <var>“L”</var>
 *       respectively (from German “Breite” and “Länge”, used in academic texts worldwide).
 *       Note that <var>“L”</var> is also the transliteration of Greek letter “lambda” (λ).</li>
 *   <li>In {@link SphericalCS}, replace “φ” and “θ” abbreviations by <var>“U”</var> and <var>“V”</var> respectively.</li>
 *   <li>In {@link PolarCS}, replace “θ” abbreviation by <var>“U”</var>.</li>
 * </ul>
 *
 * The above-cited replacements of name and Greek letters can be controlled by a call to
 * {@link org.apache.sis.io.wkt.WKTFormat#setTransliterator(Transliterator)}.
 *
 * @return {@code "Axis"}.
 *
 * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html#39">WKT 2 specification §7.5.3</a>
 */
@Override
protected String formatTo(final Formatter formatter) {
    final Convention convention = formatter.getConvention();
    final boolean isWKT1 = (convention.majorVersion() == 1);
    final boolean isInternal = (convention == Convention.INTERNAL);
    final CoordinateSystem cs = getEnclosingCS(formatter);
    AxisDirection dir = getDirection();
    String name = IdentifiedObjects.getName(this, formatter.getNameAuthority());
    if (name == null) {
        name = IdentifiedObjects.getName(this, null);
    }
    if (name != null && !isInternal) {
        final String old = name;
        name = formatter.getTransliterator().toShortAxisName(cs, dir, name);
        if (name == null && isWKT1) {
            // WKT 1 does not allow omission of name.
            name = old;
        }
    }
    /*
         * ISO 19162:2015 §7.5.3 suggests to put abbreviation in parentheses, e.g. "Easting (x)".
         * The specification also suggests to write only the abbreviation (e.g. "(X)") in the
         * special case of Geocentric axis, and disallows Greek letters.
         */
    if (!isWKT1) {
        final String a = formatter.getTransliterator().toLatinAbbreviation(cs, dir, getAbbreviation());
        if (a != null && !a.equals(name)) {
            final StringBuilder buffer = new StringBuilder();
            if (name != null) {
                buffer.append(name).append(' ');
            }
            name = buffer.append('(').append(a).append(')').toString();
        }
    }
    formatter.append(name, ElementKind.AXIS);
    /*
         * Format the axis direction, optionally followed by a MERIDIAN[…] element
         * if the direction is of the kind "South along 90°N" for instance.
         */
    DirectionAlongMeridian meridian = null;
    if (AxisDirections.isUserDefined(dir)) {
        meridian = DirectionAlongMeridian.parse(dir);
        if (meridian != null) {
            dir = meridian.baseDirection;
            if (isWKT1) {
                formatter.setInvalidWKT(this, null);
            }
        }
    }
    formatter.append(dir);
    formatter.append(meridian);
    /*
         * Formats the axis unit only if the enclosing CRS element does not provide one.
         * If the enclosing CRS provided a contextual unit, then it is assumed to apply
         * to all axes (we do not verify).
         */
    if (!isWKT1) {
        if (convention == Convention.WKT2 && cs != null) {
            final Order order = Order.create(cs, this);
            if (order != null) {
                formatter.append(order);
            } else {
                formatter.setInvalidWKT(cs, null);
            }
        }
        if (!formatter.hasContextualUnit(1)) {
            formatter.append(getUnit());
        }
    }
    return WKTKeywords.Axis;
}
Also used : Convention(org.apache.sis.io.wkt.Convention) CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) AxisDirection(org.opengis.referencing.cs.AxisDirection) InternationalString(org.opengis.util.InternationalString)

Example 19 with AxisDirection

use of org.opengis.referencing.cs.AxisDirection in project sis by apache.

the class AxisDirections method valueOf.

/**
 * Searches pre-defined {@link AxisDirection} for a given name. This method searches for a match in the set
 * of known axis directions as returned by {@link AxisDirection#values()}, plus a few special cases like
 * <cite>"Geocentre &gt; equator/90°E"</cite>. The later are used in the EPSG database for geocentric CRS.
 *
 * <p>This method does not know about {@code org.apache.sis.referencing.cs.DirectionAlongMeridian}.
 * The later is a parser which may create new directions, while this method searches only in a set
 * of predefined directions and never create new ones.</p>
 *
 * @param  name  the name of the axis direction to search.
 * @return the first axis direction having a name matching the given one, or {@code null} if none.
 */
public static AxisDirection valueOf(String name) {
    name = trimWhitespaces(name.replace('_', ' '));
    final AxisDirection[] directions = AxisDirection.values();
    AxisDirection candidate = find(name, directions);
    if (candidate == null) {
        /*
             * No match found when using the pre-defined axis name. Searches among
             * the set of geocentric directions. Expected directions are:
             *
             *    Geocentre > equator/PM      or    Geocentre > equator/0°E
             *    Geocentre > equator/90dE    or    Geocentre > equator/90°E
             *    Geocentre > north pole
             */
        int d = name.indexOf('>');
        if (d >= 0 && equalsIgnoreCase(name, 0, skipTrailingWhitespaces(name, 0, d), "Geocentre")) {
            final int length = name.length();
            d = skipLeadingWhitespaces(name, d + 1, length);
            int s = name.indexOf('/', d);
            if (s < 0) {
                if (equalsIgnoreCase(name, d, length, "north pole")) {
                    // "Geocentre > north pole"
                    return GEOCENTRIC_Z;
                }
            } else if (equalsIgnoreCase(name, d, skipTrailingWhitespaces(name, d, s), "equator")) {
                s = skipLeadingWhitespaces(name, s + 1, length);
                if (equalsIgnoreCase(name, s, length, "PM")) {
                    // "Geocentre > equator/PM"
                    return GEOCENTRIC_X;
                }
                /*
                     * At this point, the name may be "Geocentre > equator/0°E",
                     * "Geocentre > equator/90°E" or "Geocentre > equator/90dE".
                     * Parse the number, limiting the scan to 6 characters for
                     * avoiding a NumberFormatException.
                     */
                final int stopAt = Math.min(s + 6, length);
                for (int i = s; i < stopAt; i++) {
                    final char c = name.charAt(i);
                    if (c < '0' || c > '9') {
                        if (i == s)
                            break;
                        final int n = Integer.parseInt(name.substring(s, i));
                        i = skipLeadingWhitespaces(name, i, length);
                        if (equalsIgnoreCase(name, i, length, "°E") || equalsIgnoreCase(name, i, length, "dE")) {
                            switch(n) {
                                // "Geocentre > equator/0°E"
                                case 0:
                                    return GEOCENTRIC_X;
                                // "Geocentre > equator/90°E"
                                case 90:
                                    return GEOCENTRIC_Y;
                            }
                        }
                        break;
                    }
                }
            }
        }
    }
    return candidate;
}
Also used : AxisDirection(org.opengis.referencing.cs.AxisDirection)

Example 20 with AxisDirection

use of org.opengis.referencing.cs.AxisDirection in project sis by apache.

the class AxisDirectionsTest method testAngleForCompass.

/**
 * Tests the {@link AxisDirections#angleForCompass(AxisDirection, AxisDirection)} method.
 */
@Test
public void testAngleForCompass() {
    final AxisDirection[] compass = new AxisDirection[] { NORTH, NORTH_NORTH_EAST, NORTH_EAST, EAST_NORTH_EAST, EAST, EAST_SOUTH_EAST, SOUTH_EAST, SOUTH_SOUTH_EAST, SOUTH, SOUTH_SOUTH_WEST, SOUTH_WEST, WEST_SOUTH_WEST, WEST, WEST_NORTH_WEST, NORTH_WEST, NORTH_NORTH_WEST };
    assertEquals(compass.length, AxisDirections.COMPASS_COUNT);
    final int base = NORTH.ordinal();
    final int h = compass.length / 2;
    for (int i = 0; i < compass.length; i++) {
        final AxisDirection direction = compass[i];
        final AxisDirection opposite = AxisDirections.opposite(direction);
        final String message = direction.name();
        int io = i + h, in = i;
        if (i >= h)
            io -= AxisDirections.COMPASS_COUNT;
        if (i > h)
            in -= AxisDirections.COMPASS_COUNT;
        assertEquals(message, base + i, direction.ordinal());
        assertEquals(message, base + io, opposite.ordinal());
        assertEquals(message, 0, AxisDirections.angleForCompass(direction, direction));
        assertEquals(message, h, abs(AxisDirections.angleForCompass(direction, opposite)));
        assertEquals(message, in, AxisDirections.angleForCompass(direction, NORTH));
    }
}
Also used : AxisDirection(org.opengis.referencing.cs.AxisDirection) Test(org.junit.Test)

Aggregations

AxisDirection (org.opengis.referencing.cs.AxisDirection)22 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)6 CoordinateSystemAxis (org.opengis.referencing.cs.CoordinateSystemAxis)4 IncommensurableException (javax.measure.IncommensurableException)3 Test (org.junit.Test)3 Unit (javax.measure.Unit)2 DoubleDouble (org.apache.sis.internal.util.DoubleDouble)2 Angle (org.apache.sis.measure.Angle)2 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)2 EllipsoidalCS (org.opengis.referencing.cs.EllipsoidalCS)2 Field (java.lang.reflect.Field)1 DateFormat (java.text.DateFormat)1 DecimalFormat (java.text.DecimalFormat)1 Format (java.text.Format)1 NumberFormat (java.text.NumberFormat)1 SimpleDateFormat (java.text.SimpleDateFormat)1 Date (java.util.Date)1 HashMap (java.util.HashMap)1 Map (java.util.Map)1 Matcher (java.util.regex.Matcher)1