polyline.go (19348B)
1 // Copyright 2016 Google Inc. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package s2 16 17 import ( 18 "fmt" 19 "io" 20 "math" 21 22 "github.com/golang/geo/s1" 23 ) 24 25 // Polyline represents a sequence of zero or more vertices connected by 26 // straight edges (geodesics). Edges of length 0 and 180 degrees are not 27 // allowed, i.e. adjacent vertices should not be identical or antipodal. 28 type Polyline []Point 29 30 // PolylineFromLatLngs creates a new Polyline from the given LatLngs. 31 func PolylineFromLatLngs(points []LatLng) *Polyline { 32 p := make(Polyline, len(points)) 33 for k, v := range points { 34 p[k] = PointFromLatLng(v) 35 } 36 return &p 37 } 38 39 // Reverse reverses the order of the Polyline vertices. 40 func (p *Polyline) Reverse() { 41 for i := 0; i < len(*p)/2; i++ { 42 (*p)[i], (*p)[len(*p)-i-1] = (*p)[len(*p)-i-1], (*p)[i] 43 } 44 } 45 46 // Length returns the length of this Polyline. 47 func (p *Polyline) Length() s1.Angle { 48 var length s1.Angle 49 50 for i := 1; i < len(*p); i++ { 51 length += (*p)[i-1].Distance((*p)[i]) 52 } 53 return length 54 } 55 56 // Centroid returns the true centroid of the polyline multiplied by the length of the 57 // polyline. The result is not unit length, so you may wish to normalize it. 58 // 59 // Scaling by the Polyline length makes it easy to compute the centroid 60 // of several Polylines (by simply adding up their centroids). 61 func (p *Polyline) Centroid() Point { 62 var centroid Point 63 for i := 1; i < len(*p); i++ { 64 // The centroid (multiplied by length) is a vector toward the midpoint 65 // of the edge, whose length is twice the sin of half the angle between 66 // the two vertices. Defining theta to be this angle, we have: 67 vSum := (*p)[i-1].Add((*p)[i].Vector) // Length == 2*cos(theta) 68 vDiff := (*p)[i-1].Sub((*p)[i].Vector) // Length == 2*sin(theta) 69 70 // Length == 2*sin(theta) 71 centroid = Point{centroid.Add(vSum.Mul(math.Sqrt(vDiff.Norm2() / vSum.Norm2())))} 72 } 73 return centroid 74 } 75 76 // Equal reports whether the given Polyline is exactly the same as this one. 77 func (p *Polyline) Equal(b *Polyline) bool { 78 if len(*p) != len(*b) { 79 return false 80 } 81 for i, v := range *p { 82 if v != (*b)[i] { 83 return false 84 } 85 } 86 87 return true 88 } 89 90 // ApproxEqual reports whether two polylines have the same number of vertices, 91 // and corresponding vertex pairs are separated by no more the standard margin. 92 func (p *Polyline) ApproxEqual(o *Polyline) bool { 93 return p.approxEqual(o, s1.Angle(epsilon)) 94 } 95 96 // approxEqual reports whether two polylines are equal within the given margin. 97 func (p *Polyline) approxEqual(o *Polyline, maxError s1.Angle) bool { 98 if len(*p) != len(*o) { 99 return false 100 } 101 for offset, val := range *p { 102 if !val.approxEqual((*o)[offset], maxError) { 103 return false 104 } 105 } 106 return true 107 } 108 109 // CapBound returns the bounding Cap for this Polyline. 110 func (p *Polyline) CapBound() Cap { 111 return p.RectBound().CapBound() 112 } 113 114 // RectBound returns the bounding Rect for this Polyline. 115 func (p *Polyline) RectBound() Rect { 116 rb := NewRectBounder() 117 for _, v := range *p { 118 rb.AddPoint(v) 119 } 120 return rb.RectBound() 121 } 122 123 // ContainsCell reports whether this Polyline contains the given Cell. Always returns false 124 // because "containment" is not numerically well-defined except at the Polyline vertices. 125 func (p *Polyline) ContainsCell(cell Cell) bool { 126 return false 127 } 128 129 // IntersectsCell reports whether this Polyline intersects the given Cell. 130 func (p *Polyline) IntersectsCell(cell Cell) bool { 131 if len(*p) == 0 { 132 return false 133 } 134 135 // We only need to check whether the cell contains vertex 0 for correctness, 136 // but these tests are cheap compared to edge crossings so we might as well 137 // check all the vertices. 138 for _, v := range *p { 139 if cell.ContainsPoint(v) { 140 return true 141 } 142 } 143 144 cellVertices := []Point{ 145 cell.Vertex(0), 146 cell.Vertex(1), 147 cell.Vertex(2), 148 cell.Vertex(3), 149 } 150 151 for j := 0; j < 4; j++ { 152 crosser := NewChainEdgeCrosser(cellVertices[j], cellVertices[(j+1)&3], (*p)[0]) 153 for i := 1; i < len(*p); i++ { 154 if crosser.ChainCrossingSign((*p)[i]) != DoNotCross { 155 // There is a proper crossing, or two vertices were the same. 156 return true 157 } 158 } 159 } 160 return false 161 } 162 163 // ContainsPoint returns false since Polylines are not closed. 164 func (p *Polyline) ContainsPoint(point Point) bool { 165 return false 166 } 167 168 // CellUnionBound computes a covering of the Polyline. 169 func (p *Polyline) CellUnionBound() []CellID { 170 return p.CapBound().CellUnionBound() 171 } 172 173 // NumEdges returns the number of edges in this shape. 174 func (p *Polyline) NumEdges() int { 175 if len(*p) == 0 { 176 return 0 177 } 178 return len(*p) - 1 179 } 180 181 // Edge returns endpoints for the given edge index. 182 func (p *Polyline) Edge(i int) Edge { 183 return Edge{(*p)[i], (*p)[i+1]} 184 } 185 186 // ReferencePoint returns the default reference point with negative containment because Polylines are not closed. 187 func (p *Polyline) ReferencePoint() ReferencePoint { 188 return OriginReferencePoint(false) 189 } 190 191 // NumChains reports the number of contiguous edge chains in this Polyline. 192 func (p *Polyline) NumChains() int { 193 return minInt(1, p.NumEdges()) 194 } 195 196 // Chain returns the i-th edge Chain in the Shape. 197 func (p *Polyline) Chain(chainID int) Chain { 198 return Chain{0, p.NumEdges()} 199 } 200 201 // ChainEdge returns the j-th edge of the i-th edge Chain. 202 func (p *Polyline) ChainEdge(chainID, offset int) Edge { 203 return Edge{(*p)[offset], (*p)[offset+1]} 204 } 205 206 // ChainPosition returns a pair (i, j) such that edgeID is the j-th edge 207 func (p *Polyline) ChainPosition(edgeID int) ChainPosition { 208 return ChainPosition{0, edgeID} 209 } 210 211 // Dimension returns the dimension of the geometry represented by this Polyline. 212 func (p *Polyline) Dimension() int { return 1 } 213 214 // IsEmpty reports whether this shape contains no points. 215 func (p *Polyline) IsEmpty() bool { return defaultShapeIsEmpty(p) } 216 217 // IsFull reports whether this shape contains all points on the sphere. 218 func (p *Polyline) IsFull() bool { return defaultShapeIsFull(p) } 219 220 func (p *Polyline) typeTag() typeTag { return typeTagPolyline } 221 222 func (p *Polyline) privateInterface() {} 223 224 // findEndVertex reports the maximal end index such that the line segment between 225 // the start index and this one such that the line segment between these two 226 // vertices passes within the given tolerance of all interior vertices, in order. 227 func findEndVertex(p Polyline, tolerance s1.Angle, index int) int { 228 // The basic idea is to keep track of the "pie wedge" of angles 229 // from the starting vertex such that a ray from the starting 230 // vertex at that angle will pass through the discs of radius 231 // tolerance centered around all vertices processed so far. 232 // 233 // First we define a coordinate frame for the tangent and normal 234 // spaces at the starting vertex. Essentially this means picking 235 // three orthonormal vectors X,Y,Z such that X and Y span the 236 // tangent plane at the starting vertex, and Z is up. We use 237 // the coordinate frame to define a mapping from 3D direction 238 // vectors to a one-dimensional ray angle in the range (-π, 239 // π]. The angle of a direction vector is computed by 240 // transforming it into the X,Y,Z basis, and then calculating 241 // atan2(y,x). This mapping allows us to represent a wedge of 242 // angles as a 1D interval. Since the interval wraps around, we 243 // represent it as an Interval, i.e. an interval on the unit 244 // circle. 245 origin := p[index] 246 frame := getFrame(origin) 247 248 // As we go along, we keep track of the current wedge of angles 249 // and the distance to the last vertex (which must be 250 // non-decreasing). 251 currentWedge := s1.FullInterval() 252 var lastDistance s1.Angle 253 254 for index++; index < len(p); index++ { 255 candidate := p[index] 256 distance := origin.Distance(candidate) 257 258 // We don't allow simplification to create edges longer than 259 // 90 degrees, to avoid numeric instability as lengths 260 // approach 180 degrees. We do need to allow for original 261 // edges longer than 90 degrees, though. 262 if distance > math.Pi/2 && lastDistance > 0 { 263 break 264 } 265 266 // Vertices must be in increasing order along the ray, except 267 // for the initial disc around the origin. 268 if distance < lastDistance && lastDistance > tolerance { 269 break 270 } 271 272 lastDistance = distance 273 274 // Points that are within the tolerance distance of the origin 275 // do not constrain the ray direction, so we can ignore them. 276 if distance <= tolerance { 277 continue 278 } 279 280 // If the current wedge of angles does not contain the angle 281 // to this vertex, then stop right now. Note that the wedge 282 // of possible ray angles is not necessarily empty yet, but we 283 // can't continue unless we are willing to backtrack to the 284 // last vertex that was contained within the wedge (since we 285 // don't create new vertices). This would be more complicated 286 // and also make the worst-case running time more than linear. 287 direction := toFrame(frame, candidate) 288 center := math.Atan2(direction.Y, direction.X) 289 if !currentWedge.Contains(center) { 290 break 291 } 292 293 // To determine how this vertex constrains the possible ray 294 // angles, consider the triangle ABC where A is the origin, B 295 // is the candidate vertex, and C is one of the two tangent 296 // points between A and the spherical cap of radius 297 // tolerance centered at B. Then from the spherical law of 298 // sines, sin(a)/sin(A) = sin(c)/sin(C), where a and c are 299 // the lengths of the edges opposite A and C. In our case C 300 // is a 90 degree angle, therefore A = asin(sin(a) / sin(c)). 301 // Angle A is the half-angle of the allowable wedge. 302 halfAngle := math.Asin(math.Sin(tolerance.Radians()) / math.Sin(distance.Radians())) 303 target := s1.IntervalFromPointPair(center, center).Expanded(halfAngle) 304 currentWedge = currentWedge.Intersection(target) 305 } 306 307 // We break out of the loop when we reach a vertex index that 308 // can't be included in the line segment, so back up by one 309 // vertex. 310 return index - 1 311 } 312 313 // SubsampleVertices returns a subsequence of vertex indices such that the 314 // polyline connecting these vertices is never further than the given tolerance from 315 // the original polyline. Provided the first and last vertices are distinct, 316 // they are always preserved; if they are not, the subsequence may contain 317 // only a single index. 318 // 319 // Some useful properties of the algorithm: 320 // 321 // - It runs in linear time. 322 // 323 // - The output always represents a valid polyline. In particular, adjacent 324 // output vertices are never identical or antipodal. 325 // 326 // - The method is not optimal, but it tends to produce 2-3% fewer 327 // vertices than the Douglas-Peucker algorithm with the same tolerance. 328 // 329 // - The output is parametrically equivalent to the original polyline to 330 // within the given tolerance. For example, if a polyline backtracks on 331 // itself and then proceeds onwards, the backtracking will be preserved 332 // (to within the given tolerance). This is different than the 333 // Douglas-Peucker algorithm which only guarantees geometric equivalence. 334 func (p *Polyline) SubsampleVertices(tolerance s1.Angle) []int { 335 var result []int 336 337 if len(*p) < 1 { 338 return result 339 } 340 341 result = append(result, 0) 342 clampedTolerance := s1.Angle(math.Max(tolerance.Radians(), 0)) 343 344 for index := 0; index+1 < len(*p); { 345 nextIndex := findEndVertex(*p, clampedTolerance, index) 346 // Don't create duplicate adjacent vertices. 347 if (*p)[nextIndex] != (*p)[index] { 348 result = append(result, nextIndex) 349 } 350 index = nextIndex 351 } 352 353 return result 354 } 355 356 // Encode encodes the Polyline. 357 func (p Polyline) Encode(w io.Writer) error { 358 e := &encoder{w: w} 359 p.encode(e) 360 return e.err 361 } 362 363 func (p Polyline) encode(e *encoder) { 364 e.writeInt8(encodingVersion) 365 e.writeUint32(uint32(len(p))) 366 for _, v := range p { 367 e.writeFloat64(v.X) 368 e.writeFloat64(v.Y) 369 e.writeFloat64(v.Z) 370 } 371 } 372 373 // Decode decodes the polyline. 374 func (p *Polyline) Decode(r io.Reader) error { 375 d := decoder{r: asByteReader(r)} 376 p.decode(d) 377 return d.err 378 } 379 380 func (p *Polyline) decode(d decoder) { 381 version := d.readInt8() 382 if d.err != nil { 383 return 384 } 385 if int(version) != int(encodingVersion) { 386 d.err = fmt.Errorf("can't decode version %d; my version: %d", version, encodingVersion) 387 return 388 } 389 nvertices := d.readUint32() 390 if d.err != nil { 391 return 392 } 393 if nvertices > maxEncodedVertices { 394 d.err = fmt.Errorf("too many vertices (%d; max is %d)", nvertices, maxEncodedVertices) 395 return 396 } 397 *p = make([]Point, nvertices) 398 for i := range *p { 399 (*p)[i].X = d.readFloat64() 400 (*p)[i].Y = d.readFloat64() 401 (*p)[i].Z = d.readFloat64() 402 } 403 } 404 405 // Project returns a point on the polyline that is closest to the given point, 406 // and the index of the next vertex after the projected point. The 407 // value of that index is always in the range [1, len(polyline)]. 408 // The polyline must not be empty. 409 func (p *Polyline) Project(point Point) (Point, int) { 410 if len(*p) == 1 { 411 // If there is only one vertex, it is always closest to any given point. 412 return (*p)[0], 1 413 } 414 415 // Initial value larger than any possible distance on the unit sphere. 416 minDist := 10 * s1.Radian 417 minIndex := -1 418 419 // Find the line segment in the polyline that is closest to the point given. 420 for i := 1; i < len(*p); i++ { 421 if dist := DistanceFromSegment(point, (*p)[i-1], (*p)[i]); dist < minDist { 422 minDist = dist 423 minIndex = i 424 } 425 } 426 427 // Compute the point on the segment found that is closest to the point given. 428 closest := Project(point, (*p)[minIndex-1], (*p)[minIndex]) 429 if closest == (*p)[minIndex] { 430 minIndex++ 431 } 432 433 return closest, minIndex 434 } 435 436 // IsOnRight reports whether the point given is on the right hand side of the 437 // polyline, using a naive definition of "right-hand-sideness" where the point 438 // is on the RHS of the polyline iff the point is on the RHS of the line segment 439 // in the polyline which it is closest to. 440 // The polyline must have at least 2 vertices. 441 func (p *Polyline) IsOnRight(point Point) bool { 442 // If the closest point C is an interior vertex of the polyline, let B and D 443 // be the previous and next vertices. The given point P is on the right of 444 // the polyline (locally) if B, P, D are ordered CCW around vertex C. 445 closest, next := p.Project(point) 446 if closest == (*p)[next-1] && next > 1 && next < len(*p) { 447 if point == (*p)[next-1] { 448 // Polyline vertices are not on the RHS. 449 return false 450 } 451 return OrderedCCW((*p)[next-2], point, (*p)[next], (*p)[next-1]) 452 } 453 // Otherwise, the closest point C is incident to exactly one polyline edge. 454 // We test the point P against that edge. 455 if next == len(*p) { 456 next-- 457 } 458 return Sign(point, (*p)[next], (*p)[next-1]) 459 } 460 461 // Validate checks whether this is a valid polyline or not. 462 func (p *Polyline) Validate() error { 463 // All vertices must be unit length. 464 for i, pt := range *p { 465 if !pt.IsUnit() { 466 return fmt.Errorf("vertex %d is not unit length", i) 467 } 468 } 469 470 // Adjacent vertices must not be identical or antipodal. 471 for i := 1; i < len(*p); i++ { 472 prev, cur := (*p)[i-1], (*p)[i] 473 if prev == cur { 474 return fmt.Errorf("vertices %d and %d are identical", i-1, i) 475 } 476 if prev == (Point{cur.Mul(-1)}) { 477 return fmt.Errorf("vertices %d and %d are antipodal", i-1, i) 478 } 479 } 480 481 return nil 482 } 483 484 // Intersects reports whether this polyline intersects the given polyline. If 485 // the polylines share a vertex they are considered to be intersecting. When a 486 // polyline endpoint is the only intersection with the other polyline, the 487 // function may return true or false arbitrarily. 488 // 489 // The running time is quadratic in the number of vertices. 490 func (p *Polyline) Intersects(o *Polyline) bool { 491 if len(*p) == 0 || len(*o) == 0 { 492 return false 493 } 494 495 if !p.RectBound().Intersects(o.RectBound()) { 496 return false 497 } 498 499 // TODO(roberts): Use ShapeIndex here. 500 for i := 1; i < len(*p); i++ { 501 crosser := NewChainEdgeCrosser((*p)[i-1], (*p)[i], (*o)[0]) 502 for j := 1; j < len(*o); j++ { 503 if crosser.ChainCrossingSign((*o)[j]) != DoNotCross { 504 return true 505 } 506 } 507 } 508 return false 509 } 510 511 // Interpolate returns the point whose distance from vertex 0 along the polyline is 512 // the given fraction of the polyline's total length, and the index of 513 // the next vertex after the interpolated point P. Fractions less than zero 514 // or greater than one are clamped. The return value is unit length. The cost of 515 // this function is currently linear in the number of vertices. 516 // 517 // This method allows the caller to easily construct a given suffix of the 518 // polyline by concatenating P with the polyline vertices starting at that next 519 // vertex. Note that P is guaranteed to be different than the point at the next 520 // vertex, so this will never result in a duplicate vertex. 521 // 522 // The polyline must not be empty. Note that if fraction >= 1.0, then the next 523 // vertex will be set to len(p) (indicating that no vertices from the polyline 524 // need to be appended). The value of the next vertex is always between 1 and 525 // len(p). 526 // 527 // This method can also be used to construct a prefix of the polyline, by 528 // taking the polyline vertices up to next vertex-1 and appending the 529 // returned point P if it is different from the last vertex (since in this 530 // case there is no guarantee of distinctness). 531 func (p *Polyline) Interpolate(fraction float64) (Point, int) { 532 // We intentionally let the (fraction >= 1) case fall through, since 533 // we need to handle it in the loop below in any case because of 534 // possible roundoff errors. 535 if fraction <= 0 { 536 return (*p)[0], 1 537 } 538 target := s1.Angle(fraction) * p.Length() 539 540 for i := 1; i < len(*p); i++ { 541 length := (*p)[i-1].Distance((*p)[i]) 542 if target < length { 543 // This interpolates with respect to arc length rather than 544 // straight-line distance, and produces a unit-length result. 545 result := InterpolateAtDistance(target, (*p)[i-1], (*p)[i]) 546 547 // It is possible that (result == vertex(i)) due to rounding errors. 548 if result == (*p)[i] { 549 return result, i + 1 550 } 551 return result, i 552 } 553 target -= length 554 } 555 556 return (*p)[len(*p)-1], len(*p) 557 } 558 559 // Uninterpolate is the inverse operation of Interpolate. Given a point on the 560 // polyline, it returns the ratio of the distance to the point from the 561 // beginning of the polyline over the length of the polyline. The return 562 // value is always betwen 0 and 1 inclusive. 563 // 564 // The polyline should not be empty. If it has fewer than 2 vertices, the 565 // return value is zero. 566 func (p *Polyline) Uninterpolate(point Point, nextVertex int) float64 { 567 if len(*p) < 2 { 568 return 0 569 } 570 571 var sum s1.Angle 572 for i := 1; i < nextVertex; i++ { 573 sum += (*p)[i-1].Distance((*p)[i]) 574 } 575 lengthToPoint := sum + (*p)[nextVertex-1].Distance(point) 576 for i := nextVertex; i < len(*p); i++ { 577 sum += (*p)[i-1].Distance((*p)[i]) 578 } 579 // The ratio can be greater than 1.0 due to rounding errors or because the 580 // point is not exactly on the polyline. 581 return minFloat64(1.0, float64(lengthToPoint/sum)) 582 } 583 584 // TODO(roberts): Differences from C++. 585 // NearlyCoversPolyline 586 // InitToSnapped 587 // InitToSimplified 588 // SnapLevel 589 // encode/decode compressed