source: josm/trunk/src/org/openstreetmap/josm/gui/layer/NativeScaleLayer.java@ 10600

Last change on this file since 10600 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

File size: 9.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.layer;
3
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.List;
7
8import org.openstreetmap.josm.gui.NavigatableComponent;
9
10/**
11 * Represents a layer that has native scales.
12 * @author András Kolesár
13 * @since 9818 (creation)
14 * @since 10600 (functional interface)
15 */
16@FunctionalInterface
17public interface NativeScaleLayer {
18
19 /**
20 * Get native scales of this layer.
21 * @return {@link ScaleList} of native scales
22 */
23 ScaleList getNativeScales();
24
25 /**
26 * Represents a scale with native flag, used in {@link ScaleList}
27 */
28 class Scale {
29 /**
30 * Scale factor, same unit as in {@link NavigatableComponent}
31 */
32 private final double scale;
33
34 /**
35 * True if this scale is native resolution for data source.
36 */
37 private final boolean isNative;
38
39 private final int index;
40
41 /**
42 * Constructs a new Scale with given scale, native defaults to true.
43 * @param scale as defined in WMTS (scaleDenominator)
44 * @param index zoom index for this scale
45 */
46 public Scale(double scale, int index) {
47 this.scale = scale;
48 this.isNative = true;
49 this.index = index;
50 }
51
52 /**
53 * Constructs a new Scale with given scale, native and index values.
54 * @param scale as defined in WMTS (scaleDenominator)
55 * @param isNative is this scale native to the source or not
56 * @param index zoom index for this scale
57 */
58 public Scale(double scale, boolean isNative, int index) {
59 this.scale = scale;
60 this.isNative = isNative;
61 this.index = index;
62 }
63
64 @Override
65 public String toString() {
66 return String.format("%f [%s]", scale, isNative);
67 }
68
69 /**
70 * Get index of this scale in a {@link ScaleList}
71 * @return index
72 */
73 public int getIndex() {
74 return index;
75 }
76
77 public double getScale() {
78 return scale;
79 }
80 }
81
82 /**
83 * List of scales, may include intermediate steps between native resolutions
84 */
85 class ScaleList {
86 private final List<Scale> scales = new ArrayList<>();
87
88 protected ScaleList() {
89 }
90
91 public ScaleList(Collection<Double> scales) {
92 int i = 0;
93 for (Double scale: scales) {
94 this.scales.add(new Scale(scale, i++));
95 }
96 }
97
98 protected void addScale(Scale scale) {
99 scales.add(scale);
100 }
101
102 /**
103 * Returns a ScaleList that has intermediate steps between native scales.
104 * Native steps are split to equal steps near given ratio.
105 * @param ratio user defined zoom ratio
106 * @return a {@link ScaleList} with intermediate steps
107 */
108 public ScaleList withIntermediateSteps(double ratio) {
109 ScaleList result = new ScaleList();
110 Scale previous = null;
111 for (Scale current: this.scales) {
112 if (previous != null) {
113 double step = previous.scale / current.scale;
114 double factor = Math.log(step) / Math.log(ratio);
115 int steps = (int) Math.round(factor);
116 if (steps != 0) {
117 double smallStep = Math.pow(step, 1.0/steps);
118 for (int j = 1; j < steps; j++) {
119 double intermediate = previous.scale / Math.pow(smallStep, j);
120 result.addScale(new Scale(intermediate, false, current.index));
121 }
122 }
123 }
124 result.addScale(current);
125 previous = current;
126 }
127 return result;
128 }
129
130 /**
131 * Get a scale from this ScaleList or a new scale if zoomed outside.
132 * @param scale previous scale
133 * @param floor use floor instead of round, set true when fitting view to objects
134 * @return new {@link Scale}
135 */
136 public Scale getSnapScale(double scale, boolean floor) {
137 return getSnapScale(scale, NavigatableComponent.PROP_ZOOM_RATIO.get(), floor);
138 }
139
140 /**
141 * Get a scale from this ScaleList or a new scale if zoomed outside.
142 * @param scale previous scale
143 * @param ratio zoom ratio from starting from previous scale
144 * @param floor use floor instead of round, set true when fitting view to objects
145 * @return new {@link Scale}
146 */
147 public Scale getSnapScale(double scale, double ratio, boolean floor) {
148 if (scales.isEmpty())
149 return null;
150 int size = scales.size();
151 Scale first = scales.get(0);
152 Scale last = scales.get(size-1);
153
154 if (scale > first.scale) {
155 double step = scale / first.scale;
156 double factor = Math.log(step) / Math.log(ratio);
157 int steps = (int) (floor ? Math.floor(factor) : Math.round(factor));
158 if (steps == 0) {
159 return new Scale(first.scale, first.isNative, steps);
160 } else {
161 return new Scale(first.scale * Math.pow(ratio, steps), false, steps);
162 }
163 } else if (scale < last.scale) {
164 double step = last.scale / scale;
165 double factor = Math.log(step) / Math.log(ratio);
166 int steps = (int) (floor ? Math.floor(factor) : Math.round(factor));
167 if (steps == 0) {
168 return new Scale(last.scale, last.isNative, size-1+steps);
169 } else {
170 return new Scale(last.scale / Math.pow(ratio, steps), false, size-1+steps);
171 }
172 } else {
173 Scale previous = null;
174 for (int i = 0; i < size; i++) {
175 Scale current = this.scales.get(i);
176 if (previous != null) {
177 if (scale <= previous.scale && scale >= current.scale) {
178 if (floor || previous.scale / scale < scale / current.scale) {
179 return new Scale(previous.scale, previous.isNative, i-1);
180 } else {
181 return new Scale(current.scale, current.isNative, i);
182 }
183 }
184 }
185 previous = current;
186 }
187 return null;
188 }
189 }
190
191 /**
192 * Get new scale for zoom in/out with a ratio at a number of times.
193 * Used by mousewheel zoom where wheel can step more than one between events.
194 * @param scale previois scale
195 * @param ratio user defined zoom ratio
196 * @param times number of times to zoom
197 * @return new {@link Scale} object from {@link ScaleList} or outside
198 */
199 public Scale scaleZoomTimes(double scale, double ratio, int times) {
200 Scale next = getSnapScale(scale, ratio, false);
201 int abs = Math.abs(times);
202 for (int i = 0; i < abs; i++) {
203 if (times < 0) {
204 next = getNextIn(next, ratio);
205 } else {
206 next = getNextOut(next, ratio);
207 }
208 }
209 return next;
210 }
211
212 /**
213 * Get new scale for zoom in.
214 * @param scale previous scale
215 * @param ratio user defined zoom ratio
216 * @return next scale in list or a new scale when zoomed outside
217 */
218 public Scale scaleZoomIn(double scale, double ratio) {
219 Scale snap = getSnapScale(scale, ratio, false);
220 return getNextIn(snap, ratio);
221 }
222
223 /**
224 * Get new scale for zoom out.
225 * @param scale previous scale
226 * @param ratio user defined zoom ratio
227 * @return next scale in list or a new scale when zoomed outside
228 */
229 public Scale scaleZoomOut(double scale, double ratio) {
230 Scale snap = getSnapScale(scale, ratio, false);
231 return getNextOut(snap, ratio);
232 }
233
234 @Override
235 public String toString() {
236 StringBuilder stringBuilder = new StringBuilder();
237 for (Scale s: this.scales) {
238 stringBuilder.append(s.toString() + '\n');
239 }
240 return stringBuilder.toString();
241 }
242
243 private Scale getNextIn(Scale scale, double ratio) {
244 if (scale == null)
245 return null;
246 int nextIndex = scale.getIndex() + 1;
247 if (nextIndex <= 0 || nextIndex > this.scales.size()-1) {
248 return new Scale(scale.scale / ratio, nextIndex == 0, nextIndex);
249 } else {
250 Scale nextScale = this.scales.get(nextIndex);
251 return new Scale(nextScale.scale, nextScale.isNative, nextIndex);
252 }
253 }
254
255 private Scale getNextOut(Scale scale, double ratio) {
256 if (scale == null)
257 return null;
258 int nextIndex = scale.getIndex() - 1;
259 if (nextIndex < 0 || nextIndex >= this.scales.size()-1) {
260 return new Scale(scale.scale * ratio, nextIndex == this.scales.size()-1, nextIndex);
261 } else {
262 Scale nextScale = this.scales.get(nextIndex);
263 return new Scale(nextScale.scale, nextScale.isNative, nextIndex);
264 }
265 }
266 }
267}
Note: See TracBrowser for help on using the repository browser.