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

Last change on this file since 9846 was 9846, checked in by wiktorn, 8 years ago

Divde by zero found by Coverity Scan.

See: #12350

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