Class OrdinateFormat

  1 /*
  2  * Copyright (c) 2019 Martin Davis.
  3  *
  4  * All rights reserved. This program and the accompanying materials
  5  * are made available under the terms of the Eclipse Public License 2.0
  6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
  7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
  8  * and the Eclipse Distribution License is available at
  9  *
 10  * http://www.eclipse.org/org/documents/edl-v10.php.
 11  */
 12  
 13 package org.locationtech.jts.io;
 14  
 15 import java.text.DecimalFormat;
 16 import java.text.DecimalFormatSymbols;
 17  
 18 /**
 19  * Formats numeric values for ordinates
 20  * in a consistent, accurate way.
 21  * <p>
 22  * The format has the following characteristics:
 23  * <ul>
 24  * <li>It is consistent in all locales (in particular, the decimal separator is always a period)
 25  * <li>Scientific notation is never output, even for very large numbers.
 26  * This means that it is possible that output can contain a large number of digits. 
 27  * <li>The maximum number of decimal places reflects the available precision
 28  * <li>NaN values are represented as "NaN"
 29  * <li>Inf values are represented as "Inf" or "-Inf"
 30  * </ul> 
 31  * 
 32  * @author mdavis
 33  *
 34  */
 35 public class OrdinateFormat
 36 {
 37   /**
 38    * The output representation of {@link Double#POSITIVE_INFINITY}
 39    */
 40   public static final String REP_POS_INF = "Inf";
 41  
 42   /**
 43    * The output representation of {@link Double#NEGATIVE_INFINITY}
 44    */
 45   public static final String REP_NEG_INF = "-Inf";
 46  
 47   /**
 48    * The output representation of {@link Double#NaN}
 49    */
 50   public static final String REP_NAN = "NaN";
 51  
 52   /**
 53    * The maximum number of fraction digits to support output of reasonable ordinate values.
 54    * 
 55    * The default is chosen to allow representing the smallest possible IEEE-754 double-precision value,
 56    * although this is not expected to occur (and is not supported by other areas of the JTS code).
 57    */
 58   public static final int MAX_FRACTION_DIGITS = 325;
 59   
 60   /**
 61    * The default formatter using the maximum number of digits in the fraction portion of a number.
 62    */
 63   public static OrdinateFormat DEFAULT = new OrdinateFormat();
 64  
 65   /**
 66    * Creates a new formatter with the given maximum number of digits in the fraction portion of a number.
 67    * 
 68    * @param maximumFractionDigits the maximum number of fraction digits to output
 69    * @return a formatter
 70    */
 71   public static OrdinateFormat create(int maximumFractionDigits) {
 72     return new OrdinateFormat(maximumFractionDigits);
 73   }
 74   
 75   private DecimalFormat format;
 76  
 77   /**
 78    * Creates an OrdinateFormat using the default maximum number of fraction digits.
 79    */
 80   public OrdinateFormat() {
 81     format = createFormat(MAX_FRACTION_DIGITS);
 82   }
 83  
 84   /**
 85    * Creates an OrdinateFormat using the given maximum number of fraction digits.
 86    * 
 87    * @param maximumFractionDigits the maximum number of fraction digits to output
 88    */
 89   public OrdinateFormat(int maximumFractionDigits) {
 90     format = createFormat(maximumFractionDigits);
 91   }
 92  
 93   private static DecimalFormat createFormat(int maximumFractionDigits) {
 94     // specify decimal separator explicitly to work in all locales
 95     DecimalFormatSymbols symbols = new DecimalFormatSymbols();
 96     symbols.setDecimalSeparator('.');
 97     DecimalFormat format = new DecimalFormat("0", symbols);
 98     format.setMaximumFractionDigits(maximumFractionDigits);
 99     return format;
100   }
101  
102   /**
103    * Returns a string representation of the given ordinate numeric value.
104    * 
105    * @param ord the ordinate value
106    * @return the formatted number string
107    */
108   public synchronized String format(double ord)
109   {
110     /**
111      * FUTURE: If it seems better to use scientific notation 
112      * for very large/small numbers then this can be done here.
113      */
114     
115     if (Double.isNaN(ord)) return REP_NAN;
116     if (Double.isInfinite(ord)) {
117       return ord > 0 ? REP_POS_INF : REP_NEG_INF;
118     }
119     return format.format(ord);
120   }
121  
122 }
123