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

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

fix #12919 - scale indicator regression (patch by michael2402)

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