blob: 6928171f51eab4f9548d489c30d3541df42f4707 [file] [log] [blame]
/*
* Copyright 2011 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.common.geometry;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Logger;
/**
* Tests for {@link S2EdgeIndex}.
*
* @author [email protected] (Andriy Bihun) ported from util/geometry
* @author [email protected] (Mark Pilloff) original author
*/
public strictfp class S2EdgeIndexTest extends GeometryTestCase {
private static final Logger log = Logger.getLogger(S2EdgeIndexTest.class.getCanonicalName());
public static class EdgeVectorIndex extends S2EdgeIndex {
private List<S2Edge> edges;
public EdgeVectorIndex(List<S2Edge> edges) {
this.edges = edges;
}
@Override
protected int getNumEdges() {
return edges.size();
}
@Override
protected S2Point edgeFrom(int index) {
return edges.get(index).getStart();
}
@Override
protected S2Point edgeTo(int index) {
return edges.get(index).getEnd();
}
}
/**
* Generates a random edge whose center is in the given cap.
*/
private S2Edge randomEdgeCrossingCap(double maxLengthMeters, S2Cap cap) {
// Pick the edge center at random.
S2Point edgeCenter = samplePoint(cap);
// Pick two random points in a suitably sized cap about the edge center.
S2Cap edgeCap = S2Cap.fromAxisAngle(
edgeCenter, S1Angle.radians(maxLengthMeters / S2LatLng.EARTH_RADIUS_METERS / 2));
S2Point p1 = samplePoint(edgeCap);
S2Point p2 = samplePoint(edgeCap);
return new S2Edge(p1, p2);
}
/*
* Generates "numEdges" random edges, of length at most "edgeLengthMetersMax"
* and each of whose center is in a randomly located cap with radius
* "capSpanMeters", and puts results into "edges".
*/
private void generateRandomEarthEdges(
double edgeLengthMetersMax, double capSpanMeters, int numEdges, List<S2Edge> edges) {
S2Cap cap = S2Cap.fromAxisAngle(
randomPoint(), S1Angle.radians(capSpanMeters / S2LatLng.EARTH_RADIUS_METERS));
for (int i = 0; i < numEdges; ++i) {
edges.add(randomEdgeCrossingCap(edgeLengthMetersMax, cap));
}
}
private void checkAllCrossings(
List<S2Edge> allEdges, int minCrossings, int maxChecksCrossingsRatio) {
EdgeVectorIndex index = new EdgeVectorIndex(allEdges);
index.computeIndex();
EdgeVectorIndex.DataEdgeIterator it = new EdgeVectorIndex.DataEdgeIterator(index);
double totalCrossings = 0;
double totalIndexChecks = 0;
for (int in = 0; in < allEdges.size(); ++in) {
S2Edge e = allEdges.get(in);
HashSet<Integer> candidateSet = Sets.newHashSet();
StringBuilder sb = new StringBuilder();
for (it.getCandidates(e.getStart(), e.getEnd()); it.hasNext(); it.next()) {
candidateSet.add(it.index());
sb.append(it.index()).append("/");
++totalIndexChecks;
}
for (int i = 0; i < allEdges.size(); ++i) {
int crossing = S2EdgeUtil.robustCrossing(
e.getStart(), e.getEnd(), allEdges.get(i).getStart(), allEdges.get(i).getEnd());
if (crossing >= 0) {
StringBuilder sbError = new StringBuilder();
sbError
.append("\n==CHECK_ERROR===================================\n")
.append("CandidateSet: ")
.append(sb)
.append("\nin=")
.append(in)
.append(" i=")
.append(i)
.append(" robustCrossing=")
.append(crossing)
.append("\nfrom:\n")
.append(e)
.append("\nto:\n")
.append(allEdges.get(i))
.append("\n==================================================");
assertTrue(sbError.toString(), candidateSet.contains(i));
++totalCrossings;
}
}
}
log.info(
"Pairs/num crossings/check crossing ratio: "
+ Integer.toString(allEdges.size() * allEdges.size()) + "/"
+ Double.toString(totalCrossings) + "/"
+ Double.toString(totalIndexChecks / totalCrossings));
assertTrue(minCrossings <= totalCrossings);
assertTrue(totalCrossings * maxChecksCrossingsRatio >= totalIndexChecks);
}
/*
* Generates random edges and tests, for each edge, that all those that cross
* are candidates.
*/
private void tryCrossingsRandomInCap(int numEdges, double edgeLengthMax, double capSpanMeters,
int minCrossings, int maxChecksCrossingsRatio) {
List<S2Edge> allEdges = Lists.newArrayList();
generateRandomEarthEdges(edgeLengthMax, capSpanMeters, numEdges, allEdges);
checkAllCrossings(allEdges, minCrossings, maxChecksCrossingsRatio);
}
public void testSpecificEdges() {
List<S2Point> ps = Lists.newArrayList();
ps.add(new S2Point(0.8088625416501157, -0.40633615485481134, 0.4250086092929434));
ps.add(new S2Point(0.8088939911085784, -0.40631384442755236, 0.4249700824469155));
ps.add(new S2Point(0.8088088971141814, -0.40642839367135375, 0.425022503835579));
ps.add(new S2Point(0.8088643962606756, -0.406333410696549, 0.4250077032402616));
List<S2Edge> allEdges = Lists.newArrayList();
allEdges.add(new S2Edge(ps.get(0), ps.get(1)));
allEdges.add(new S2Edge(ps.get(2), ps.get(3)));
checkAllCrossings(allEdges, 0, 16);
}
public void testLoopCandidateOfItself() {
List<S2Point> ps = Lists.newArrayList(); // A diamond loop around 0,180.
ps.add(makePoint("0:178"));
ps.add(makePoint("-1:180"));
ps.add(makePoint("0:-179"));
ps.add(makePoint("1:-180"));
List<S2Edge> allEdges = Lists.newArrayList();
for (int i = 0; i < 4; ++i) {
allEdges.add(new S2Edge(ps.get(i), ps.get((i + 1) % 4)));
}
checkAllCrossings(allEdges, 0, 16);
}
public void testRandomEdgeCrossings() {
tryCrossingsRandomInCap(2000, 30, 5000, 500, 2);
tryCrossingsRandomInCap(1000, 100, 5000, 500, 3);
tryCrossingsRandomInCap(1000, 1000, 5000, 1000, 40);
tryCrossingsRandomInCap(500, 5000, 5000, 5000, 20);
}
public void testRandomEdgeCrossingsSparse() {
for (int i = 0; i < 5; ++i) {
tryCrossingsRandomInCap(2000, 100, 5000, 500, 8);
tryCrossingsRandomInCap(2000, 300, 50000, 1000, 10);
}
}
}