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

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

see #19251 - Java 8: use Stream

File size: 9.4 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;
7import java.util.Locale;
8import java.util.stream.Collectors;
9
10import org.openstreetmap.josm.gui.NavigatableComponent;
11
12/**
13 * Represents a layer that has native scales.
14 * @author András Kolesár
15 * @since 9818 (creation)
16 * @since 10600 (functional interface)
17 */
18@FunctionalInterface
19public interface NativeScaleLayer {
20
21 /**
22 * Get native scales of this layer.
23 * @return {@link ScaleList} of native scales
24 */
25 ScaleList getNativeScales();
26
27 /**
28 * Represents a scale with native flag, used in {@link ScaleList}
29 */
30 class Scale {
31 /**
32 * Scale factor, same unit as in {@link NavigatableComponent}
33 */
34 private final double scale;
35
36 /**
37 * True if this scale is native resolution for data source.
38 */
39 private final boolean isNative;
40
41 private final int index;
42
43 /**
44 * Constructs a new Scale with given scale, native defaults to true.
45 * @param scale as defined in WMTS (scaleDenominator)
46 * @param index zoom index for this scale
47 */
48 public Scale(double scale, int index) {
49 this.scale = scale;
50 this.isNative = true;
51 this.index = index;
52 }
53
54 /**
55 * Constructs a new Scale with given scale, native and index values.
56 * @param scale as defined in WMTS (scaleDenominator)
57 * @param isNative is this scale native to the source or not
58 * @param index zoom index for this scale
59 */
60 public Scale(double scale, boolean isNative, int index) {
61 this.scale = scale;
62 this.isNative = isNative;
63 this.index = index;
64 }
65
66 @Override
67 public String toString() {
68 return String.format(Locale.ENGLISH, "%f [%s]", scale, isNative);
69 }
70
71 /**
72 * Get index of this scale in a {@link ScaleList}
73 * @return index
74 */
75 public int getIndex() {
76 return index;
77 }
78
79 public double getScale() {
80 return scale;
81 }
82 }
83
84 /**
85 * List of scales, may include intermediate steps between native resolutions
86 */
87 class ScaleList {
88 private final List<Scale> scales = new ArrayList<>();
89
90 protected ScaleList() {
91 }
92
93 public ScaleList(Collection<Double> scales) {
94 int i = 0;
95 for (Double scale: scales) {
96 this.scales.add(new Scale(scale, i++));
97 }
98 }
99
100 protected void addScale(Scale scale) {
101 scales.add(scale);
102 }
103
104 /**
105 * Returns a ScaleList that has intermediate steps between native scales.
106 * Native steps are split to equal steps near given ratio.
107 * @param ratio user defined zoom ratio
108 * @return a {@link ScaleList} with intermediate steps
109 */
110 public ScaleList withIntermediateSteps(double ratio) {
111 ScaleList result = new ScaleList();
112 Scale previous = null;
113 for (Scale current: this.scales) {
114 if (previous != null) {
115 double step = previous.scale / current.scale;
116 double factor = Math.log(step) / Math.log(ratio);
117 int steps = (int) Math.round(factor);
118 if (steps != 0) {
119 double smallStep = Math.pow(step, 1.0/steps);
120 for (int j = 1; j < steps; j++) {
121 double intermediate = previous.scale / Math.pow(smallStep, j);
122 result.addScale(new Scale(intermediate, false, current.index));
123 }
124 }
125 }
126 result.addScale(current);
127 previous = current;
128 }
129 return result;
130 }
131
132 /**
133 * Get a scale from this ScaleList or a new scale if zoomed outside.
134 * @param scale previous scale
135 * @param floor use floor instead of round, set true when fitting view to objects
136 * @return new {@link Scale}
137 */
138 public Scale getSnapScale(double scale, boolean floor) {
139 return getSnapScale(scale, NavigatableComponent.PROP_ZOOM_RATIO.get(), floor);
140 }
141
142 /**
143 * Get a scale from this ScaleList or a new scale if zoomed outside.
144 * @param scale previous scale
145 * @param ratio zoom ratio from starting from previous scale
146 * @param floor use floor instead of round, set true when fitting view to objects
147 * @return new {@link Scale}
148 */
149 public Scale getSnapScale(double scale, double ratio, boolean floor) {
150 if (scales.isEmpty())
151 return null;
152 int size = scales.size();
153 Scale first = scales.get(0);
154 Scale last = scales.get(size-1);
155
156 if (scale > first.scale) {
157 double step = scale / first.scale;
158 double factor = Math.log(step) / Math.log(ratio);
159 int steps = (int) (floor ? Math.floor(factor) : Math.round(factor));
160 if (steps == 0) {
161 return new Scale(first.scale, first.isNative, steps);
162 } else {
163 return new Scale(first.scale * Math.pow(ratio, steps), false, steps);
164 }
165 } else if (scale < last.scale) {
166 double step = last.scale / scale;
167 double factor = Math.log(step) / Math.log(ratio);
168 int steps = (int) (floor ? Math.floor(factor) : Math.round(factor));
169 if (steps == 0) {
170 return new Scale(last.scale, last.isNative, size-1+steps);
171 } else {
172 return new Scale(last.scale / Math.pow(ratio, steps), false, size-1+steps);
173 }
174 } else {
175 Scale previous = null;
176 for (int i = 0; i < size; i++) {
177 Scale current = this.scales.get(i);
178 if (previous != null && scale <= previous.scale && scale >= current.scale) {
179 if (floor || previous.scale / scale < scale / current.scale) {
180 return new Scale(previous.scale, previous.isNative, i-1);
181 } else {
182 return new Scale(current.scale, current.isNative, i);
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 return this.scales.stream().map(Scale::toString).collect(Collectors.joining("\n"));
237 }
238
239 private Scale getNextIn(Scale scale, double ratio) {
240 if (scale == null)
241 return null;
242 int nextIndex = scale.getIndex() + 1;
243 if (nextIndex <= 0 || nextIndex > this.scales.size()-1) {
244 return new Scale(scale.scale / ratio, nextIndex == 0, nextIndex);
245 } else {
246 Scale nextScale = this.scales.get(nextIndex);
247 return new Scale(nextScale.scale, nextScale.isNative, nextIndex);
248 }
249 }
250
251 private Scale getNextOut(Scale scale, double ratio) {
252 if (scale == null)
253 return null;
254 int nextIndex = scale.getIndex() - 1;
255 if (nextIndex < 0 || nextIndex >= this.scales.size()-1) {
256 return new Scale(scale.scale * ratio, nextIndex == this.scales.size()-1, nextIndex);
257 } else {
258 Scale nextScale = this.scales.get(nextIndex);
259 return new Scale(nextScale.scale, nextScale.isNative, nextIndex);
260 }
261 }
262 }
263}
Note: See TracBrowser for help on using the repository browser.