001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.data; 003 004import static org.openstreetmap.josm.tools.I18n.marktr; 005 006import java.text.NumberFormat; 007import java.util.LinkedHashMap; 008import java.util.Locale; 009import java.util.Map; 010 011import org.openstreetmap.josm.Main; 012 013/** 014 * A system of units used to express length and area measurements. 015 * @since 3406 (creation) 016 * @since 6992 (extraction in this package) 017 */ 018public class SystemOfMeasurement { 019 020 /** 021 * Metric system (international standard). 022 * @since 3406 023 */ 024 public static final SystemOfMeasurement METRIC = new SystemOfMeasurement(1, "m", 1000, "km", 10000, "ha"); 025 026 /** 027 * Chinese system. 028 * @since 3406 029 */ 030 public static final SystemOfMeasurement CHINESE = new SystemOfMeasurement(1.0/3.0, "\u5e02\u5c3a" /* chi */, 500, "\u5e02\u91cc" /* li */); 031 032 /** 033 * Imperial system (British Commonwealth and former British Empire). 034 * @since 3406 035 */ 036 public static final SystemOfMeasurement IMPERIAL = new SystemOfMeasurement(0.3048, "ft", 1609.344, "mi", 4046.86, "ac"); 037 038 /** 039 * Nautical mile system (navigation, polar exploration). 040 * @since 5549 041 */ 042 public static final SystemOfMeasurement NAUTICAL_MILE = new SystemOfMeasurement(185.2, "kbl", 1852, "NM"); 043 044 /** 045 * Known systems of measurement. 046 * @since 3406 047 */ 048 public static final Map<String, SystemOfMeasurement> ALL_SYSTEMS; 049 static { 050 ALL_SYSTEMS = new LinkedHashMap<>(); 051 ALL_SYSTEMS.put(marktr("Metric"), METRIC); 052 ALL_SYSTEMS.put(marktr("Chinese"), CHINESE); 053 ALL_SYSTEMS.put(marktr("Imperial"), IMPERIAL); 054 ALL_SYSTEMS.put(marktr("Nautical Mile"), NAUTICAL_MILE); 055 } 056 057 /** First value, in meters, used to translate unit according to above formula. */ 058 public final double aValue; 059 /** Second value, in meters, used to translate unit according to above formula. */ 060 public final double bValue; 061 /** First unit used to format text. */ 062 public final String aName; 063 /** Second unit used to format text. */ 064 public final String bName; 065 /** Specific optional area value, in squared meters, between {@code aValue*aValue} and {@code bValue*bValue}. Set to {@code -1} if not used. 066 * @since 5870 */ 067 public final double areaCustomValue; 068 /** Specific optional area unit. Set to {@code null} if not used. 069 * @since 5870 */ 070 public final String areaCustomName; 071 072 /** 073 * System of measurement. Currently covers only length (and area) units. 074 * 075 * If a quantity x is given in m (x_m) and in unit a (x_a) then it translates as 076 * x_a == x_m / aValue 077 * 078 * @param aValue First value, in meters, used to translate unit according to above formula. 079 * @param aName First unit used to format text. 080 * @param bValue Second value, in meters, used to translate unit according to above formula. 081 * @param bName Second unit used to format text. 082 */ 083 public SystemOfMeasurement(double aValue, String aName, double bValue, String bName) { 084 this(aValue, aName, bValue, bName, -1, null); 085 } 086 087 /** 088 * System of measurement. Currently covers only length (and area) units. 089 * 090 * If a quantity x is given in m (x_m) and in unit a (x_a) then it translates as 091 * x_a == x_m / aValue 092 * 093 * @param aValue First value, in meters, used to translate unit according to above formula. 094 * @param aName First unit used to format text. 095 * @param bValue Second value, in meters, used to translate unit according to above formula. 096 * @param bName Second unit used to format text. 097 * @param areaCustomValue Specific optional area value, in squared meters, between {@code aValue*aValue} and {@code bValue*bValue}. 098 * Set to {@code -1} if not used. 099 * @param areaCustomName Specific optional area unit. Set to {@code null} if not used. 100 * 101 * @since 5870 102 */ 103 public SystemOfMeasurement(double aValue, String aName, double bValue, String bName, double areaCustomValue, String areaCustomName) { 104 this.aValue = aValue; 105 this.aName = aName; 106 this.bValue = bValue; 107 this.bName = bName; 108 this.areaCustomValue = areaCustomValue; 109 this.areaCustomName = areaCustomName; 110 } 111 112 /** 113 * Returns the text describing the given distance in this system of measurement. 114 * @param dist The distance in metres 115 * @return The text describing the given distance in this system of measurement. 116 */ 117 public String getDistText(double dist) { 118 return getDistText(dist, null, 0.01); 119 } 120 121 /** 122 * Returns the text describing the given distance in this system of measurement. 123 * @param dist The distance in metres 124 * @param format A {@link NumberFormat} to format the area value 125 * @param threshold Values lower than this {@code threshold} are displayed as {@code "< [threshold]"} 126 * @return The text describing the given distance in this system of measurement. 127 * @since 6422 128 */ 129 public String getDistText(final double dist, final NumberFormat format, final double threshold) { 130 double a = dist / aValue; 131 if (!Main.pref.getBoolean("system_of_measurement.use_only_lower_unit", false) && a > bValue / aValue) 132 return formatText(dist / bValue, bName, format); 133 else if (a < threshold) 134 return "< " + formatText(threshold, aName, format); 135 else 136 return formatText(a, aName, format); 137 } 138 139 /** 140 * Returns the text describing the given area in this system of measurement. 141 * @param area The area in square metres 142 * @return The text describing the given area in this system of measurement. 143 * @since 5560 144 */ 145 public String getAreaText(double area) { 146 return getAreaText(area, null, 0.01); 147 } 148 149 /** 150 * Returns the text describing the given area in this system of measurement. 151 * @param area The area in square metres 152 * @param format A {@link NumberFormat} to format the area value 153 * @param threshold Values lower than this {@code threshold} are displayed as {@code "< [threshold]"} 154 * @return The text describing the given area in this system of measurement. 155 * @since 6422 156 */ 157 public String getAreaText(final double area, final NumberFormat format, final double threshold) { 158 double a = area / (aValue*aValue); 159 boolean lowerOnly = Main.pref.getBoolean("system_of_measurement.use_only_lower_unit", false); 160 boolean customAreaOnly = Main.pref.getBoolean("system_of_measurement.use_only_custom_area_unit", false); 161 if ((!lowerOnly && areaCustomValue > 0 && a > areaCustomValue / (aValue*aValue) && a < (bValue*bValue) / (aValue*aValue)) || customAreaOnly) 162 return formatText(area / areaCustomValue, areaCustomName, format); 163 else if (!lowerOnly && a >= (bValue*bValue) / (aValue*aValue)) 164 return formatText(area / (bValue * bValue), bName + "\u00b2", format); 165 else if (a < threshold) 166 return "< " + formatText(threshold, aName + "\u00b2", format); 167 else 168 return formatText(a, aName + "\u00b2", format); 169 } 170 171 private static String formatText(double v, String unit, NumberFormat format) { 172 if (format != null) { 173 return format.format(v) + " " + unit; 174 } 175 return String.format(Locale.US, "%." + (v<9.999999 ? 2 : 1) + "f %s", v, unit); 176 } 177}