github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/types/s2.go (about)

     1  /*
     2   * Copyright 2016-2018 Dgraph Labs, Inc. and Contributors
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package types
    18  
    19  import (
    20  	"encoding/json"
    21  
    22  	"github.com/dgraph-io/dgraph/x"
    23  	"github.com/golang/geo/s2"
    24  	"github.com/pkg/errors"
    25  	geom "github.com/twpayne/go-geom"
    26  	"github.com/twpayne/go-geom/encoding/geojson"
    27  )
    28  
    29  func edgesCrossPoints(l *s2.Loop, pts []s2.Point) bool {
    30  	n := len(pts)
    31  	for i := 0; i < n; i++ {
    32  		crosser := s2.NewChainEdgeCrosser(pts[i], pts[(i+1)%n], l.Vertex(0))
    33  		for i := 1; i <= l.NumEdges(); i++ { // add vertex 0 twice as it is a closed loop
    34  			if crosser.EdgeOrVertexChainCrossing(l.Vertex(i)) {
    35  				return true
    36  			}
    37  		}
    38  	}
    39  	return false
    40  }
    41  
    42  func intersects(l *s2.Loop, loop *s2.Loop) bool {
    43  	// Quick check if the bounding boxes intersect
    44  	if !l.RectBound().Intersects(loop.RectBound()) {
    45  		return false
    46  	}
    47  
    48  	// Quick check to see if the first vertex is in the loop.
    49  	if l.ContainsPoint(loop.Vertex(0)) {
    50  		return true
    51  	}
    52  
    53  	// At this stage the one vertex is outside the loop. We check if any of the edges of the cell
    54  	// cross the loop.
    55  	if edgesCrossPoints(l, loop.Vertices()) {
    56  		return true
    57  	}
    58  
    59  	// At this stage we know that there is one point of the loop is outside us and the boundaries do
    60  	// not interesect. The only way for the loops to intersect is if it contains us.  We test this
    61  	// by checking if an arbitrary vertex is inside the loop.
    62  	if loop.RectBound().Contains(l.RectBound()) {
    63  		return loop.ContainsPoint(l.Vertex(0))
    64  	}
    65  	return false
    66  }
    67  
    68  func findVertex(a *s2.Loop, p s2.Point) int {
    69  	pts := a.Vertices()
    70  	for i := 0; i < len(pts); i++ {
    71  		if pts[i].ApproxEqual(p) {
    72  			return i
    73  		}
    74  	}
    75  	return -1
    76  }
    77  
    78  // Contains checks whether loop A contains loop B.
    79  func Contains(a *s2.Loop, b *s2.Loop) bool {
    80  	// For this loop A to contains the given loop B, all of the following must
    81  	// be true:
    82  	//
    83  	//  (1) There are no edge crossings between A and B except at vertices.
    84  	//
    85  	//  (2) At every vertex that is shared between A and B, the local edge
    86  	//      ordering implies that A contains B.
    87  	//
    88  	//  (3) If there are no shared vertices, then A must contain a vertex of B
    89  	//      and B must not contain a vertex of A.  (An arbitrary vertex may be
    90  	//      chosen in each case.)
    91  	//
    92  	// The second part of (3) is necessary to detect the case of two loops whose
    93  	// union is the entire sphere, i.e. two loops that contains each other's
    94  	// boundaries but not each other's interiors.
    95  
    96  	if !a.RectBound().Contains(b.RectBound()) {
    97  		return false
    98  	}
    99  
   100  	// Unless there are shared vertices, we need to check whether A contains a
   101  	// vertex of B.  Since shared vertices are rare, it is more efficient to do
   102  	// this test up front as a quick rejection test.
   103  	if !a.ContainsPoint(b.Vertex(0)) && findVertex(a, b.Vertex(0)) < 0 {
   104  		return false
   105  	}
   106  
   107  	// Now check whether there are any edge crossings.
   108  	if edgesCrossPoints(a, b.Vertices()) {
   109  		return false
   110  	}
   111  
   112  	// At this point we know that the boundaries of A and B do not intersect,
   113  	// and that A contains a vertex of B.  However we still need to check for
   114  	// the case mentioned above, where (A union B) is the entire sphere.
   115  	// Normally this check is very cheap due to the bounding box precondition.
   116  	if a.RectBound().Union(b.RectBound()).IsFull() {
   117  		if b.ContainsPoint(a.Vertex(0)) && findVertex(b, a.Vertex(0)) < 0 {
   118  			return false
   119  		}
   120  	}
   121  	return true
   122  
   123  }
   124  
   125  // Intersects returns true if the two loops intersect.
   126  func Intersects(l1 *s2.Loop, l2 *s2.Loop) bool {
   127  	if l2.NumEdges() > l1.NumEdges() {
   128  		// Use the larger loop for edge indexing.
   129  		return intersects(l2, l1)
   130  	}
   131  	return intersects(l1, l2)
   132  }
   133  
   134  func convertToGeom(str string) (geom.T, error) {
   135  	// validate would ensure that we have a closed loop for all the polygons. We don't support open
   136  	// loop polygons.
   137  	closed := func(p *geom.Polygon) error {
   138  		coords := p.Coords()
   139  		if len(coords) == 0 {
   140  			return errors.Errorf("Got empty polygon.")
   141  		}
   142  		// Check that first ring is closed.
   143  		c := coords[0]
   144  		l := len(c)
   145  		if c[0][0] == c[l-1][0] && c[0][1] == c[l-1][1] {
   146  			return nil
   147  		}
   148  		return errors.Errorf("Last coord not same as first")
   149  	}
   150  
   151  	validate := func(g geom.T) (geom.T, error) {
   152  		switch v := g.(type) {
   153  		case *geom.MultiPolygon:
   154  			for i := 0; i < v.NumPolygons(); i++ {
   155  				if err := closed(v.Polygon(i)); err != nil {
   156  					return nil, err
   157  				}
   158  			}
   159  		case *geom.Polygon:
   160  			if err := closed(v); err != nil {
   161  				return nil, err
   162  			}
   163  		}
   164  		return g, nil
   165  	}
   166  
   167  	var g geojson.Geometry
   168  	if err := json.Unmarshal([]byte(str), &g); err == nil {
   169  		t, err := g.Decode()
   170  		if err != nil {
   171  			return nil, err
   172  		}
   173  		return validate(t)
   174  	}
   175  
   176  	s := x.WhiteSpace.Replace(str)
   177  	if len(s) < 5 { // [1,2]
   178  		return nil, errors.Errorf("Invalid coordinates")
   179  	}
   180  	var m json.RawMessage
   181  	var err error
   182  
   183  	if s[0:4] == "[[[[" {
   184  		g.Type = "MultiPolygon"
   185  		err = m.UnmarshalJSON([]byte(s))
   186  		if err != nil {
   187  			return nil, errors.Wrapf(err, "Invalid coordinates")
   188  		}
   189  		g.Coordinates = &m
   190  		g1, err := g.Decode()
   191  		if err != nil {
   192  			return nil, errors.Wrapf(err, "Invalid coordinates")
   193  		}
   194  		return validate(g1)
   195  	}
   196  
   197  	if s[0:3] == "[[[" {
   198  		g.Type = "Polygon"
   199  		err = m.UnmarshalJSON([]byte(s))
   200  		if err != nil {
   201  			return nil, errors.Wrapf(err, "Invalid coordinates")
   202  		}
   203  		g.Coordinates = &m
   204  		g1, err := g.Decode()
   205  		if err != nil {
   206  			return nil, errors.Wrapf(err, "Invalid coordinates")
   207  		}
   208  		return validate(g1)
   209  	}
   210  
   211  	if s[0] == '[' {
   212  		g.Type = "Point"
   213  		err = m.UnmarshalJSON([]byte(s))
   214  		if err != nil {
   215  			return nil, errors.Wrapf(err, "Invalid coordinates")
   216  		}
   217  		g.Coordinates = &m
   218  		return g.Decode()
   219  	}
   220  	return nil, errors.Errorf("Invalid coordinates")
   221  }