source: josm/trunk/src/org/openstreetmap/josm/data/SystemOfMeasurement.java@ 10308

Last change on this file since 10308 was 10292, checked in by Don-vip, 8 years ago

sonar - squid:S2275 - Format specifiers should be used instead of string concatenation

  • Property svn:eol-style set to native
File size: 11.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data;
3
4import static org.openstreetmap.josm.tools.I18n.marktr;
5
6import java.text.NumberFormat;
7import java.util.Collections;
8import java.util.LinkedHashMap;
9import java.util.Locale;
10import java.util.Map;
11import java.util.concurrent.CopyOnWriteArrayList;
12
13import org.openstreetmap.josm.Main;
14import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
15
16/**
17 * A system of units used to express length and area measurements.
18 * <p>
19 * This class also manages one globally set system of measurement stored in the {@link ProjectionPreference}
20 * @since 3406 (creation)
21 * @since 6992 (extraction in this package)
22 */
23public class SystemOfMeasurement {
24
25 /**
26 * Interface to notify listeners of the change of the system of measurement.
27 * @since 8554
28 */
29 public interface SoMChangeListener {
30 /**
31 * The current SoM has changed.
32 * @param oldSoM The old system of measurement
33 * @param newSoM The new (current) system of measurement
34 */
35 void systemOfMeasurementChanged(String oldSoM, String newSoM);
36 }
37
38 /**
39 * Metric system (international standard).
40 * @since 3406
41 */
42 public static final SystemOfMeasurement METRIC = new SystemOfMeasurement(1, "m", 1000, "km", "km/h", 3.6, 10000, "ha");
43
44 /**
45 * Chinese system.
46 * See <a href="https://en.wikipedia.org/wiki/Chinese_units_of_measurement#Chinese_length_units_effective_in_1930">length units</a>,
47 * <a href="https://en.wikipedia.org/wiki/Chinese_units_of_measurement#Chinese_area_units_effective_in_1930">area units</a>
48 * @since 3406
49 */
50 public static final SystemOfMeasurement CHINESE = new SystemOfMeasurement(1.0/3.0, "\u5e02\u5c3a" /* chi */, 500, "\u5e02\u91cc" /* li */,
51 "km/h", 3.6, 666.0 + 2.0/3.0, "\u4ea9" /* mu */);
52
53 /**
54 * Imperial system (British Commonwealth and former British Empire).
55 * @since 3406
56 */
57 public static final SystemOfMeasurement IMPERIAL = new SystemOfMeasurement(0.3048, "ft", 1609.344, "mi", "mph", 2.23694, 4046.86, "ac");
58
59 /**
60 * Nautical mile system (navigation, polar exploration).
61 * @since 5549
62 */
63 public static final SystemOfMeasurement NAUTICAL_MILE = new SystemOfMeasurement(185.2, "kbl", 1852, "NM", "kn", 1.94384);
64
65 /**
66 * Known systems of measurement.
67 * @since 3406
68 */
69 public static final Map<String, SystemOfMeasurement> ALL_SYSTEMS;
70 static {
71 Map<String, SystemOfMeasurement> map = new LinkedHashMap<>();
72 map.put(marktr("Metric"), METRIC);
73 map.put(marktr("Chinese"), CHINESE);
74 map.put(marktr("Imperial"), IMPERIAL);
75 map.put(marktr("Nautical Mile"), NAUTICAL_MILE);
76 ALL_SYSTEMS = Collections.unmodifiableMap(map);
77 }
78
79 private static final CopyOnWriteArrayList<SoMChangeListener> somChangeListeners = new CopyOnWriteArrayList<>();
80
81 /**
82 * Removes a global SoM change listener.
83 *
84 * @param listener the listener. Ignored if null or already absent
85 * @since 8554
86 */
87 public static void removeSoMChangeListener(SoMChangeListener listener) {
88 somChangeListeners.remove(listener);
89 }
90
91 /**
92 * Adds a SoM change listener.
93 *
94 * @param listener the listener. Ignored if null or already registered.
95 * @since 8554
96 */
97 public static void addSoMChangeListener(SoMChangeListener listener) {
98 if (listener != null) {
99 somChangeListeners.addIfAbsent(listener);
100 }
101 }
102
103 protected static void fireSoMChanged(String oldSoM, String newSoM) {
104 for (SoMChangeListener l : somChangeListeners) {
105 l.systemOfMeasurementChanged(oldSoM, newSoM);
106 }
107 }
108
109 /**
110 * Returns the current global system of measurement.
111 * @return The current system of measurement (metric system by default).
112 * @since 8554
113 */
114 public static SystemOfMeasurement getSystemOfMeasurement() {
115 SystemOfMeasurement som = SystemOfMeasurement.ALL_SYSTEMS.get(ProjectionPreference.PROP_SYSTEM_OF_MEASUREMENT.get());
116 if (som == null)
117 return SystemOfMeasurement.METRIC;
118 return som;
119 }
120
121 /**
122 * Sets the current global system of measurement.
123 * @param somKey The system of measurement key. Must be defined in {@link SystemOfMeasurement#ALL_SYSTEMS}.
124 * @throws IllegalArgumentException if {@code somKey} is not known
125 * @since 8554
126 */
127 public static void setSystemOfMeasurement(String somKey) {
128 if (!SystemOfMeasurement.ALL_SYSTEMS.containsKey(somKey)) {
129 throw new IllegalArgumentException("Invalid system of measurement: "+somKey);
130 }
131 String oldKey = ProjectionPreference.PROP_SYSTEM_OF_MEASUREMENT.get();
132 if (ProjectionPreference.PROP_SYSTEM_OF_MEASUREMENT.put(somKey)) {
133 fireSoMChanged(oldKey, somKey);
134 }
135 }
136
137 /** First value, in meters, used to translate unit according to above formula. */
138 public final double aValue;
139 /** Second value, in meters, used to translate unit according to above formula. */
140 public final double bValue;
141 /** First unit used to format text. */
142 public final String aName;
143 /** Second unit used to format text. */
144 public final String bName;
145 /** Speed value for the most common speed symbol, in meters per second
146 * @since 10175 */
147 public final double speedValue;
148 /** Most common speed symbol (kmh/h, mph, kn, etc.)
149 * @since 10175 */
150 public final String speedName;
151 /** Specific optional area value, in squared meters, between {@code aValue*aValue} and {@code bValue*bValue}. Set to {@code -1} if not used.
152 * @since 5870 */
153 public final double areaCustomValue;
154 /** Specific optional area unit. Set to {@code null} if not used.
155 * @since 5870 */
156 public final String areaCustomName;
157
158 /**
159 * System of measurement. Currently covers only length (and area) units.
160 *
161 * If a quantity x is given in m (x_m) and in unit a (x_a) then it translates as
162 * x_a == x_m / aValue
163 *
164 * @param aValue First value, in meters, used to translate unit according to above formula.
165 * @param aName First unit used to format text.
166 * @param bValue Second value, in meters, used to translate unit according to above formula.
167 * @param bName Second unit used to format text.
168 * @param speedName the most common speed symbol (kmh/h, mph, kn, etc.)
169 * @param speedValue the speed value for the most common speed symbol, for 1 meter per second
170 * @since 10175
171 */
172 public SystemOfMeasurement(double aValue, String aName, double bValue, String bName, String speedName, double speedValue) {
173 this(aValue, aName, bValue, bName, speedName, speedValue, -1, null);
174 }
175
176 /**
177 * System of measurement. Currently covers only length (and area) units.
178 *
179 * If a quantity x is given in m (x_m) and in unit a (x_a) then it translates as
180 * x_a == x_m / aValue
181 *
182 * @param aValue First value, in meters, used to translate unit according to above formula.
183 * @param aName First unit used to format text.
184 * @param bValue Second value, in meters, used to translate unit according to above formula.
185 * @param bName Second unit used to format text.
186 * @param speedName the most common speed symbol (kmh/h, mph, kn, etc.)
187 * @param speedValue the speed value for the most common speed symbol, for 1 meter per second
188 * @param areaCustomValue Specific optional area value, in squared meters, between {@code aValue*aValue} and {@code bValue*bValue}.
189 * Set to {@code -1} if not used.
190 * @param areaCustomName Specific optional area unit. Set to {@code null} if not used.
191 *
192 * @since 10175
193 */
194 public SystemOfMeasurement(double aValue, String aName, double bValue, String bName, String speedName, double speedValue,
195 double areaCustomValue, String areaCustomName) {
196 this.aValue = aValue;
197 this.aName = aName;
198 this.bValue = bValue;
199 this.bName = bName;
200 this.speedValue = speedValue;
201 this.speedName = speedName;
202 this.areaCustomValue = areaCustomValue;
203 this.areaCustomName = areaCustomName;
204 }
205
206 /**
207 * Returns the text describing the given distance in this system of measurement.
208 * @param dist The distance in metres
209 * @return The text describing the given distance in this system of measurement.
210 */
211 public String getDistText(double dist) {
212 return getDistText(dist, null, 0.01);
213 }
214
215 /**
216 * Returns the text describing the given distance in this system of measurement.
217 * @param dist The distance in metres
218 * @param format A {@link NumberFormat} to format the area value
219 * @param threshold Values lower than this {@code threshold} are displayed as {@code "< [threshold]"}
220 * @return The text describing the given distance in this system of measurement.
221 * @since 6422
222 */
223 public String getDistText(final double dist, final NumberFormat format, final double threshold) {
224 double a = dist / aValue;
225 if (!Main.pref.getBoolean("system_of_measurement.use_only_lower_unit", false) && a > bValue / aValue)
226 return formatText(dist / bValue, bName, format);
227 else if (a < threshold)
228 return "< " + formatText(threshold, aName, format);
229 else
230 return formatText(a, aName, format);
231 }
232
233 /**
234 * Returns the text describing the given area in this system of measurement.
235 * @param area The area in square metres
236 * @return The text describing the given area in this system of measurement.
237 * @since 5560
238 */
239 public String getAreaText(double area) {
240 return getAreaText(area, null, 0.01);
241 }
242
243 /**
244 * Returns the text describing the given area in this system of measurement.
245 * @param area The area in square metres
246 * @param format A {@link NumberFormat} to format the area value
247 * @param threshold Values lower than this {@code threshold} are displayed as {@code "< [threshold]"}
248 * @return The text describing the given area in this system of measurement.
249 * @since 6422
250 */
251 public String getAreaText(final double area, final NumberFormat format, final double threshold) {
252 double a = area / (aValue*aValue);
253 boolean lowerOnly = Main.pref.getBoolean("system_of_measurement.use_only_lower_unit", false);
254 boolean customAreaOnly = Main.pref.getBoolean("system_of_measurement.use_only_custom_area_unit", false);
255 if ((!lowerOnly && areaCustomValue > 0 && a > areaCustomValue / (aValue*aValue)
256 && a < (bValue*bValue) / (aValue*aValue)) || customAreaOnly)
257 return formatText(area / areaCustomValue, areaCustomName, format);
258 else if (!lowerOnly && a >= (bValue*bValue) / (aValue*aValue))
259 return formatText(area / (bValue * bValue), bName + '\u00b2', format);
260 else if (a < threshold)
261 return "< " + formatText(threshold, aName + '\u00b2', format);
262 else
263 return formatText(a, aName + '\u00b2', format);
264 }
265
266 private static String formatText(double v, String unit, NumberFormat format) {
267 if (format != null) {
268 return format.format(v) + ' ' + unit;
269 }
270 return String.format(Locale.US, v < 9.999999 ? "%.2f %s" : "%.1f %s", v, unit);
271 }
272}
Note: See TracBrowser for help on using the repository browser.