@@ -23,13 +23,17 @@ public abstract class GeometrySimplifier<T extends Geometry> {
23
23
protected final int maxPoints ;
24
24
protected final SimplificationErrorCalculator calculator ;
25
25
protected final PointError [] points ;
26
+ protected final Monitor monitor ;
26
27
protected int length ;
28
+ protected String description ;
27
29
28
30
protected final PriorityQueue <PointError > queue = new PriorityQueue <>();
29
31
30
- public GeometrySimplifier (int maxPoints , SimplificationErrorCalculator calculator ) {
32
+ protected GeometrySimplifier (String description , int maxPoints , SimplificationErrorCalculator calculator , Monitor monitor ) {
33
+ this .description = description ;
31
34
this .maxPoints = maxPoints ;
32
35
this .calculator = calculator ;
36
+ this .monitor = monitor ;
33
37
this .points = new PointError [maxPoints ];
34
38
this .length = 0 ;
35
39
}
@@ -61,9 +65,11 @@ public void consume(double x, double y) {
61
65
// Remove point with lowest error
62
66
PointError toRemove = queue .remove ();
63
67
removeAndAdd (toRemove .index , pointError );
68
+ notifyMonitorPointRemoved (toRemove );
64
69
} else {
65
70
this .points [length ] = pointError ;
66
71
length ++;
72
+ notifyMonitorPointAdded ();
67
73
}
68
74
}
69
75
@@ -134,22 +140,100 @@ public String toString() {
134
140
}
135
141
136
142
@ Override
137
- public double getX () {
143
+ public double x () {
138
144
return x ;
139
145
}
140
146
141
147
@ Override
142
- public double getY () {
148
+ public double y () {
143
149
return y ;
144
150
}
145
151
}
146
152
153
+ /**
154
+ * Implementation of this interface will receive calls with internal data at each step of the
155
+ * simplification algorithm. This is of use for debugging complex cases, as well as gaining insight
156
+ * into the way the algorithm works. Data provided in the callback includes:
157
+ * <ul>
158
+ * <li>String description of current process</li>
159
+ * <li>List of points in current simplification</li>
160
+ * <li>Last point removed from the simplification</li>
161
+ * </ul>
162
+ * mode, list of points representing the current linked-list of internal nodes used for
163
+ * triangulation, and a list of triangles so far created by the algorithm.
164
+ */
165
+ public interface Monitor {
166
+ /** Every time a point is added to the collection, this method sends the resulting state */
167
+ void pointAdded (String status , List <SimplificationErrorCalculator .PointLike > points );
168
+
169
+ /** Every time a point is added and another is removed from the collection, this method sends the resulting state */
170
+ void pointRemoved (
171
+ String status ,
172
+ List <SimplificationErrorCalculator .PointLike > points ,
173
+ SimplificationErrorCalculator .PointLike removed ,
174
+ double error ,
175
+ SimplificationErrorCalculator .PointLike previous ,
176
+ SimplificationErrorCalculator .PointLike next
177
+ );
178
+
179
+ /**
180
+ * When a new simplification or sub-simplification starts, this provides a description of the simplification,
181
+ * as well as the current maxPoints target for this simplification. For a single simplification, maxPoints
182
+ * will simply be the value passed to the constructor, but compound simplifications will calculate smaller
183
+ * numbers for sub-simplifications (eg. holes in polygons, or shells in multi-polygons).
184
+ */
185
+ void startSimplification (String description , int maxPoints );
186
+
187
+ /**
188
+ * When simplification or sub-simplification is completed, this is called.
189
+ */
190
+ void endSimplification (String description , List <SimplificationErrorCalculator .PointLike > points );
191
+ }
192
+
193
+ protected void notifyMonitorSimplificationStart () {
194
+ if (monitor != null ) {
195
+ monitor .startSimplification (description , maxPoints );
196
+ }
197
+ }
198
+
199
+ protected void notifyMonitorSimplificationEnd () {
200
+ if (monitor != null ) {
201
+ monitor .endSimplification (description , getCurrentPoints ());
202
+ }
203
+ }
204
+
205
+ protected void notifyMonitorPointRemoved (PointError removed ) {
206
+ if (monitor != null ) {
207
+ PointError previous = points [removed .index - 1 ];
208
+ PointError next = points [removed .index ];
209
+ monitor .pointRemoved (description + ".addAndRemovePoint()" , getCurrentPoints (), removed , removed .error , previous , next );
210
+ }
211
+ }
212
+
213
+ protected void notifyMonitorPointAdded () {
214
+ if (monitor != null ) {
215
+ monitor .pointAdded (description + ".addPoint()" , getCurrentPoints ());
216
+ }
217
+ }
218
+
219
+ private List <SimplificationErrorCalculator .PointLike > getCurrentPoints () {
220
+ ArrayList <SimplificationErrorCalculator .PointLike > simplification = new ArrayList <>();
221
+ for (int i = 0 ; i < length ; i ++) {
222
+ simplification .add (points [i ]);
223
+ }
224
+ return simplification ;
225
+ }
226
+
147
227
/**
148
228
* Simplifies a Line geometry to the specified maximum number of points.
149
229
*/
150
230
public static class LineStrings extends GeometrySimplifier <Line > {
151
231
public LineStrings (int maxPoints , SimplificationErrorCalculator calculator ) {
152
- super (maxPoints , calculator );
232
+ this (maxPoints , calculator , null );
233
+ }
234
+
235
+ public LineStrings (int maxPoints , SimplificationErrorCalculator calculator , Monitor monitor ) {
236
+ super ("LineString" , maxPoints , calculator , monitor );
153
237
}
154
238
155
239
@ Override
@@ -158,9 +242,11 @@ public Line simplify(Line line) {
158
242
return line ;
159
243
}
160
244
reset ();
245
+ notifyMonitorSimplificationStart ();
161
246
for (int i = 0 ; i < line .length (); i ++) {
162
247
consume (line .getX (i ), line .getY (i ));
163
248
}
249
+ notifyMonitorSimplificationEnd ();
164
250
return produce ();
165
251
}
166
252
@@ -185,7 +271,11 @@ public Line produce() {
185
271
*/
186
272
public static class LinearRings extends GeometrySimplifier <LinearRing > {
187
273
public LinearRings (int maxPoints , SimplificationErrorCalculator calculator ) {
188
- super (maxPoints , calculator );
274
+ this (maxPoints , calculator , null );
275
+ }
276
+
277
+ public LinearRings (int maxPoints , SimplificationErrorCalculator calculator , Monitor monitor ) {
278
+ super ("LinearRing" , maxPoints , calculator , monitor );
189
279
assert maxPoints >= 4 ;
190
280
}
191
281
@@ -195,9 +285,11 @@ public LinearRing simplify(LinearRing ring) {
195
285
return ring ;
196
286
}
197
287
reset ();
288
+ notifyMonitorSimplificationStart ();
198
289
for (int i = 0 ; i < ring .length (); i ++) {
199
290
consume (ring .getX (i ), ring .getY (i ));
200
291
}
292
+ notifyMonitorSimplificationEnd ();
201
293
return produce ();
202
294
}
203
295
@@ -221,7 +313,11 @@ public static class Polygons extends GeometrySimplifier<Polygon> {
221
313
ArrayList <GeometrySimplifier <LinearRing >> holeSimplifiers = new ArrayList <>();
222
314
223
315
public Polygons (int maxPoints , SimplificationErrorCalculator calculator ) {
224
- super (maxPoints , calculator );
316
+ this (maxPoints , calculator , null );
317
+ }
318
+
319
+ public Polygons (int maxPoints , SimplificationErrorCalculator calculator , Monitor monitor ) {
320
+ super ("Polygon" , maxPoints , calculator , monitor );
225
321
}
226
322
227
323
@ Override
@@ -237,17 +333,20 @@ public Polygon simplify(Polygon geometry) {
237
333
return geometry ;
238
334
}
239
335
reset ();
336
+ notifyMonitorSimplificationStart ();
240
337
for (int i = 0 ; i < ring .length (); i ++) {
241
338
consume (ring .getX (i ), ring .getY (i ));
242
339
}
243
340
for (int i = 0 ; i < geometry .getNumberOfHoles (); i ++) {
244
341
LinearRing hole = geometry .getHole (i );
245
342
double simplificationFactor = (double ) maxPoints / ring .length ();
246
343
int maxHolePoints = Math .max (4 , (int ) (simplificationFactor * hole .length ()));
247
- LinearRings holeSimplifier = new LinearRings (maxHolePoints , calculator );
344
+ LinearRings holeSimplifier = new LinearRings (maxHolePoints , calculator , this .monitor );
345
+ holeSimplifier .description = "Polygon.Hole" ;
248
346
holeSimplifiers .add (holeSimplifier );
249
347
holeSimplifier .simplify (hole );
250
348
}
349
+ notifyMonitorSimplificationEnd ();
251
350
return produce ();
252
351
}
253
352
@@ -267,7 +366,7 @@ private List<LinearRing> produceHoles() {
267
366
* It does not make use of its own simplifier capabilities.
268
367
* The largest inner polygon is simplified to the specified maxPoints, while the rest are simplified
269
368
* to a maxPoints value that is a fraction of their size compared to the largest size.
270
- *
369
+ * <p>
271
370
* Note that this simplifier cannot work in streaming mode.
272
371
* Since a MultiPolygon can contain more than one polygon,
273
372
* the <code>consume(Point)</code> method would not know which polygon to add to.
@@ -279,7 +378,11 @@ public static class MultiPolygons extends GeometrySimplifier<MultiPolygon> {
279
378
ArrayList <Integer > indexes = new ArrayList <>();
280
379
281
380
public MultiPolygons (int maxPoints , SimplificationErrorCalculator calculator ) {
282
- super (maxPoints , calculator );
381
+ this (maxPoints , calculator , null );
382
+ }
383
+
384
+ public MultiPolygons (int maxPoints , SimplificationErrorCalculator calculator , Monitor monitor ) {
385
+ super ("MultiPolygon" , maxPoints , calculator , monitor );
283
386
}
284
387
285
388
@ Override
@@ -296,18 +399,21 @@ public MultiPolygon simplify(MultiPolygon geometry) {
296
399
Polygon polygon = geometry .get (i );
297
400
maxPolyLength = Math .max (maxPolyLength , polygon .getPolygon ().length ());
298
401
}
402
+ notifyMonitorSimplificationStart ();
299
403
for (int i = 0 ; i < geometry .size (); i ++) {
300
404
Polygon polygon = geometry .get (i );
301
405
double simplificationFactor = (double ) maxPoints / maxPolyLength ;
302
406
int maxPolyPoints = Math .max (4 , (int ) (simplificationFactor * polygon .getPolygon ().length ()));
303
- Polygons simplifier = new Polygons (maxPolyPoints , calculator );
407
+ Polygons simplifier = new Polygons (maxPolyPoints , calculator , monitor );
408
+ simplifier .description = "MultiPolygon.Polygon[" + i + "]" ;
304
409
simplifier .simplify (polygon );
305
410
if (simplifier .length > 0 ) {
306
411
// Invalid polygons (all points co-located) will not be simplified
307
412
polygonSimplifiers .add (simplifier );
308
413
indexes .add (i );
309
414
}
310
415
}
416
+ notifyMonitorSimplificationEnd ();
311
417
return produce ();
312
418
}
313
419
0 commit comments