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

Last change on this file since 18959 was 16894, checked in by simon04, 4 years ago

fix #4888 - SlippyMapBBoxChooser: show map scale

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