• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2011 Google Inc.
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 com.google.common.geometry;
18 
19 import com.google.common.collect.Lists;
20 import com.google.common.collect.Sets;
21 
22 import java.util.HashSet;
23 import java.util.List;
24 import java.util.logging.Logger;
25 
26 /**
27  * Tests for {@link S2EdgeIndex}.
28  *
29  * @author andriy@google.com (Andriy Bihun) ported from util/geometry
30  * @author pilloff@google.com (Mark Pilloff) original author
31  */
32 public strictfp class S2EdgeIndexTest extends GeometryTestCase {
33   private static final Logger log = Logger.getLogger(S2EdgeIndexTest.class.getCanonicalName());
34 
35   public static class EdgeVectorIndex extends S2EdgeIndex {
36     private List<S2Edge> edges;
37 
EdgeVectorIndex(List<S2Edge> edges)38     public EdgeVectorIndex(List<S2Edge> edges) {
39       this.edges = edges;
40     }
41 
42     @Override
getNumEdges()43     protected int getNumEdges() {
44       return edges.size();
45     }
46 
47     @Override
edgeFrom(int index)48     protected S2Point edgeFrom(int index) {
49       return edges.get(index).getStart();
50     }
51 
52     @Override
edgeTo(int index)53     protected S2Point edgeTo(int index) {
54       return edges.get(index).getEnd();
55     }
56   }
57 
58   /**
59    * Generates a random edge whose center is in the given cap.
60    */
randomEdgeCrossingCap(double maxLengthMeters, S2Cap cap)61   private S2Edge randomEdgeCrossingCap(double maxLengthMeters, S2Cap cap) {
62     // Pick the edge center at random.
63     S2Point edgeCenter = samplePoint(cap);
64     // Pick two random points in a suitably sized cap about the edge center.
65     S2Cap edgeCap = S2Cap.fromAxisAngle(
66         edgeCenter, S1Angle.radians(maxLengthMeters / S2LatLng.EARTH_RADIUS_METERS / 2));
67     S2Point p1 = samplePoint(edgeCap);
68     S2Point p2 = samplePoint(edgeCap);
69     return new S2Edge(p1, p2);
70   }
71 
72   /*
73    * Generates "numEdges" random edges, of length at most "edgeLengthMetersMax"
74    * and each of whose center is in a randomly located cap with radius
75    * "capSpanMeters", and puts results into "edges".
76    */
generateRandomEarthEdges( double edgeLengthMetersMax, double capSpanMeters, int numEdges, List<S2Edge> edges)77   private void generateRandomEarthEdges(
78       double edgeLengthMetersMax, double capSpanMeters, int numEdges, List<S2Edge> edges) {
79     S2Cap cap = S2Cap.fromAxisAngle(
80         randomPoint(), S1Angle.radians(capSpanMeters / S2LatLng.EARTH_RADIUS_METERS));
81     for (int i = 0; i < numEdges; ++i) {
82       edges.add(randomEdgeCrossingCap(edgeLengthMetersMax, cap));
83     }
84   }
85 
checkAllCrossings( List<S2Edge> allEdges, int minCrossings, int maxChecksCrossingsRatio)86   private void checkAllCrossings(
87       List<S2Edge> allEdges, int minCrossings, int maxChecksCrossingsRatio) {
88     EdgeVectorIndex index = new EdgeVectorIndex(allEdges);
89     index.computeIndex();
90     EdgeVectorIndex.DataEdgeIterator it = new EdgeVectorIndex.DataEdgeIterator(index);
91     double totalCrossings = 0;
92     double totalIndexChecks = 0;
93 
94     for (int in = 0; in < allEdges.size(); ++in) {
95       S2Edge e = allEdges.get(in);
96 
97       HashSet<Integer> candidateSet = Sets.newHashSet();
98 
99       StringBuilder sb = new StringBuilder();
100       for (it.getCandidates(e.getStart(), e.getEnd()); it.hasNext(); it.next()) {
101         candidateSet.add(it.index());
102         sb.append(it.index()).append("/");
103         ++totalIndexChecks;
104       }
105 
106       for (int i = 0; i < allEdges.size(); ++i) {
107         int crossing = S2EdgeUtil.robustCrossing(
108             e.getStart(), e.getEnd(), allEdges.get(i).getStart(), allEdges.get(i).getEnd());
109         if (crossing >= 0) {
110           StringBuilder sbError = new StringBuilder();
111           sbError
112               .append("\n==CHECK_ERROR===================================\n")
113               .append("CandidateSet: ")
114               .append(sb)
115               .append("\nin=")
116               .append(in)
117               .append(" i=")
118               .append(i)
119               .append(" robustCrossing=")
120               .append(crossing)
121               .append("\nfrom:\n")
122               .append(e)
123               .append("\nto:\n")
124               .append(allEdges.get(i))
125               .append("\n==================================================");
126           assertTrue(sbError.toString(), candidateSet.contains(i));
127           ++totalCrossings;
128         }
129       }
130     }
131 
132     log.info(
133         "Pairs/num crossings/check crossing ratio: "
134             + Integer.toString(allEdges.size() * allEdges.size()) + "/"
135             + Double.toString(totalCrossings) + "/"
136             + Double.toString(totalIndexChecks / totalCrossings));
137     assertTrue(minCrossings <= totalCrossings);
138     assertTrue(totalCrossings * maxChecksCrossingsRatio >= totalIndexChecks);
139   }
140 
141   /*
142    * Generates random edges and tests, for each edge, that all those that cross
143    * are candidates.
144    */
tryCrossingsRandomInCap(int numEdges, double edgeLengthMax, double capSpanMeters, int minCrossings, int maxChecksCrossingsRatio)145   private void tryCrossingsRandomInCap(int numEdges, double edgeLengthMax, double capSpanMeters,
146       int minCrossings, int maxChecksCrossingsRatio) {
147     List<S2Edge> allEdges = Lists.newArrayList();
148     generateRandomEarthEdges(edgeLengthMax, capSpanMeters, numEdges, allEdges);
149     checkAllCrossings(allEdges, minCrossings, maxChecksCrossingsRatio);
150   }
151 
testSpecificEdges()152   public void testSpecificEdges() {
153     List<S2Point> ps = Lists.newArrayList();
154     ps.add(new S2Point(0.8088625416501157, -0.40633615485481134, 0.4250086092929434));
155     ps.add(new S2Point(0.8088939911085784, -0.40631384442755236, 0.4249700824469155));
156     ps.add(new S2Point(0.8088088971141814, -0.40642839367135375, 0.425022503835579));
157     ps.add(new S2Point(0.8088643962606756, -0.406333410696549, 0.4250077032402616));
158     List<S2Edge> allEdges = Lists.newArrayList();
159     allEdges.add(new S2Edge(ps.get(0), ps.get(1)));
160     allEdges.add(new S2Edge(ps.get(2), ps.get(3)));
161     checkAllCrossings(allEdges, 0, 16);
162   }
163 
testLoopCandidateOfItself()164   public void testLoopCandidateOfItself() {
165     List<S2Point> ps = Lists.newArrayList(); // A diamond loop around 0,180.
166     ps.add(makePoint("0:178"));
167     ps.add(makePoint("-1:180"));
168     ps.add(makePoint("0:-179"));
169     ps.add(makePoint("1:-180"));
170     List<S2Edge> allEdges = Lists.newArrayList();
171     for (int i = 0; i < 4; ++i) {
172       allEdges.add(new S2Edge(ps.get(i), ps.get((i + 1) % 4)));
173     }
174     checkAllCrossings(allEdges, 0, 16);
175   }
176 
testRandomEdgeCrossings()177   public void testRandomEdgeCrossings() {
178     tryCrossingsRandomInCap(2000, 30, 5000, 500, 2);
179     tryCrossingsRandomInCap(1000, 100, 5000, 500, 3);
180     tryCrossingsRandomInCap(1000, 1000, 5000, 1000, 40);
181     tryCrossingsRandomInCap(500, 5000, 5000, 5000, 20);
182   }
183 
testRandomEdgeCrossingsSparse()184   public void testRandomEdgeCrossingsSparse() {
185     for (int i = 0; i < 5; ++i) {
186       tryCrossingsRandomInCap(2000, 100, 5000, 500, 8);
187       tryCrossingsRandomInCap(2000, 300, 50000, 1000, 10);
188     }
189   }
190 }
191