OrdinateFormat.java
/*
* Copyright (c) 2019 Martin Davis.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.io;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
/**
* Formats numeric values for ordinates
* in a consistent, accurate way.
* <p>
* The format has the following characteristics:
* <ul>
* <li>It is consistent in all locales (in particular, the decimal separator is always a period)
* <li>Scientific notation is never output, even for very large numbers.
* This means that it is possible that output can contain a large number of digits.
* <li>The maximum number of decimal places reflects the available precision
* <li>NaN values are represented as "NaN"
* <li>Inf values are represented as "Inf" or "-Inf"
* </ul>
*
* @author mdavis
*
*/
public class OrdinateFormat
{
private static final String DECIMAL_PATTERN = "0";
/**
* The output representation of {@link Double#POSITIVE_INFINITY}
*/
public static final String REP_POS_INF = "Inf";
/**
* The output representation of {@link Double#NEGATIVE_INFINITY}
*/
public static final String REP_NEG_INF = "-Inf";
/**
* The output representation of {@link Double#NaN}
*/
public static final String REP_NAN = "NaN";
/**
* The maximum number of fraction digits to support output of reasonable ordinate values.
*
* The default is chosen to allow representing the smallest possible IEEE-754 double-precision value,
* although this is not expected to occur (and is not supported by other areas of the JTS code).
*/
public static final int MAX_FRACTION_DIGITS = 325;
/**
* The default formatter using the maximum number of digits in the fraction portion of a number.
*/
public static OrdinateFormat DEFAULT = new OrdinateFormat();
/**
* Creates a new formatter with the given maximum number of digits in the fraction portion of a number.
*
* @param maximumFractionDigits the maximum number of fraction digits to output
* @return a formatter
*/
public static OrdinateFormat create(int maximumFractionDigits) {
return new OrdinateFormat(maximumFractionDigits);
}
private DecimalFormat format;
/**
* Creates an OrdinateFormat using the default maximum number of fraction digits.
*/
public OrdinateFormat() {
format = createFormat(MAX_FRACTION_DIGITS);
}
/**
* Creates an OrdinateFormat using the given maximum number of fraction digits.
*
* @param maximumFractionDigits the maximum number of fraction digits to output
*/
public OrdinateFormat(int maximumFractionDigits) {
format = createFormat(maximumFractionDigits);
}
private static DecimalFormat createFormat(int maximumFractionDigits) {
// ensure format uses standard WKY number format
NumberFormat nf = NumberFormat.getInstance(Locale.US);
// This is expected to succeed for Locale.US
DecimalFormat format;
try {
format = (DecimalFormat) nf;
}
catch (ClassCastException ex) {
throw new RuntimeException("Unable to create DecimalFormat for Locale.US");
}
format.applyPattern(DECIMAL_PATTERN);
format.setMaximumFractionDigits(maximumFractionDigits);
return format;
}
/**
* Returns a string representation of the given ordinate numeric value.
*
* @param ord the ordinate value
* @return the formatted number string
*/
public synchronized String format(double ord)
{
/**
* FUTURE: If it seems better to use scientific notation
* for very large/small numbers then this can be done here.
*/
if (Double.isNaN(ord)) return REP_NAN;
if (Double.isInfinite(ord)) {
return ord > 0 ? REP_POS_INF : REP_NEG_INF;
}
return format.format(ord);
}
}