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

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

see #11390 - sonar - squid:S1609 - Java 8: @FunctionalInterface annotation should be used to flag Single Abstract Method interfaces

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