@@ -27,32 +27,26 @@ import (
27
27
//
28
28
// A Snapper defines the following methods:
29
29
//
30
- // 1. The SnapPoint method, which snaps a point P to a nearby point (the
30
+ // 1. The SnapPoint method, which snaps a point P to a nearby point (the
31
+ // candidate snap site). Any point may be returned, including P
32
+ // itself (the identity snap function).
31
33
//
32
- // candidate snap site). Any point may be returned, including P
33
- // itself (the identity snap function).
34
+ // 2. SnapRadius, the maximum distance that vertices can move when
35
+ // snapped. The snapRadius must be at least as large as the maximum
36
+ // distance between P and SnapPoint(P) for any point P.
34
37
//
35
- // 2. SnapRadius, the maximum distance that vertices can move when
38
+ // Note that the maximum distance that edge interiors can move when
39
+ // snapped is slightly larger than "snapRadius", and is reported by
40
+ // the Builder options maxEdgeDeviation (see there for details).
36
41
//
37
- // snapped. The snapRadius must be at least as large as the maximum
38
- // distance between P and SnapPoint(P) for any point P.
42
+ // 3. MinVertexSeparation, the guaranteed minimum distance between
43
+ // vertices in the output. This is generally a fraction of
44
+ // snapRadius where the fraction depends on the snap function.
39
45
//
40
- // 3. MaxEdgeDeviation, the maximum distance that edges can move when
41
- //
42
- // snapped. It is slightly larger than snapRadius because when a
43
- // geodesic edge is snapped, the center of the edge moves further than
44
- // its endpoints. This value is computed automatically by Builder.
45
- //
46
- // 4. MinVertexSeparation, the guaranteed minimum distance between
47
- //
48
- // vertices in the output. This is generally a fraction of
49
- // snapRadius where the fraction depends on the snap function.
50
- //
51
- // 5. A MinEdgeVertexSeparation, the guaranteed minimum distance
52
- //
53
- // between edges and non-incident vertices in the output. This is
54
- // generally a fraction of snapRadius where the fraction depends on
55
- // the snap function.
46
+ // 4. A MinEdgeVertexSeparation, the guaranteed minimum distance
47
+ // between edges and non-incident vertices in the output. This is
48
+ // generally a fraction of snapRadius where the fraction depends on
49
+ // the snap function.
56
50
//
57
51
// It is important to note that SnapPoint does not define the actual
58
52
// mapping from input vertices to output vertices, since the points it
@@ -65,41 +59,36 @@ import (
65
59
//
66
60
// Builder makes the following guarantees (within a small error margin):
67
61
//
68
- // 1. Every vertex is at a location returned by SnapPoint.
69
- //
70
- // 2. Vertices are within snapRadius of the corresponding input vertex.
71
- //
72
- // 3. Edges are within maxEdgeDeviation of the corresponding input edge
62
+ // 1. Every vertex is at a location returned by SnapPoint.
73
63
//
74
- // (a distance slightly larger than snapRadius) .
64
+ // 2. Vertices are within snapRadius of the corresponding input vertex .
75
65
//
76
- // 4. Vertices are separated by at least minVertexSeparation
66
+ // 3. Edges are within maxEdgeDeviation of the corresponding input edge
67
+ // (a distance slightly larger than snapRadius).
77
68
//
78
- // (a fraction of snapRadius that depends on the snap function).
69
+ // 4. Vertices are separated by at least minVertexSeparation
70
+ // (a fraction of snapRadius that depends on the snap function).
79
71
//
80
- // 5. Edges and non-incident vertices are separated by at least
72
+ // 5. Edges and non-incident vertices are separated by at least
73
+ // minEdgeVertexSeparation (a fraction of snapRadius).
81
74
//
82
- // minEdgeVertexSeparation (a fraction of snapRadius).
75
+ // 6. Vertex and edge locations do not change unless one of the conditions
76
+ // above is not already met (idempotency / stability).
83
77
//
84
- // 6. Vertex and edge locations do not change unless one of the conditions
85
- //
86
- // above is not already met (idempotency / stability).
87
- //
88
- // 7. The topology of the input geometry is preserved (up to the creation
89
- //
90
- // of degeneracies). This means that there exists a continuous
91
- // deformation from the input to the output such that no vertex
92
- // crosses an edge.
78
+ // 7. The topology of the input geometry is preserved (up to the creation
79
+ // of degeneracies). This means that there exists a continuous
80
+ // deformation from the input to the output such that no vertex
81
+ // crosses an edge.
93
82
type Snapper interface {
94
- // SnapRadius reports the maximum distance that vertices can move when snapped.
95
- // This requires that SnapRadius <= maxSnapRadius
83
+ // SnapRadius reports the maximum distance that vertices can move when
84
+ // snapped. The snap radius can be any value between 0 and maxSnapRadius.
85
+ //
86
+ // If the snap radius is zero, then vertices are snapped together only if
87
+ // they are identical. Edges will not be snapped to any vertices other
88
+ // than their endpoints, even if there are vertices whose distance to the
89
+ // edge is zero, unless split_crossing_edges() is true (see below).
96
90
SnapRadius () s1.Angle
97
-
98
- // MaxEdgeDeviation returns the maximum distance that the center of an
99
- // edge can move when snapped. This is slightly larger than SnapRadius
100
- // because when a geodesic edge is snapped, the center of the edge moves
101
- // further than its endpoints.
102
- MaxEdgeDeviation () s1.Angle
91
+ // TODO(rsned): Add SetSnapRadius method to allow users to update value.
103
92
104
93
// MinVertexSeparation returns the guaranteed minimum distance between
105
94
// vertices in the output. This is generally some fraction of SnapRadius.
@@ -158,20 +147,19 @@ func (sf IdentitySnapper) SnapRadius() s1.Angle {
158
147
return sf .snapRadius
159
148
}
160
149
161
- // MaxEdgeDeviation returns the maximum edge deviation this type supports.
162
- func (sf IdentitySnapper ) MaxEdgeDeviation () s1.Angle {
163
- return maxEdgeDeviationRatio * sf .snapRadius
164
- }
165
-
166
150
// MinVertexSeparation returns the minimum vertex separation for this snap type.
167
151
func (sf IdentitySnapper ) MinVertexSeparation () s1.Angle {
152
+ // Since SnapFunction does not move the input point, output vertices are
153
+ // separated by the full snapRadius.
168
154
return sf .snapRadius
169
155
}
170
156
171
157
// MinEdgeVertexSeparation returns the minimum edge vertex separation.
172
158
// For the identity snap function, edges are separated from all non-incident
173
159
// vertices by at least 0.5 * snapRadius.
174
160
func (sf IdentitySnapper ) MinEdgeVertexSeparation () s1.Angle {
161
+ // In the worst case configuration, the edge-vertex separation is half of the
162
+ // vertex separation.
175
163
return 0.5 * sf .snapRadius
176
164
}
177
165
@@ -197,6 +185,8 @@ type CellIDSnapper struct {
197
185
snapRadius s1.Angle
198
186
}
199
187
188
+ // TODO(rsned): Add SetLevel method to allow changes to the type.
189
+
200
190
// NewCellIDSnapper returns a snap function with the default level set.
201
191
func NewCellIDSnapper () CellIDSnapper {
202
192
return CellIDSnapper {
@@ -215,12 +205,6 @@ func CellIDSnapperForLevel(level int) CellIDSnapper {
215
205
216
206
// SnapRadius reports the maximum distance that vertices can move when snapped.
217
207
// This requires that SnapRadius <= maxSnapRadius
218
- // Defines the snap radius to be used (see Builder). The snap radius
219
- // must be at least the minimum value for the current level, but larger
220
- // values can also be used (e.g., to simplify the geometry).
221
- //
222
- // This requires snapRadius >= MinSnapRadiusForLevel(level)
223
- // and snapRadius <= maxSnapRadius
224
208
func (sf CellIDSnapper ) SnapRadius () s1.Angle {
225
209
return sf .snapRadius
226
210
}
@@ -247,53 +231,48 @@ func (sf CellIDSnapper) minSnapRadiusForLevel(level int) s1.Angle {
247
231
// the minimum possible snap radius for the chosen level, do this:
248
232
//
249
233
// sf := CellIDSnapperForLevel(f.levelForMaxSnapRadius(distance));
234
+ //
235
+ // TODO(rsned): pop this method out to standalone.
250
236
func (sf CellIDSnapper ) levelForMaxSnapRadius (snapRadius s1.Angle ) int {
251
237
// When choosing a level, we need to account for the error bound of
252
238
// 4 * dblEpsilon that is added by MinSnapRadiusForLevel.
253
239
return MaxDiagMetric .MinLevel (2 * (snapRadius .Radians () - 4 * dblEpsilon ))
254
240
}
255
241
256
- // MaxEdgeDeviation returns the maximum edge deviation this type supports.
257
- func (sf CellIDSnapper ) MaxEdgeDeviation () s1.Angle {
258
- return maxEdgeDeviationRatio * sf .snapRadius
259
- }
260
-
261
- // MinVertexSeparation returns the guaranteed minimum distance between
262
- // vertices in the output. This is generally some fraction of SnapRadius.
263
- // For CellID snapping, the minimum separation between vertices depends on
264
- // level and snapRadius. It can vary between 0.5 * snapRadius
265
- // and snapRadius.
242
+ // MinVertexSeparation reports the minimum separation between vertices depending
243
+ // on level and snapRadius. It can vary between 0.5 * snapRadius and snapRadius.
266
244
func (sf CellIDSnapper ) MinVertexSeparation () s1.Angle {
267
245
// We have three different bounds for the minimum vertex separation: one is
268
246
// a constant bound, one is proportional to snapRadius, and one is equal to
269
- // snapRadius minus a constant. These bounds give the best results for
270
- // small, medium, and large snap radii respectively. We return the maximum
247
+ // snapRadius minus a constant. These bounds give the best results for
248
+ // small, medium, and large snap radii respectively. We return the maximum
271
249
// of the three bounds.
272
250
//
273
251
// 1. Constant bound: Vertices are always separated by at least
274
- // MinEdgeMetric.Value(level), the minimum edge length for the chosen snap level.
252
+ // MinEdgeMetric.Value(level), the minimum edge length for the chosen
253
+ // snap level.
275
254
//
276
255
// 2. Proportional bound: It can be shown that in the plane, the worst-case
277
256
// configuration has a vertex separation of 2 / sqrt(13) * snapRadius.
278
257
// This is verified in the unit test, except that on the sphere the ratio
279
258
// is slightly smaller at cell level 2 (0.54849 vs. 0.55470). We reduce
280
259
// that value a bit more below to be conservative.
281
260
//
282
- // 3. Best asymptotic bound: This bound bound is derived by observing we
261
+ // 3. Best asymptotic bound: This bound is derived by observing we
283
262
// only select a new site when it is at least snapRadius away from all
284
263
// existing sites, and the site can move by at most
285
264
// 0.5 * MaxDiagMetric.Value(level) when snapped.
286
265
minEdge := s1 .Angle (MinEdgeMetric .Value (sf .level ))
287
266
maxDiag := s1 .Angle (MaxDiagMetric .Value (sf .level ))
288
267
return maxAngle (minEdge ,
289
- maxAngle (s1 .Angle (2 / math .Sqrt (13 ))* sf .snapRadius , sf .snapRadius - 0.5 * maxDiag ))
268
+ // per 2 above, a little less than 2 / sqrt(13)
269
+ maxAngle (0.548 * sf .snapRadius ,
270
+ sf .snapRadius - 0.5 * maxDiag ))
290
271
}
291
272
292
273
// MinEdgeVertexSeparation returns the guaranteed minimum spacing between
293
- // edges and non-incident vertices in the output.
294
- // For CellID snapping, the minimum separation between edges and
295
- // non-incident vertices depends on level and snapRadius. It can
296
- // be as low as 0.219 * snapRadius, but is typically 0.5 * snapRadius
274
+ // edges and non-incident vertices in the output depending on level and snapRadius..
275
+ // It can be as low as 0.219 * snapRadius, but is typically 0.5 * snapRadius
297
276
// or more.
298
277
func (sf CellIDSnapper ) MinEdgeVertexSeparation () s1.Angle {
299
278
// Similar to MinVertexSeparation, in this case we have four bounds: a
@@ -302,15 +281,16 @@ func (sf CellIDSnapper) MinEdgeVertexSeparation() s1.Angle {
302
281
// snapRadius, and a bound that is equal to snapRadius minus a constant.
303
282
//
304
283
// 1. Constant bounds:
305
- // (a) At the minimum snap radius for a given level, it can be shown that
306
- // vertices are separated from edges by at least 0.5 * MinDiagMetric.Value(level) in
307
- // the plane. The unit test verifies this, except that on the sphere the
308
- // worst case is slightly better: 0.5652980068 * MinDiagMetric.Value(level).
284
+ // (a) At the minimum snap radius for a given level, it can be shown
285
+ // that vertices are separated from edges by at least 0.5 *a
286
+ // MinDiagMetric.Value(level) in the plane. The unit test verifies this,
287
+ // except that on the sphere the worst case is slightly better:
288
+ // 0.5652980068 * MinDiagMetric.Value(level).
309
289
//
310
290
// (b) Otherwise, for arbitrary snap radii the worst-case configuration
311
291
// in the plane has an edge-vertex separation of sqrt(3/19) *
312
- // MinDiagMetric.Value(level), where sqrt(3/19) is about 0.3973597071. The unit
313
- // test verifies that the bound is slightly better on the sphere:
292
+ // MinDiagMetric.Value(level), where sqrt(3/19) is about 0.3973597071.
293
+ // The unit test verifies that the bound is slightly better on the sphere:
314
294
// 0.3973595687 * MinDiagMetric.Value(level).
315
295
//
316
296
// 2. Proportional bound: In the plane, the worst-case configuration has an
@@ -337,8 +317,8 @@ func (sf CellIDSnapper) MinEdgeVertexSeparation() s1.Angle {
337
317
338
318
// Otherwise, these bounds hold for any snapRadius.
339
319
vertexSep := sf .MinVertexSeparation ()
340
- return maxAngle (s1 . Angle ( math . Sqrt ( 3.0 / 19.0 )) * minDiag ,
341
- maxAngle (s1 . Angle ( 2 * math . Sqrt ( 3.0 / 247.0 )) * sf . snapRadius ,
320
+ return maxAngle (0.397 * minDiag , // sqrt(3 /19) in the plane
321
+ maxAngle (0.219 * sf . snapRadius , // 2*sqrt(3 /247) in the plane
342
322
0.5 * (vertexSep / sf .snapRadius )* vertexSep ))
343
323
}
344
324
@@ -360,6 +340,11 @@ const (
360
340
// example, in E6 coordinates the point (23.12345651, -45.65432149) would
361
341
// become (23123457, -45654321).
362
342
//
343
+ // The main argument of the Snapper is the exponent for the power of 10
344
+ // that coordinates should be multiplied by before rounding. For example,
345
+ // NewIntLatLngSnapper(7) is a function that snaps to E7 coordinates. The
346
+ // exponent can range from 0 to 10.
347
+ //
363
348
// Each exponent has a corresponding minimum snap radius, which is simply the
364
349
// maximum distance that a vertex can move when snapped. It is approximately
365
350
// equal to 1/sqrt(2) times the nominal point spacing; for example, for
@@ -388,6 +373,8 @@ func NewIntLatLngSnapper(exponent int) IntLatLngSnapper {
388
373
return sf
389
374
}
390
375
376
+ // TODO(rsned): Add SetExponent() method.
377
+
391
378
// SnapRadius reports the snap radius to be used. The snap radius
392
379
// must be at least the minimum value for the current exponent, but larger
393
380
// values can also be used (e.g., to simplify the geometry).
@@ -400,6 +387,8 @@ func (sf IntLatLngSnapper) SnapRadius() s1.Angle {
400
387
401
388
// minSnapRadiusForExponent returns the minimum allowable snap radius for the given
402
389
// exponent (approximately equal to 10**(-exponent) / sqrt(2)) degrees).
390
+ //
391
+ // TODO(rsned): Pop this method out so it can be used by other callers.
403
392
func (sf IntLatLngSnapper ) minSnapRadiusForExponent (exponent int ) s1.Angle {
404
393
// snapRadius needs to be an upper bound on the true distance that a
405
394
// point can move when snapped, taking into account numerical errors.
@@ -426,18 +415,21 @@ func (sf IntLatLngSnapper) minSnapRadiusForExponent(exponent int) s1.Angle {
426
415
// (much larger than the errors above), which can change the position by
427
416
// up to (sqrt(2) * 0.5 * sf.to) radians.
428
417
power := math .Pow10 (exponent )
429
- return (s1 .Degree * s1 .Angle ((1 / math .Sqrt2 )/ power ) + s1 .Angle ((9 * math .Sqrt2 + 1.5 )* dblEpsilon ))
418
+ return (s1 .Degree * s1 .Angle ((1 / math .Sqrt2 )/ power ) +
419
+ s1 .Angle ((9 * math .Sqrt2 + 1.5 )* dblEpsilon ))
430
420
}
431
421
432
422
// exponentForMaxSnapRadius returns the minimum exponent such that vertices will
433
423
// not move by more than snapRadius. This can be useful when choosing an appropriate
434
424
// exponent for snapping. The return value is always a valid exponent (out of
435
425
// range values are silently clamped).
426
+ //
427
+ // TODO(rsned): Pop this method out so it can be used by other callers.
436
428
func (sf IntLatLngSnapper ) exponentForMaxSnapRadius (snapRadius s1.Angle ) int {
437
429
// When choosing an exponent, we need to account for the error bound of
438
430
// (9 * sqrt(2) + 1.5) * dblEpsilon added by minSnapRadiusForExponent.
439
431
snapRadius -= (9 * math .Sqrt2 + 1.5 ) * dblEpsilon
440
- snapRadius = s1 . Angle ( math . Max ( float64 ( snapRadius ) , 1e-30 ) )
432
+ snapRadius = maxAngle ( snapRadius , 1e-30 )
441
433
exponent := math .Log10 ((1 / math .Sqrt2 ) / snapRadius .Degrees ())
442
434
443
435
// There can be small errors in the calculation above, so to ensure that
@@ -447,14 +439,8 @@ func (sf IntLatLngSnapper) exponentForMaxSnapRadius(snapRadius s1.Angle) int {
447
439
minInt (maxIntSnappingExponent , int (math .Ceil (exponent - 2 * dblEpsilon ))))
448
440
}
449
441
450
- // MaxEdgeDeviation returns the maximum edge deviation this type supports.
451
- func (sf IntLatLngSnapper ) MaxEdgeDeviation () s1.Angle {
452
- return maxEdgeDeviationRatio * sf .snapRadius
453
- }
454
-
455
- // MinVertexSeparation returns the guaranteed minimum distance between vertices
456
- // in the output. For IntLatLng snapping, the minimum separation between vertices
457
- // depends on exponent and snapRadius.
442
+ // MinVertexSeparation reports the minimum separation between vertices depending on
443
+ // exponent and snapRadius. It can vary between 0.471 * snapRadius and snapRadius.
458
444
func (sf IntLatLngSnapper ) MinVertexSeparation () s1.Angle {
459
445
// We have two bounds for the minimum vertex separation: one is proportional
460
446
// to snapRadius, and one is equal to snapRadius minus a constant. These
@@ -471,19 +457,18 @@ func (sf IntLatLngSnapper) MinVertexSeparation() s1.Angle {
471
457
// only select a new site when it is at least snapRadius away from all
472
458
// existing sites, and snapping a vertex can move it by up to
473
459
// ((1 / sqrt(2)) * sf.to) degrees.
474
- return maxAngle (( math . Sqrt2 / 3 ) * sf .snapRadius ,
460
+ return maxAngle (0.471 * sf .snapRadius , // sqrt(2)/3 in the plane
475
461
sf .snapRadius - s1 .Degree * s1 .Angle (1 / math .Sqrt2 )* sf .to )
476
462
}
477
463
478
- // MinEdgeVertexSeparation returns the guaranteed minimum spacing between edges
479
- // and non-incident vertices in the output. For IntLatLng snapping, the minimum
480
- // separation between edges and non-incident vertices depends on level and
464
+ // MinEdgeVertexSeparation reports the minimum separation between edges
465
+ // and non-incident vertices in the output depending on the level and
481
466
// snapRadius. It can be as low as 0.222 * snapRadius, but is typically
482
467
// 0.39 * snapRadius or more.
483
468
func (sf IntLatLngSnapper ) MinEdgeVertexSeparation () s1.Angle {
484
469
// Similar to MinVertexSeparation, in this case we have three bounds:
485
470
// one is a constant bound, one is proportional to snapRadius, and one is
486
- // equal to snapRadius minus a constant .
471
+ // approaches 0.5 * snapRadius asymptotically .
487
472
//
488
473
// 1. Constant bound: In the plane, the worst-case configuration has an
489
474
// edge-vertex separation of ((1 / sqrt(13)) * sf.to) degrees.
@@ -504,12 +489,15 @@ func (sf IntLatLngSnapper) MinEdgeVertexSeparation() s1.Angle {
504
489
// bound approaches 0.5 * snapRadius as the snap radius becomes large
505
490
// relative to the grid spacing.
506
491
vertexSep := sf .MinVertexSeparation ()
507
- return maxAngle (s1 .Angle (1 / math .Sqrt (13 ))* s1 .Degree * sf .to ,
508
- maxAngle ((2.0 / 9.0 )* sf .snapRadius , 0.5 * (vertexSep / sf .snapRadius )* vertexSep ))
492
+ return maxAngle (0.277 * s1 .Degree * sf .to , // 1/sqrt(13) in the plane
493
+ maxAngle (0.222 * sf .snapRadius , // 2/9 in the plane
494
+ 0.5 * (vertexSep / sf .snapRadius )* vertexSep ))
509
495
}
510
496
511
497
// SnapPoint returns a candidate snap site for the given point.
512
498
func (sf IntLatLngSnapper ) SnapPoint (point Point ) Point {
499
+ // TODO(rsned): C++ DCHECK's on exponent being in valid range. What should we
500
+ // do when it's bad here.
513
501
input := LatLngFromPoint (point )
514
502
lat := s1 .Angle (roundAngle (input .Lat * sf .from ))
515
503
lng := s1 .Angle (roundAngle (input .Lng * sf .from ))
0 commit comments