source: josm/trunk/src/org/openstreetmap/josm/gui/MapScaler.java@ 13038

Last change on this file since 13038 was 12987, checked in by bastiK, 7 years ago

see #15410 - change preferences scheme for named colors - makes runtime color name registry obsolete

  • Property svn:eol-style set to native
File size: 6.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui;
3
4import static org.openstreetmap.josm.gui.help.HelpUtil.ht;
5import static org.openstreetmap.josm.tools.I18n.marktr;
6
7import java.awt.Color;
8import java.awt.Dimension;
9import java.awt.Graphics;
10import java.awt.geom.Rectangle2D;
11
12import javax.accessibility.Accessible;
13import javax.accessibility.AccessibleContext;
14import javax.accessibility.AccessibleValue;
15import javax.swing.JComponent;
16
17import org.openstreetmap.josm.data.preferences.NamedColorProperty;
18import org.openstreetmap.josm.gui.help.Helpful;
19
20/**
21 * Map scale bar, displaying the distance in meter that correspond to 100 px on screen.
22 * @since 115
23 */
24public class MapScaler extends JComponent implements Helpful, Accessible {
25
26 private final NavigatableComponent mv;
27
28 private static final int PADDING_LEFT = 5;
29 private static final int PADDING_RIGHT = 50;
30
31 private static final NamedColorProperty SCALER_COLOR = new NamedColorProperty(marktr("scale"), Color.WHITE);
32
33 /**
34 * Constructs a new {@code MapScaler}.
35 * @param mv map view
36 */
37 public MapScaler(NavigatableComponent mv) {
38 this.mv = mv;
39 setPreferredLineLength(100);
40 setOpaque(false);
41 }
42
43 /**
44 * Sets the preferred length the distance line should have.
45 * @param pixel The length.
46 */
47 public void setPreferredLineLength(int pixel) {
48 setPreferredSize(new Dimension(pixel + PADDING_LEFT + PADDING_RIGHT, 30));
49 }
50
51 @Override
52 public void paint(Graphics g) {
53 g.setColor(getColor());
54
55 double dist100Pixel = mv.getDist100Pixel(true);
56 TickMarks tickMarks = new TickMarks(dist100Pixel, getWidth() - PADDING_LEFT - PADDING_RIGHT);
57 tickMarks.paintTicks(g);
58 }
59
60 /**
61 * Returns the color of map scaler.
62 * @return the color of map scaler
63 */
64 public static Color getColor() {
65 return SCALER_COLOR.get();
66 }
67
68 @Override
69 public String helpTopic() {
70 return ht("/MapView/Scaler");
71 }
72
73 @Override
74 public AccessibleContext getAccessibleContext() {
75 if (accessibleContext == null) {
76 accessibleContext = new AccessibleMapScaler();
77 }
78 return accessibleContext;
79 }
80
81 class AccessibleMapScaler extends AccessibleJComponent implements AccessibleValue {
82
83 @Override
84 public Number getCurrentAccessibleValue() {
85 return mv.getDist100Pixel();
86 }
87
88 @Override
89 public boolean setCurrentAccessibleValue(Number n) {
90 return false;
91 }
92
93 @Override
94 public Number getMinimumAccessibleValue() {
95 return null;
96 }
97
98 @Override
99 public Number getMaximumAccessibleValue() {
100 return null;
101 }
102 }
103
104 /**
105 * This class finds the best possible tick mark positions.
106 * <p>
107 * It will attempt to use steps of 1m, 2.5m, 10m, 25m, ...
108 */
109 private static final class TickMarks {
110
111 private final double dist100Pixel;
112 /**
113 * Distance in meters between two ticks.
114 */
115 private final double spacingMeter;
116 private final int steps;
117 private final int minorStepsPerMajor;
118
119 /**
120 * Creates a new tick mark helper.
121 * @param dist100Pixel The distance of 100 pixel on the map.
122 * @param width The width of the mark.
123 */
124 TickMarks(double dist100Pixel, int width) {
125 this.dist100Pixel = dist100Pixel;
126 double lineDistance = dist100Pixel * width / 100;
127
128 double log10 = Math.log(lineDistance) / Math.log(10);
129 double spacingLog10 = Math.pow(10, Math.floor(log10));
130 int minorStepsPerMajor;
131 double distanceBetweenMinor;
132 if (log10 - Math.floor(log10) < .75) {
133 // Add 2 ticks for every full unit
134 distanceBetweenMinor = spacingLog10 / 2;
135 minorStepsPerMajor = 2;
136 } else {
137 // Add 10 ticks for every full unit
138 distanceBetweenMinor = spacingLog10;
139 minorStepsPerMajor = 5;
140 }
141 // round down to the last major step.
142 int majorSteps = (int) Math.floor(lineDistance / distanceBetweenMinor / minorStepsPerMajor);
143 if (majorSteps >= 4) {
144 // we have many major steps, do not paint the minor now.
145 this.spacingMeter = distanceBetweenMinor * minorStepsPerMajor;
146 this.minorStepsPerMajor = 1;
147 } else {
148 this.minorStepsPerMajor = minorStepsPerMajor;
149 this.spacingMeter = distanceBetweenMinor;
150 }
151 steps = majorSteps * this.minorStepsPerMajor;
152 }
153
154 /**
155 * Paint the ticks to the graphics.
156 * @param g The graphics to paint on.
157 */
158 public void paintTicks(Graphics g) {
159 double spacingPixel = spacingMeter / (dist100Pixel / 100);
160 double textBlockedUntil = -1;
161 for (int step = 0; step <= steps; step++) {
162 int x = (int) (PADDING_LEFT + spacingPixel * step);
163 boolean isMajor = step % minorStepsPerMajor == 0;
164 int paddingY = isMajor ? 0 : 3;
165 g.drawLine(x, paddingY, x, 10 - paddingY);
166
167 if (step == 0 || step == steps) {
168 String text;
169 if (step == 0) {
170 text = "0";
171 } else {
172 text = NavigatableComponent.getDistText(spacingMeter * step);
173 }
174 Rectangle2D bound = g.getFontMetrics().getStringBounds(text, g);
175 int left = (int) (x - bound.getWidth() / 2);
176 if (textBlockedUntil > left) {
177 left = (int) (textBlockedUntil + 5);
178 }
179 g.drawString(text, left, 23);
180 textBlockedUntil = left + bound.getWidth() + 2;
181 }
182 }
183 g.drawLine(PADDING_LEFT + 0, 5, (int) (PADDING_LEFT + spacingPixel * steps), 5);
184 }
185 }
186}
Note: See TracBrowser for help on using the repository browser.