-
Notifications
You must be signed in to change notification settings - Fork 65
/
Copy paths2polygonbuilder.h
321 lines (276 loc) · 13.4 KB
/
s2polygonbuilder.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
// Copyright 2006 Google Inc. All Rights Reserved.
#ifndef UTIL_GEOMETRY_S2POLYGONBUILDER_H__
#define UTIL_GEOMETRY_S2POLYGONBUILDER_H__
#if defined __GNUC__ || defined __APPLE__
#include <ext/hash_map>
#else
#include <hash_map>
#endif
using __gnu_cxx::hash_map;
#if defined __GNUC__ || defined __APPLE__
#include <ext/hash_set>
#else
#include <hash_set>
#endif
using __gnu_cxx::hash_set;
#include <set>
using std::set;
using std::multiset;
#include <utility>
using std::pair;
using std::make_pair;
#include <vector>
using std::vector;
#include "base/basictypes.h"
#include "base/scoped_ptr.h"
#include "s2.h"
#include "s1angle.h"
#include "util/math/matrix3x3.h"
class S2Loop;
class S2Polygon;
// This is a simple class for assembling polygons out of edges. It requires
// that no two edges cross. It can handle both directed and undirected edges,
// and optionally it can also remove duplicate edge pairs (consisting of two
// identical edges or an edge and its reverse edge). This is useful for
// computing seamless unions of polygons that have been cut into pieces.
//
// Here are some of the situations this class was designed to handle:
//
// 1. Computing the union of disjoint polygons that may share part of their
// boundaries. For example, reassembling a lake that has been split into
// two loops by a state boundary.
//
// 2. Constructing polygons from input data that does not follow S2
// conventions, i.e. where loops may have repeated vertices, or distinct
// loops may share edges, or shells and holes have opposite or unspecified
// orientations.
//
// 3. Computing the symmetric difference of a set of polygons whose edges
// intersect only at vertices. This can be used to implement a limited
// form of polygon intersection or subtraction as well as unions.
//
// 4. As a tool for implementing other polygon operations by generating a
// collection of directed edges and then assembling them into loops.
class S2PolygonBuilderOptions {
public:
S2PolygonBuilderOptions() :
undirected_edges_(false), xor_edges_(true), validate_(false),
vertex_merge_radius_(S1Angle::Radians(0)),
edge_splice_fraction_(0.866) {}
// These are the options that should be used for assembling well-behaved
// input data into polygons. All edges should be directed such that
// "shells" and "holes" have opposite orientations (typically CCW shells and
// clockwise holes), unless it is known that shells and holes do not share
// any edges.
inline static S2PolygonBuilderOptions DIRECTED_XOR();
// These are the options that should be used for assembling polygons that do
// not follow the conventions above, e.g. where edge directions may vary
// within a single loop, or shells and holes are not oppositely oriented and
// may share edges.
inline static S2PolygonBuilderOptions UNDIRECTED_XOR();
// These are the options that should be used for assembling edges where the
// desired output is a collection of loops rather than a polygon, and edges
// may occur more than once. Edges are treated as undirected and are not
// XORed together.
inline static S2PolygonBuilderOptions UNDIRECTED_UNION();
// Default value: false.
//
// If "undirected_edges" is false, then the input is assumed to consist of
// edges that can be assembled into oriented loops without reversing any of
// the edges. Otherwise, "undirected_edges" should be set to true.
bool undirected_edges() const { return undirected_edges_; }
void set_undirected_edges(bool undirected_edges);
// Default value: true.
//
// If "xor_edges" is true, then any duplicate edge pairs are removed. This
// is useful for computing the union of a collection of polygons whose
// interiors are disjoint but whose boundaries may share some common edges
// (e.g. computing the union of South Africa, Lesotho, and Swaziland).
//
// Note that for directed edges, a "duplicate edge pair" consists of an edge
// and its corresponding reverse edge. This means that either (a) "shells"
// and "holes" must have opposite orientations, or (b) shells and holes do
// not share edges. Otherwise undirected_edges() should be specified.
//
// There are only two reasons to turn off xor_edges():
//
// (1) AssemblePolygon() will be called, and you want to assert that there
// are no duplicate edge pairs in the input.
//
// (2) AssembleLoops() will be called, and you want to keep abutting loops
// separate in the output rather than merging their regions together
// (e.g. assembling loops for Kansas City, KS and Kansas City, MO
// simultaneously).
bool xor_edges() const { return xor_edges_; }
void set_xor_edges(bool xor_edges);
// Default value: false.
//
// If true, IsValid() is called on all loops and polygons before
// constructing them. If any loop is invalid (e.g. self-intersecting), it
// is rejected and returned as a set of "unused edges". Any remaining valid
// loops are kept. If the entire polygon is invalid (e.g. two loops
// intersect), then all loops are rejected and returned as unused edges.
bool validate() const { return validate_; }
void set_validate(bool validate);
// Default value: 0.
//
// If set to a positive value, all vertex pairs that are separated by less
// than this distance will be merged together. Note that vertices can move
// arbitrarily far if there is a long chain of vertices separated by less
// than this distance.
//
// This method is useful for assembling polygons out of input data where
// vertices and/or edges may not be perfectly aligned.
S1Angle vertex_merge_radius() const { return vertex_merge_radius_; }
void set_vertex_merge_radius(S1Angle const& vertex_merge_radius);
// Default value: 0.866 (approximately sqrt(3)/2).
//
// The edge splice radius is automatically set to this fraction of the vertex
// merge radius. If the edge splice radius is positive, then all vertices
// that are closer than this distance to an edge are spliced into that edge.
// Note that edges can move arbitrarily far if there is a long chain of
// vertices in just the right places.
//
// You can turn off edge splicing by setting this value to zero. This will
// save some time if you don't need this feature, or you don't want vertices
// to be spliced into nearby edges for some reason.
//
// Note that the edge splice fraction must be less than sqrt(3)/2 in order to
// avoid infinite loops in the merge algorithm. The default value is very
// close to this maximum and therefore results in the maximum amount of edge
// splicing for a given vertex merge radius.
//
// The only reason to reduce the edge splice fraction is if you want to limit
// changes in edge direction due to splicing. The direction of an edge can
// change by up to asin(edge_splice_fraction) due to each splice. Thus by
// default, edges are allowed to change direction by up to 60 degrees per
// splice. However, note that most direction changes are much smaller than
// this: the worst case occurs only if the vertex being spliced is just
// outside the vertex merge radius from one of the edge endpoints.
double edge_splice_fraction() const { return edge_splice_fraction_; }
void set_edge_splice_fraction(double edge_splice_fraction);
private:
bool undirected_edges_;
bool xor_edges_;
bool validate_;
S1Angle vertex_merge_radius_;
double edge_splice_fraction_;
};
class S2PolygonBuilder {
public:
explicit S2PolygonBuilder(S2PolygonBuilderOptions const& options);
~S2PolygonBuilder();
S2PolygonBuilderOptions const& options() const { return options_; }
// Add the given edge to the polygon builder. This method should be used
// for input data that may not follow S2 polygon conventions. Note that
// edges are not allowed to cross each other. Also note that as a
// convenience, edges where v0 == v1 are ignored.
//
// Returns true if an edge was added, and false if an edge was erased
// (due to XORing) or not added (if both endpoints were the same).
bool AddEdge(S2Point const& v0, S2Point const& v1);
// Add all edges in the given loop. If the sign() of the loop is negative
// (i.e. this loop represents a hole), the reverse edges are added instead.
// This implies that "shells" are CCW and "holes" are CW, as required for
// the directed edges convention described above.
//
// This method does not take ownership of the loop.
void AddLoop(S2Loop const* loop);
// Add all loops in the given polygon. Shells and holes are added with
// opposite orientations as described for AddLoop().
//
// This method does not take ownership of the polygon.
void AddPolygon(S2Polygon const* polygon);
// This type is used to return any edges that could not be assembled.
typedef vector<pair<S2Point, S2Point> > EdgeList;
// Assembles the given edges into as many non-crossing loops as possible.
// When there is a choice about how to assemble the loops, then CCW loops
// are preferred. Returns true if all edges were assembled. If
// "unused_edges" is not NULL, it is initialized to the set of edges that
// could not be assembled into loops.
//
// Note that if xor_edges() is false and duplicate edge pairs may be
// present, then undirected_edges() should be specified unless all loops can
// be assembled in a counter-clockwise direction. Otherwise this method may
// not be able to assemble all loops due to its preference for CCW loops.
//
// This method resets the S2PolygonBuilder state so that it can be reused.
bool AssembleLoops(vector<S2Loop*>* loops, EdgeList* unused_edges);
// Like AssembleLoops, but normalizes all the loops so that they enclose
// less than half the sphere, and then assembles the loops into a polygon.
//
// For this method to succeed, there should be no duplicate edges in the
// input. If this is not known to be true, then the "xor_edges" option
// should be set (which is true by default).
//
// Note that S2Polygons cannot represent arbitrary regions on the sphere,
// because of the limitation that no loop encloses more than half of the
// sphere. For example, an S2Polygon cannot represent a 100km wide band
// around the equator. In such cases, this method will return the
// *complement* of the expected region. So for example if all the world's
// coastlines were assembled, the output S2Polygon would represent the land
// area (irrespective of the input edge or loop orientations).
bool AssemblePolygon(S2Polygon* polygon, EdgeList* unused_edges);
// This function is only for debugging. If it is called, then points will
// be transformed by the inverse of the given matrix before being printed as
// lat-lng coordinates in degrees. "m" should be orthonormal.
void set_debug_matrix(Matrix3x3_d const& m);
protected:
// These functions print either a single vertex, all edges from a single
// vertex, or all edges in the builder.
void DumpVertex(S2Point const& v) const;
void DumpEdges(S2Point const& v0) const;
void Dump() const;
private:
// Return true if the given edge exists.
bool HasEdge(S2Point const& v0, S2Point const& v1);
// Erase an edge or an entire loop. The edge/loop must exist.
void EraseEdge(S2Point const& v0, S2Point const& v1);
void EraseLoop(S2Point const* v, int n);
// Assembles and returns a single loop starting with the given edge.
// If a loop cannot be assembled starting from this edge, returns NULL
// and updates "unused_edges".
S2Loop* AssembleLoop(S2Point const& v0, S2Point const& v1,
EdgeList* unused_edges);
// Adds all the given edges to "unused_edges".
void RejectLoop(S2Point const* v, int n, EdgeList* unused_edges);
// Builds a map indicating which vertices need to be moved from their
// current position to a new position, and also returns a spatial index
// containing all of the vertices that do not need to be moved.
class PointIndex;
typedef hash_map<S2Point, S2Point> MergeMap;
void BuildMergeMap(PointIndex* index, MergeMap* merge_map);
// Moves a set of vertices from old to new positions.
void MoveVertices(MergeMap const& merge_map);
// Modifies each edge by splicing in any vertices whose distance to the edge
// is at most (edge_splice_fraction() * vertex_merge_radius()).
void SpliceEdges(PointIndex* index);
S2PolygonBuilderOptions options_;
// This is only used for debugging purposes.
scoped_ptr<Matrix3x3_d> debug_matrix_;
// The current set of edges, grouped by origin. The set of destination
// vertices is a multiset so that the same edge can be present more than
// once. We could have also used a multiset<pair<S2Point, S2Point> >,
// but this representation is a bit more convenient.
typedef multiset<S2Point> VertexSet;
typedef hash_map<S2Point, VertexSet> EdgeSet;
scoped_ptr<EdgeSet> edges_;
// Unique collection of the starting (first) vertex of all edges,
// in the order they are added to edges_.
vector<S2Point> starting_vertices_;
};
inline S2PolygonBuilderOptions S2PolygonBuilderOptions::DIRECTED_XOR() {
return S2PolygonBuilderOptions();
}
inline S2PolygonBuilderOptions S2PolygonBuilderOptions::UNDIRECTED_XOR() {
S2PolygonBuilderOptions options;
options.set_undirected_edges(true);
return options;
}
inline S2PolygonBuilderOptions S2PolygonBuilderOptions::UNDIRECTED_UNION() {
S2PolygonBuilderOptions options;
options.set_undirected_edges(true);
options.set_xor_edges(false);
return options;
}
#endif // UTIL_GEOMETRY_S2POLYGONBUILDER_H__