use of org.apache.sis.measure.AngleFormat in project sis by apache.
the class AnglePattern method getAngleFormat.
/**
* Returns the angle format to use for this pattern. The formatter is cached on the assumption
* that the same pattern will be used for formatting more than once.
*
* @param locale the locale.
* @return the angle format for this pattern and the given locale.
*/
private AngleFormat getAngleFormat(final Locale locale) {
final CacheKey<AngleFormat> key = new CacheKey<>(AngleFormat.class, pattern, locale, null);
AngleFormat format = key.peek();
if (format == null) {
final Cache.Handler<AngleFormat> handler = key.lock();
try {
format = handler.peek();
if (format == null) {
format = new AngleFormat(pattern, locale);
}
} finally {
handler.putAndUnlock(format);
}
}
return format;
}
use of org.apache.sis.measure.AngleFormat 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;
}
use of org.apache.sis.measure.AngleFormat in project sis by apache.
the class CoordinateFormat method applyPattern.
/**
* Sets the pattern for number, angle or date fields.
* The pattern syntax depends on the {@code valueType} argument:
*
* <ul>
* <li>If {@code valueType} is {@code Number.class}, then the pattern syntax shall be as described in the
* {@link DecimalFormat} class. This pattern may be used for any ordinate to be formatted as plain number,
* for example in {@linkplain org.apache.sis.referencing.cs.DefaultCartesianCS Cartesian coordinate system}.</li>
* <li>If {@code valueType} is {@code Angle.class}, then the pattern syntax shall be as described in the
* {@link AngleFormat} class. This pattern may be used for any ordinate to be formatted as latitude or longitude,
* for example in {@linkplain org.apache.sis.referencing.cs.DefaultEllipsoidalCS ellipsoidal coordinate system}.</li>
* <li>If {@code valueType} is {@code Date.class}, then the pattern syntax shall be as described in the
* {@link SimpleDateFormat} class. This pattern may be used for any ordinate to be formatted as date and time,
* for example in {@linkplain org.apache.sis.referencing.cs.DefaultTimeCS time coordinate system}.</li>
* </ul>
*
* @param valueType the base type of ordinate values to parse and format:
* {@code Number.class}, {@code Angle.class} or {@code Date.class}.
* @param pattern the pattern as specified in {@link DecimalFormat}, {@link AngleFormat}
* or {@link SimpleDateFormat} javadoc.
* @return {@code true} if the pattern has been applied, or {@code false} if {@code valueType} does not
* specify a known type or if the format associated to that type does not support patterns.
* @throws IllegalArgumentException if the given pattern is invalid.
*/
public boolean applyPattern(final Class<?> valueType, final String pattern) {
ArgumentChecks.ensureNonNull("pattern", pattern);
final Format format = getFormat(valueType);
if (format instanceof DecimalFormat) {
((DecimalFormat) format).applyPattern(pattern);
} else if (format instanceof SimpleDateFormat) {
((SimpleDateFormat) format).applyPattern(pattern);
} else if (format instanceof AngleFormat) {
((AngleFormat) format).applyPattern(pattern);
} else {
return false;
}
return true;
}
use of org.apache.sis.measure.AngleFormat in project sis by apache.
the class LocationFormat method createFormat.
/**
* Creates the format to use for formatting a latitude, longitude or projected coordinate.
* This method is invoked by {@code format(Location, Appendable)} when first needed.
*
* @param valueType {@code Angle.class}. {@code Number.class} or {@code Unit.class}.
* @return a new {@link AngleFormat}, {@link NumberFormat} or {@link UnitFormat} instance
* depending on the argument value.
*/
@Override
protected Format createFormat(final Class<?> valueType) {
final Format f = super.createFormat(valueType);
if (f instanceof NumberFormat) {
final NumberFormat nf = (NumberFormat) f;
nf.setMinimumFractionDigits(0);
// 1 metre accuracy, assuming lengths in metres.
nf.setMaximumFractionDigits(0);
} else if (f instanceof AngleFormat) {
// 30 metres accuracy.
((AngleFormat) f).applyPattern("D°MM′SS″");
}
return f;
}
use of org.apache.sis.measure.AngleFormat 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;
}
Aggregations