1
// Copyright 2009 Google Inc. All Rights Reserved.
3
#include "s2edgeindex.h"
16
#include "base/commandlineflags.h"
17
#include "base/stringprintf.h"
18
#include "base/logging.h"
19
#include "testing/base/public/benchmark.h"
20
#include "testing/base/public/gunit.h"
24
#include "s2edgeutil.h"
26
#include "s2testing.h"
27
#include "util/math/vector3-inl.h"
28
#include "util/math/matrix3x3-inl.h"
30
DECLARE_bool(always_recurse_on_children);
32
typedef pair<S2Point, S2Point> S2Edge;
34
static const double kEarthRadiusMeters = 6371000;
38
// Generates a random edge whose center is in the given cap.
39
static S2Edge RandomEdgeCrossingCap(double max_length_meters,
41
// Pick the edge center at random.
42
S2Point edge_center = S2Testing::SamplePoint(cap);
43
// Pick two random points in a suitably sized cap about the edge center.
44
S2Cap edge_cap = S2Cap::FromAxisAngle(
46
S1Angle::Radians(max_length_meters / kEarthRadiusMeters / 2));
47
S2Point p1 = S2Testing::SamplePoint(edge_cap);
48
S2Point p2 = S2Testing::SamplePoint(edge_cap);
49
return S2Edge(p1, p2);
52
// Generates "num_edges" random edges, of length at most
53
// "edge_length_meters_max" and each of whose center is in a randomly located
54
// cap with radius "cap_span_meters", and puts results into "edges".
55
static void GenerateRandomEarthEdges(double edge_length_meters_max,
56
double cap_span_meters,
58
vector<S2Edge>* edges) {
59
S2Cap cap = S2Cap::FromAxisAngle(
60
S2Testing::RandomPoint(),
61
S1Angle::Radians(cap_span_meters / kEarthRadiusMeters));
62
for (int i = 0; i < num_edges; ++i) {
63
edges->push_back(RandomEdgeCrossingCap(edge_length_meters_max, cap));
67
static string ToString(const S2Edge& e1, const S2Edge& e2) {
68
double u1, v1, u2, v2, u3, v3, u4, v4;
69
S2::XYZtoFaceUV(e1.first, &u1, &v1);
70
S2::XYZtoFaceUV(e1.second, &u2, &v2);
71
S2::XYZtoFaceUV(e2.first, &u3, &v3);
72
S2::XYZtoFaceUV(e2.second, &u4, &v4);
79
return StringPrintf("[%6.2f,%6.2f] 0,0 -> %3.3e,%3.3e) --"
80
" (%3.3e,%3.3e -> %3.3e %3.3e)",
81
u1, v1, u2, v2, u3, v3, u4, v4);
84
class EdgeVectorIndex: public S2EdgeIndex {
86
explicit EdgeVectorIndex(vector<S2Edge> const* edges):
89
virtual int num_edges() const { return edges_->size(); }
90
virtual S2Point const* edge_from(int index) const {
91
return &((*edges_)[index].first);
93
virtual S2Point const* edge_to(int index) const {
94
return &((*edges_)[index].second);
97
vector<S2Edge> const* edges_;
100
static void TestAllCrossings(vector<S2Edge> const& all_edges,
102
int max_checks_crossings_ratio) {
103
EdgeVectorIndex index(&all_edges);
104
index.ComputeIndex();
105
EdgeVectorIndex::Iterator it(&index);
106
double total_crossings = 0;
107
double total_index_checks = 0;
109
VLOG(1) << "start detailed checking\n";
110
for (int in = 0; in < all_edges.size(); ++in) {
111
S2Edge e = all_edges[in];
113
set<int> candidate_set;
115
for (it.GetCandidates(e.first, e.second); !it.Done(); it.Next()) {
116
candidate_set.insert(it.Index());
117
total_index_checks++;
120
for (int i = 0; i < all_edges.size(); ++i) {
121
int crossing = S2EdgeUtil::RobustCrossing(e.first, e.second,
123
all_edges[i].second);
125
EXPECT_EQ(1, candidate_set.count(i))
126
<< "Edge " << i << " is not a candidate of edge " << in
127
<< " (" << ToString(all_edges[i], e) << ")";
133
VLOG(1) << "Pairs/num crossings/check crossing ratio: "
134
<< all_edges.size() * all_edges.size() << "/"
135
<< total_crossings << "/"
136
<< total_index_checks / total_crossings;
137
EXPECT_LE(min_crossings, total_crossings);
138
EXPECT_GE(total_crossings * max_checks_crossings_ratio, total_index_checks);
142
// Generates random edges and tests, for each edge,
143
// that all those that cross are candidates.
144
static void TestCrossingsRandomInCap(int num_edges,
145
double edge_length_max,
146
double cap_span_meters,
148
int max_checks_crossings_ratio) {
149
vector<S2Edge> all_edges;
150
GenerateRandomEarthEdges(edge_length_max, cap_span_meters, num_edges,
152
TestAllCrossings(all_edges, min_crossings, max_checks_crossings_ratio);
156
TEST(S2EdgeIndex, LoopCandidateOfItself) {
157
vector<S2Point> ps; // A diamond loop around 0,180.
158
ps.push_back(S2Testing::MakePoint("0:178"));
159
ps.push_back(S2Testing::MakePoint("-1:180"));
160
ps.push_back(S2Testing::MakePoint("0:-179"));
161
ps.push_back(S2Testing::MakePoint("1:-180"));
162
vector<S2Edge> all_edges;
163
for (int i = 0; i < 4; ++i) {
164
all_edges.push_back(make_pair(ps[i], ps[(i+1)%4]));
166
TestAllCrossings(all_edges, 0, 16);
169
TEST(S2EdgeIndex, RandomEdgeCrossings) {
170
TestCrossingsRandomInCap(2000, 30, 5000, 500, 2);
171
TestCrossingsRandomInCap(1000, 100, 5000, 500, 3);
172
TestCrossingsRandomInCap(1000, 1000, 5000, 1000, 40);
173
TestCrossingsRandomInCap(500, 5000, 5000, 5000, 20);
176
TEST(S2EdgeIndex, RandomEdgeCrossingsSparse) {
177
for (int i = 0; i < 5; ++i) {
178
TestCrossingsRandomInCap(2000, 100, 5000, 500, 8);
179
TestCrossingsRandomInCap(2000, 300, 50000, 1000, 10);
183
TEST(S2EdgeIndex, DegenerateEdgeOnCellVertexIsItsOwnCandidate) {
184
for (int i = 0; i < 100; ++i) {
185
S2Cell cell(S2Testing::GetRandomCellId());
186
S2Edge e = make_pair(cell.GetVertex(0), cell.GetVertex(0));
187
vector<S2Edge> all_edges;
188
all_edges.push_back(e);
189
EdgeVectorIndex index(&all_edges);
190
index.ComputeIndex();
191
EdgeVectorIndex::Iterator it(&index);
192
it.GetCandidates(e.first, e.second);
193
ASSERT_FALSE(it.Done());
195
ASSERT_TRUE(it.Done());
199
TEST(S2EdgeIndex, LongEdgeCrossesLoop) {
200
vector<S2Edge> all_edges;
201
S2Point loop_center = S2Testing::MakePoint("42:107");
202
pair<S2Point, S2Point> test_edge = make_pair(S2::Origin(), loop_center);
204
S2Loop* loop = S2Testing::MakeRegularLoop(loop_center, 4,
205
7e-3); // = 5km/6400km
206
for (int i = 0; i < loop->num_vertices(); ++i) {
207
all_edges.push_back(make_pair(loop->vertex(i), loop->vertex(i+1)));
210
EdgeVectorIndex index(&all_edges);
211
index.ComputeIndex();
212
EdgeVectorIndex::Iterator it(&index);
213
it.GetCandidates(test_edge.first, test_edge.second);
215
ASSERT_FALSE(it.Done()); // At least one candidate
218
TEST(S2EdgeIndex, CollinearEdgesOnCellBoundaries) {
219
FLAGS_always_recurse_on_children = true;
220
const int kNumPointsOnEdge = 8; // About 32 edges
221
for (int level = 0; level <= S2CellId::kMaxLevel; ++level) {
222
S2Cell cell(S2Testing::GetRandomCellId(level));
223
int v1 = S2Testing::rnd.Uniform(4);
224
int v2 = (v1 + 1) & 3;
225
S2Point p1 = cell.GetVertex(v1);
226
S2Point p2 = cell.GetVertex(v2);
227
S2Point p2_p1 = (p2 - p1) / kNumPointsOnEdge;
228
vector<S2Edge> all_edges;
229
S2Point points[kNumPointsOnEdge+1];
230
for (int i = 0; i <= kNumPointsOnEdge; ++i) {
231
points[i] = (p1 + i * p2_p1).Normalize();
232
for (int j = 0; j < i; ++j) {
233
all_edges.push_back(make_pair(points[i], points[j]));
236
TestAllCrossings(all_edges, kNumPointsOnEdge*kNumPointsOnEdge,
237
kNumPointsOnEdge*kNumPointsOnEdge);
241
TEST(S2EdgeIndex, QuadTreeGetsComputedAutomatically) {
242
int const kNumVertices = 200;
243
S2Point loop_center = S2Testing::MakePoint("42:107");
244
S2Loop* loop = S2Testing::MakeRegularLoop(loop_center, kNumVertices,
245
7e-3); // = 5km/6400km
246
S2Point q = S2Testing::MakePoint("5:5");
248
S2LoopIndex index(loop);
249
S2LoopIndex::Iterator it(&index);
251
int num_candidates = 0;
252
it.GetCandidates(q, q);
253
for (; !it.Done(); it.Next()) {
256
EXPECT_EQ(kNumVertices, num_candidates);
258
bool computed = false;
259
for (int i = 0; i < 500; ++i) { // Trigger the quad tree computation
261
for (it.GetCandidates(q, q); !it.Done(); it.Next()) num_candidates++;
262
if (!computed && num_candidates == 0) {
263
LOG(INFO) << "Index was computed at iteration " << i;
269
for (it.GetCandidates(q, q); !it.Done(); it.Next()) num_candidates++;
270
EXPECT_EQ(0, num_candidates);
276
// MICROBENCHMARKS (and related structures)
278
// Generates a bunch of random edges and tests each against all others for
279
// crossings. This is just for benchmarking; there's no correctness testing in
280
// this function. Set "cutoff_level" negative to apply brute force checking.
281
static void ComputeCrossings(int num_edges,
282
double edge_length_max,
283
double cap_span_meters,
285
StopBenchmarkTiming();
286
vector<S2Edge> all_edges;
287
GenerateRandomEarthEdges(edge_length_max, cap_span_meters, num_edges,
289
StartBenchmarkTiming();
291
for (vector<S2Edge>::const_iterator it = all_edges.begin();
292
it != all_edges.end(); ++it) {
293
for (vector<S2Edge>::const_iterator it2 = all_edges.begin();
294
it2 != all_edges.end(); ++it2) {
295
S2EdgeUtil::RobustCrossing(it->first, it->second,
296
it2->first, it2->second);
300
EdgeVectorIndex index(&all_edges);
301
index.ComputeIndex();
302
EdgeVectorIndex::Iterator can_it(&index);
303
for (vector<S2Edge>::const_iterator it = all_edges.begin();
304
it != all_edges.end(); ++it) {
305
for (can_it.GetCandidates(it->first, it->second);
306
!can_it.Done(); can_it.Next()) {
307
int in = can_it.Index();
308
S2EdgeUtil::RobustCrossing(all_edges[in].first, all_edges[in].second,
316
// "Sparse" tests are those where we expect relatively few segment crossings.
317
// In general the segment lengths are short (< 300m) and the random segments
318
// are distributed over caps of radius 5-50km.
320
static void BM_TestCrossingsSparse(int iters, int num_edges, int brute_force) {
321
for (int i = 0; i < iters; ++i) {
322
ComputeCrossings(num_edges, 30, 5000, brute_force);
323
ComputeCrossings(num_edges, 100, 5000, brute_force);
326
BENCHMARK(BM_TestCrossingsSparse)
332
->ArgPair(100, false)
334
->ArgPair(300, false)
336
->ArgPair(600, false)
337
->ArgPair(1000, true)
338
->ArgPair(1000, false)
339
->ArgPair(2000, false)
340
->ArgPair(5000, false)
341
->ArgPair(10000, false)
342
->ArgPair(20000, false)
343
->ArgPair(50000, false)
344
->ArgPair(100000, false);
346
// These benchmarks are used to find the right trade-off between brute force
349
// To compute kQuadTreeInsertionCost
350
static void BM_QuadTreeInsertionCost(int iters) {
351
const int kNumVertices = 1000;
352
StopBenchmarkTiming();
353
S2Point loop_center = S2Testing::MakePoint("42:107");
354
S2Loop* loop = S2Testing::MakeRegularLoop(loop_center, kNumVertices,
355
7e-3); // = 5km/6400km
356
StartBenchmarkTiming();
358
for (int i = 0; i < iters; ++i) {
359
S2LoopIndex index(loop);
360
index.ComputeIndex();
364
BENCHMARK(BM_QuadTreeInsertionCost);
366
// To compute kQuadTreeFindCost
367
static void BM_QuadTreeFindCost(int iters, int num_vertices) {
368
StopBenchmarkTiming();
369
S2Point loop_center = S2Testing::MakePoint("42:107");
370
S2Loop* loop = S2Testing::MakeRegularLoop(loop_center, num_vertices,
371
7e-3); // = 5km/6400km
372
S2Point p(S2Testing::MakePoint("42:106.99"));
373
S2Point q(S2Testing::MakePoint("42:107.01"));
374
S2LoopIndex index(loop);
375
index.ComputeIndex();
376
EdgeVectorIndex::Iterator can_it(&index);
377
StartBenchmarkTiming();
379
for (int i = 0; i < iters; ++i) {
380
can_it.GetCandidates(p, q);
384
BENCHMARK(BM_QuadTreeFindCost)