Search in sources :

Example 1 with Angle

use of org.apache.sis.measure.Angle in project sis by apache.

the class CoordinateFormat method format.

/**
 * Formats the given coordinate and appends the resulting text to the given stream or buffer.
 *
 * @param  position    the coordinate to format.
 * @param  toAppendTo  where the text is to be appended.
 * @throws IOException if an error occurred while writing to the given appendable.
 */
@Override
@SuppressWarnings("UnnecessaryBoxing")
public void format(final DirectPosition position, final Appendable toAppendTo) throws IOException {
    ArgumentChecks.ensureNonNull("position", position);
    ArgumentChecks.ensureNonNull("toAppendTo", toAppendTo);
    CoordinateReferenceSystem crs = position.getCoordinateReferenceSystem();
    if (crs == null) {
        // May still be null.
        crs = defaultCRS;
    }
    if (crs != lastCRS) {
        initialize(crs);
    }
    /*
         * Standard java.text.Format API can only write into a StringBuffer. If the given Appendable is not a
         * StringBuffer, then we will need to format in a temporary buffer before to copy to the Appendable.
         */
    final StringBuffer destination;
    if (toAppendTo instanceof StringBuffer) {
        destination = (StringBuffer) toAppendTo;
    } else {
        if (buffer == null) {
            buffer = new StringBuffer();
        }
        destination = buffer;
        destination.setLength(0);
    }
    if (dummy == null) {
        dummy = new FieldPosition(0);
    }
    /*
         * The format to use for each ordinate has been computed by 'initialize'.  The format array length
         * should match the number of dimensions in the given position if the DirectPosition is consistent
         * with its CRS, but we will nevertheless verify has a paranoiac check.  If there is no CRS, or if
         * the DirectPosition dimension is (illegally) greater than the CRS dimension, then we will format
         * the ordinate as a number.
         */
    final int dimension = position.getDimension();
    for (int i = 0; i < dimension; i++) {
        double value = position.getOrdinate(i);
        final Object object;
        final Format f;
        if (formats != null && i < formats.length) {
            f = formats[i];
            if (isNegative(i)) {
                value = -value;
            }
            if (toFormatUnit != null) {
                final UnitConverter c = toFormatUnit[i];
                if (c != null) {
                    value = c.convert(value);
                }
            }
            switch(types[i]) {
                default:
                    object = Double.valueOf(value);
                    break;
                case LONGITUDE:
                    object = new Longitude(value);
                    break;
                case LATITUDE:
                    object = new Latitude(value);
                    break;
                case ANGLE:
                    object = new Angle(value);
                    break;
                case DATE:
                    object = new Date(Math.round(value) + epochs[i]);
                    break;
            }
        } else {
            object = value;
            f = getFormat(Number.class);
        }
        /*
             * At this point we got the value to format together with the Format instance to use.
             */
        if (i != 0) {
            toAppendTo.append(separator);
        }
        if (f.format(object, destination, dummy) != toAppendTo) {
            toAppendTo.append(destination);
            destination.setLength(0);
        }
        if (unitSymbols != null && i < unitSymbols.length) {
            final String symbol = unitSymbols[i];
            if (symbol != null) {
                toAppendTo.append(Characters.NO_BREAK_SPACE).append(symbol);
            }
        }
    }
}
Also used : Latitude(org.apache.sis.measure.Latitude) FieldPosition(java.text.FieldPosition) Longitude(org.apache.sis.measure.Longitude) Date(java.util.Date) Format(java.text.Format) SimpleDateFormat(java.text.SimpleDateFormat) NumberFormat(java.text.NumberFormat) DateFormat(java.text.DateFormat) AngleFormat(org.apache.sis.measure.AngleFormat) CompoundFormat(org.apache.sis.io.CompoundFormat) DecimalFormat(java.text.DecimalFormat) Angle(org.apache.sis.measure.Angle) UnitConverter(javax.measure.UnitConverter) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 2 with Angle

use of org.apache.sis.measure.Angle in project sis by apache.

the class AnglePattern method format.

/**
 * Formats the given angle. This function is typically invoked for formatting only one value.
 * But it is nevertheless defined as a matrix function for more efficient conversions of a bulk of angles.
 *
 * @param value   the value to format.
 * @param locale  the target locale.
 */
String[][] format(final double[][] value, final Locale locale) {
    final AngleFormat format = getAngleFormat(locale);
    final String[][] text = new String[value.length][];
    synchronized (format) {
        for (int j = 0; j < value.length; j++) {
            final double[] input = value[j];
            if (input != null) {
                // Paranoiac check.
                final String[] result = new String[input.length];
                for (int i = 0; i < input.length; i++) {
                    final double v = input[i];
                    final Angle angle;
                    switch(type) {
                        default:
                            angle = new Angle(v);
                            break;
                        case LATITUDE:
                            angle = new Latitude(v);
                            break;
                        case LONGITUDE:
                            angle = new Longitude(v);
                            break;
                    }
                    result[i] = format.format(angle);
                }
                text[j] = result;
            }
        }
    }
    return text;
}
Also used : Angle(org.apache.sis.measure.Angle) Latitude(org.apache.sis.measure.Latitude) AngleFormat(org.apache.sis.measure.AngleFormat) Longitude(org.apache.sis.measure.Longitude)

Example 3 with Angle

use of org.apache.sis.measure.Angle in project sis by apache.

the class DefaultCartesianCS method ensurePerpendicularAxis.

/**
 * Ensures that all axes are perpendicular.
 */
private void ensurePerpendicularAxis(final Map<String, ?> properties) throws IllegalArgumentException {
    final int dimension = getDimension();
    for (int i = 0; i < dimension; i++) {
        final AxisDirection axis0 = getAxis(i).getDirection();
        for (int j = i; ++j < dimension; ) {
            final AxisDirection axis1 = getAxis(j).getDirection();
            final Angle angle = CoordinateSystems.angle(axis0, axis1);
            /*
                 * The angle may be null for grid directions (COLUMN_POSITIVE, COLUMN_NEGATIVE,
                 * ROW_POSITIVE, ROW_NEGATIVE). We conservatively accept those directions even if
                 * they are not really for Cartesian CS because we do not know the grid geometry.
                 */
            if (angle != null && Math.abs(angle.degrees()) != 90) {
                throw new IllegalArgumentException(Resources.forProperties(properties).getString(Resources.Keys.NonPerpendicularDirections_2, axis0, axis1));
            }
        }
    }
}
Also used : Angle(org.apache.sis.measure.Angle) AxisDirection(org.opengis.referencing.cs.AxisDirection)

Example 4 with Angle

use of org.apache.sis.measure.Angle in project sis by apache.

the class CoordinateFormat method initialize.

/**
 * Computes the value of transient fields from the given CRS.
 */
private void initialize(final CoordinateReferenceSystem crs) {
    types = null;
    formats = null;
    units = null;
    toFormatUnit = null;
    unitSymbols = null;
    epochs = null;
    negate = 0;
    lastCRS = crs;
    if (crs == null) {
        return;
    }
    /*
         * If no CRS were specified, we will format everything as numbers. Working with null CRS
         * is sometime useful because null CRS are allowed in DirectPosition according ISO 19107.
         * Otherwise (if a CRS is given), infer the format subclasses from the axes.
         */
    final CoordinateSystem cs = crs.getCoordinateSystem();
    final int dimension = cs.getDimension();
    final byte[] types = new byte[dimension];
    final Format[] formats = new Format[dimension];
    for (int i = 0; i < dimension; i++) {
        final CoordinateSystemAxis axis = cs.getAxis(i);
        final Unit<?> unit = axis.getUnit();
        /*
             * Formatter for angular units. Target unit is DEGREE_ANGLE.
             * Type is LONGITUDE, LATITUDE or ANGLE depending on axis direction.
             */
        if (Units.isAngular(unit)) {
            byte type = ANGLE;
            final AxisDirection dir = axis.getDirection();
            if (AxisDirection.NORTH.equals(dir)) {
                type = LATITUDE;
            } else if (AxisDirection.EAST.equals(dir)) {
                type = LONGITUDE;
            } else if (AxisDirection.SOUTH.equals(dir)) {
                type = LATITUDE;
                negate(i);
            } else if (AxisDirection.WEST.equals(dir)) {
                type = LONGITUDE;
                negate(i);
            }
            types[i] = type;
            formats[i] = getFormat(Angle.class);
            setConverter(dimension, i, unit.asType(javax.measure.quantity.Angle.class).getConverterTo(Units.DEGREE));
            continue;
        }
        /*
             * Formatter for temporal units. Target unit is MILLISECONDS.
             * Type is DATE.
             */
        if (Units.isTemporal(unit)) {
            final CoordinateReferenceSystem t = CRS.getComponentAt(crs, i, i + 1);
            if (t instanceof TemporalCRS) {
                if (epochs == null) {
                    epochs = new long[dimension];
                }
                types[i] = DATE;
                formats[i] = getFormat(Date.class);
                epochs[i] = ((TemporalCRS) t).getDatum().getOrigin().getTime();
                setConverter(dimension, i, unit.asType(Time.class).getConverterTo(Units.MILLISECOND));
                if (AxisDirection.PAST.equals(axis.getDirection())) {
                    negate(i);
                }
                continue;
            }
            types[i] = TIME;
        // Fallthrough: formatted as number.
        }
        /*
             * Formatter for all other units. Do NOT set types[i] since it may have been set
             * to a non-zero value by previous case. If not, the default value (zero) is the
             * one we want.
             */
        formats[i] = getFormat(Number.class);
        if (unit != null) {
            if (units == null) {
                units = new Unit<?>[dimension];
            }
            units[i] = unit;
            final String symbol = getFormat(Unit.class).format(unit);
            if (!symbol.isEmpty()) {
                if (unitSymbols == null) {
                    unitSymbols = new String[dimension];
                }
                unitSymbols[i] = symbol;
            }
        }
    }
    // Assign only on success.
    this.types = types;
    this.formats = formats;
}
Also used : CoordinateSystem(org.opengis.referencing.cs.CoordinateSystem) CoordinateSystemAxis(org.opengis.referencing.cs.CoordinateSystemAxis) Unit(javax.measure.Unit) Date(java.util.Date) TemporalCRS(org.opengis.referencing.crs.TemporalCRS) Format(java.text.Format) SimpleDateFormat(java.text.SimpleDateFormat) NumberFormat(java.text.NumberFormat) DateFormat(java.text.DateFormat) AngleFormat(org.apache.sis.measure.AngleFormat) CompoundFormat(org.apache.sis.io.CompoundFormat) DecimalFormat(java.text.DecimalFormat) Angle(org.apache.sis.measure.Angle) AxisDirection(org.opengis.referencing.cs.AxisDirection) CoordinateReferenceSystem(org.opengis.referencing.crs.CoordinateReferenceSystem)

Example 5 with Angle

use of org.apache.sis.measure.Angle in project sis by apache.

the class CoordinateFormat method parse.

/**
 * Parses a coordinate from the given character sequence.
 * This method presumes that the coordinate reference system is the {@linkplain #getDefaultCRS() default CRS}.
 * The parsing begins at the {@linkplain ParsePosition#getIndex() index} given by the {@code pos} argument.
 * If parsing succeeds, then the {@code pos} index is updated to the index after the last ordinate value and
 * the parsed coordinate is returned. Otherwise (if parsing fails), the {@code pos} index is left unchanged,
 * the {@code pos} {@linkplain ParsePosition#getErrorIndex() error index} is set to the index of the first
 * unparsable character and an exception is thrown with a similar {@linkplain ParseException#getErrorOffset()
 * error index}.
 *
 * @param  text  the character sequence for the coordinate to parse.
 * @param  pos   the index where to start the parsing.
 * @return the parsed coordinate (never {@code null}).
 * @throws ParseException if an error occurred while parsing the coordinate.
 */
@Override
public DirectPosition parse(final CharSequence text, final ParsePosition pos) throws ParseException {
    ArgumentChecks.ensureNonNull("text", text);
    ArgumentChecks.ensureNonNull("pos", pos);
    final int start = pos.getIndex();
    final int length = text.length();
    /*
         * The NumberFormat, DateFormat and AngleFormat work only on String values, not on CharSequence.
         * If the given text is not a String, we will convert an arbitrarily small section of the given
         * text. Note that this will require to adjust the ParsePosition indices.
         */
    final int offset;
    final String asString;
    final ParsePosition subPos;
    if (text instanceof String) {
        offset = 0;
        subPos = pos;
        asString = (String) text;
    } else {
        offset = start;
        subPos = new ParsePosition(0);
        asString = text.subSequence(start, Math.min(start + READ_AHEAD_LIMIT, length)).toString();
    }
    /*
         * The Format instances to be used for each ordinate values is determined by the default CRS.
         * If no such CRS has been specified, then we will parse everything as plain numbers.
         */
    if (lastCRS != defaultCRS) {
        initialize(defaultCRS);
    }
    final double[] ordinates;
    Format format;
    final Format[] formats = this.formats;
    if (formats != null) {
        format = null;
        ordinates = new double[formats.length];
    } else {
        format = getFormat(Number.class);
        ordinates = new double[DEFAULT_DIMENSION];
    }
    /*
         * For each ordinate value except the first one, we need to skip the separator.
         * If we do not find the separator, we may consider that we reached the coordinate
         * end ahead of time. We currently allow that only for coordinate without CRS.
         */
    for (int i = 0; i < ordinates.length; i++) {
        if (i != 0) {
            final int end = subPos.getIndex();
            int index = offset + end;
            while (!CharSequences.regionMatches(text, index, parseSeparator)) {
                if (index < length) {
                    final int c = Character.codePointAt(text, index);
                    if (Character.isSpaceChar(c)) {
                        index += Character.charCount(c);
                        continue;
                    }
                }
                if (formats == null) {
                    pos.setIndex(index);
                    return new GeneralDirectPosition(Arrays.copyOf(ordinates, i));
                }
                pos.setIndex(start);
                pos.setErrorIndex(index);
                throw new LocalizedParseException(getLocale(), Errors.Keys.UnexpectedCharactersAfter_2, new CharSequence[] { text.subSequence(start, end), CharSequences.token(text, index) }, index);
            }
            subPos.setIndex(index + parseSeparator.length() - offset);
        }
        /*
             * At this point 'subPos' is set to the beginning of the next ordinate to parse in 'asString'.
             * Parse the value as a number, angle or date, as determined from the coordinate system axis.
             */
        if (formats != null) {
            format = formats[i];
        }
        @SuppressWarnings("null") final Object object = format.parseObject(asString, subPos);
        if (object == null) {
            /*
                 * If we failed to parse, build an error message with the type that was expected for that ordinate.
                 * If the given CharSequence was not a String, we may need to update the error index since we tried
                 * to parse only a substring.
                 */
            Class<?> type = Number.class;
            if (types != null) {
                switch(types[i]) {
                    case LONGITUDE:
                        type = Longitude.class;
                        break;
                    case LATITUDE:
                        type = Latitude.class;
                        break;
                    case ANGLE:
                        type = Angle.class;
                        break;
                    case DATE:
                        type = Date.class;
                        break;
                }
            }
            pos.setIndex(start);
            if (subPos != pos) {
                pos.setErrorIndex(offset + subPos.getErrorIndex());
            }
            throw new LocalizedParseException(getLocale(), type, text, pos);
        }
        double value;
        if (object instanceof Angle) {
            value = ((Angle) object).degrees();
        } else if (object instanceof Date) {
            value = ((Date) object).getTime() - epochs[i];
        } else {
            value = ((Number) object).doubleValue();
        }
        /*
             * The conversions and sign reversal applied below shall be in exact reverse order than
             * in the 'format(…)' method. However we have one additional step compared to format(…):
             * the unit written after the ordinate value may not be the same than the unit declared
             * in the CRS axis, so we have to parse the unit and convert the value before to apply
             * the reverse of 'format(…)' steps.
             */
        if (units != null) {
            final Unit<?> target = units[i];
            if (target != null) {
                final int base = subPos.getIndex();
                int index = base;
                /*
                     * Skip whitespaces using Character.isSpaceChar(…), not Character.isWhitespace(…),
                     * because we need to skip also the non-breaking space (Characters.NO_BREAK_SPACE).
                     * If we can not parse the unit after those spaces, we will revert to the original
                     * position (absence of unit will not be considered an error).
                     */
                while (index < asString.length()) {
                    final int c = asString.codePointAt(index);
                    if (Character.isSpaceChar(c)) {
                        index += Character.charCount(c);
                        continue;
                    }
                    subPos.setIndex(index);
                    final Object unit = getFormat(Unit.class).parseObject(asString, subPos);
                    if (unit == null) {
                        subPos.setIndex(base);
                        subPos.setErrorIndex(-1);
                    } else
                        try {
                            value = ((Unit<?>) unit).getConverterToAny(target).convert(value);
                        } catch (IncommensurableException e) {
                            index += offset;
                            pos.setIndex(start);
                            pos.setErrorIndex(index);
                            throw (ParseException) new ParseException(e.getMessage(), index).initCause(e);
                        }
                    break;
                }
            }
        }
        if (toFormatUnit != null) {
            final UnitConverter c = toFormatUnit[i];
            if (c != null) {
                value = c.inverse().convert(value);
            }
        }
        if (isNegative(i)) {
            value = -value;
        }
        ordinates[i] = value;
    }
    final GeneralDirectPosition position = new GeneralDirectPosition(ordinates);
    position.setCoordinateReferenceSystem(defaultCRS);
    return position;
}
Also used : IncommensurableException(javax.measure.IncommensurableException) Unit(javax.measure.Unit) Date(java.util.Date) Format(java.text.Format) SimpleDateFormat(java.text.SimpleDateFormat) NumberFormat(java.text.NumberFormat) DateFormat(java.text.DateFormat) AngleFormat(org.apache.sis.measure.AngleFormat) CompoundFormat(org.apache.sis.io.CompoundFormat) DecimalFormat(java.text.DecimalFormat) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) Angle(org.apache.sis.measure.Angle) UnitConverter(javax.measure.UnitConverter) ParseException(java.text.ParseException) LocalizedParseException(org.apache.sis.internal.util.LocalizedParseException) ParsePosition(java.text.ParsePosition)

Aggregations

Angle (org.apache.sis.measure.Angle)9 AngleFormat (org.apache.sis.measure.AngleFormat)5 NumberFormat (java.text.NumberFormat)4 Date (java.util.Date)4 DateFormat (java.text.DateFormat)3 DecimalFormat (java.text.DecimalFormat)3 Format (java.text.Format)3 SimpleDateFormat (java.text.SimpleDateFormat)3 CompoundFormat (org.apache.sis.io.CompoundFormat)3 Latitude (org.apache.sis.measure.Latitude)3 Longitude (org.apache.sis.measure.Longitude)3 CoordinateReferenceSystem (org.opengis.referencing.crs.CoordinateReferenceSystem)3 ParseException (java.text.ParseException)2 Unit (javax.measure.Unit)2 UnitConverter (javax.measure.UnitConverter)2 Test (org.junit.Test)2 AxisDirection (org.opengis.referencing.cs.AxisDirection)2 CoordinateSystem (org.opengis.referencing.cs.CoordinateSystem)2 InternationalString (org.opengis.util.InternationalString)2 IOException (java.io.IOException)1