19
#include "mongo/pch.h"
20
#include "mongo/db/jsobj.h"
26
explicit Point( const BSONElement& e ) {
27
BSONObjIterator i(e.Obj());
28
_x = i.next().number();
29
_y = i.next().number();
32
explicit Point( const BSONObj& o ) {
34
_x = i.next().number();
35
_y = i.next().number();
38
Point( double x , double y )
42
Point() : _x(0),_y(0) {
45
double distance( const Point& p ) const {
49
// Avoid numerical error if possible...
50
if( a == 0 ) return abs( _y - p._y );
51
if( b == 0 ) return abs( _x - p._x );
53
return sqrt( ( a * a ) + ( b * b ) );
57
* Distance method that compares x or y coords when other direction is zero,
58
* avoids numerical error when distances are very close to radius but axis-aligned.
60
* An example of the problem is:
61
* (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
63
* 51.9999 + 0.0001 <= 52.0
65
* This avoids some (but not all!) suprising results in $center queries where points are
66
* ( radius + center.x, center.y ) or vice-versa.
68
bool distanceWithin( const Point& p, double radius ) const {
74
// Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
75
// calculated imprecisely. We need to force the compiler to always evaluate it correctly,
76
// hence the weirdness.
78
// On some 32-bit linux machines, removing the volatile keyword or calculating the sum inline
79
// will make certain geo tests fail. Of course this check will force volatile for all 32-bit systems,
80
// not just affected systems.
81
if( sizeof(void*) <= 4 ){
82
volatile double sum = _y > p._y ? p._y + radius : _y + radius;
83
return _y > p._y ? sum >= _y : sum >= p._y;
86
// Original math, correct for most systems
87
return _y > p._y ? p._y + radius >= _y : _y + radius >= p._y;
91
if( sizeof(void*) <= 4 ){
92
volatile double sum = _x > p._x ? p._x + radius : _x + radius;
93
return _x > p._x ? sum >= _x : sum >= p._x;
96
return _x > p._x ? p._x + radius >= _x : _x + radius >= p._x;
100
return sqrt( ( a * a ) + ( b * b ) ) <= radius;
103
string toString() const {
105
buf << "(" << _x << "," << _y << ")";
24
double distance(const Point& p1, const Point &p2);
25
bool distanceWithin(const Point &p1, const Point &p2, double radius);
26
void checkEarthBounds(const Point &p);
27
double spheredist_rad(const Point& p1, const Point& p2);
28
double spheredist_deg(const Point& p1, const Point& p2);
32
Point(double x, double y);
33
explicit Point(const BSONElement& e);
34
explicit Point(const BSONObj& o);
35
string toString() const;
43
Circle(double radius, Point center);
117
Box( double x , double y , double size )
119
_max( x + size , y + size ) {
122
Box( Point min , Point max )
123
: _min( min ) , _max( max ) {
128
BSONArray toBSON() const {
129
return BSON_ARRAY( BSON_ARRAY( _min._x << _min._y ) << BSON_ARRAY( _max._x << _max._y ) );
132
string toString() const {
134
buf << _min.toString() << " -->> " << _max.toString();
138
bool between( double min , double max , double val , double fudge=0) const {
139
return val + fudge >= min && val <= max + fudge;
142
bool onBoundary( double bound, double val, double fudge = 0 ) const {
143
return ( val >= bound - fudge && val <= bound + fudge );
146
bool mid( double amin , double amax , double bmin , double bmax , bool min , double& res ) const {
147
verify( amin <= amax );
148
verify( bmin <= bmax );
153
res = min ? bmin : amax;
158
res = min ? amin : bmax;
162
double intersects( const Box& other ) const {
167
if ( mid( _min._x , _max._x , other._min._x , other._max._x , true , boundMin._x ) == false ||
168
mid( _min._x , _max._x , other._min._x , other._max._x , false , boundMax._x ) == false ||
169
mid( _min._y , _max._y , other._min._y , other._max._y , true , boundMin._y ) == false ||
170
mid( _min._y , _max._y , other._min._y , other._max._y , false , boundMax._y ) == false )
173
Box intersection( boundMin , boundMax );
175
return intersection.area() / area();
178
double area() const {
179
return ( _max._x - _min._x ) * ( _max._y - _min._y );
182
double maxDim() const {
183
return max( _max._x - _min._x, _max._y - _min._y );
189
Point center() const {
190
return Point( ( _min._x + _max._x ) / 2 ,
191
( _min._y + _max._y ) / 2 );
194
void truncate(double min, double max) {
195
if( _min._x < min ) _min._x = min;
196
if( _min._y < min ) _min._y = min;
197
if( _max._x > max ) _max._x = max;
198
if( _max._y > max ) _max._y = max;
201
void fudge(double error) {
208
bool onBoundary( Point p, double fudge = 0 ) {
209
return onBoundary( _min._x, p._x, fudge ) ||
210
onBoundary( _max._x, p._x, fudge ) ||
211
onBoundary( _min._y, p._y, fudge ) ||
212
onBoundary( _max._y, p._y, fudge );
215
bool inside( Point p , double fudge = 0 ) const {
216
bool res = inside( p._x , p._y , fudge );
217
//cout << "is : " << p.toString() << " in " << toString() << " = " << res << endl;
221
bool inside( double x , double y , double fudge = 0 ) const {
223
between( _min._x , _max._x , x , fudge ) &&
224
between( _min._y , _max._y , y , fudge );
227
bool contains(const Box& other, double fudge=0) {
228
return inside(other._min, fudge) && inside(other._max, fudge);
52
Box(double x, double y, double size);
53
Box(Point min, Point max);
55
BSONArray toBSON() const;
56
string toString() const;
58
bool between(double min, double max, double val, double fudge = 0) const;
59
bool onBoundary(double bound, double val, double fudge = 0) const;
60
bool mid(double amin, double amax, double bmin, double bmax, bool min, double* res) const;
62
double intersects(const Box& other) const;
64
double maxDim() const;
66
void truncate(double min, double max);
67
void fudge(double error);
68
bool onBoundary(Point p, double fudge = 0);
69
bool inside(Point p, double fudge = 0) const;
70
bool inside(double x, double y, double fudge = 0) const;
71
bool contains(const Box& other, double fudge = 0);
239
Polygon( void ) : _centroidCalculated( false ) {}
241
Polygon( vector<Point> points ) : _centroidCalculated( false ),
242
_points( points ) { }
244
void add( Point p ) {
245
_centroidCalculated = false;
246
_points.push_back( p );
249
int size( void ) const {
250
return _points.size();
254
* Determine if the point supplied is contained by the current polygon.
256
* The algorithm uses a ray casting method.
79
Polygon(vector<Point> points);
84
bool contains(const Point& p) const;
88
* -1 if no intersection
89
* 0 if maybe an intersection (using fudge)
90
* 1 if there is an intersection
258
bool contains( const Point& p ) const {
259
return contains( p, 0 ) > 0;
262
int contains( const Point &p, double fudge ) const {
264
Box fudgeBox( Point( p._x - fudge, p._y - fudge ), Point( p._x + fudge, p._y + fudge ) );
267
Point p1 = _points[0];
268
for ( int i = 1; i <= size(); i++ ) {
269
Point p2 = _points[i % size()];
271
GEODEBUG( "Doing intersection check of " << fudgeBox.toString() << " with seg " << p1.toString() << " to " << p2.toString() );
273
// We need to check whether or not this segment intersects our error box
275
// Points not too far below box
276
fudgeBox._min._y <= std::max( p1._y, p2._y ) &&
277
// Points not too far above box
278
fudgeBox._max._y >= std::min( p1._y, p2._y ) &&
279
// Points not too far to left of box
280
fudgeBox._min._x <= std::max( p1._x, p2._x ) &&
281
// Points not too far to right of box
282
fudgeBox._max._x >= std::min( p1._x, p2._x ) ) {
284
GEODEBUG( "Doing detailed check" );
286
// If our box contains one or more of these points, we need to do an exact check.
287
if( fudgeBox.inside(p1) ) {
288
GEODEBUG( "Point 1 inside" );
291
if( fudgeBox.inside(p2) ) {
292
GEODEBUG( "Point 2 inside" );
296
// Do intersection check for vertical sides
297
if ( p1._y != p2._y ) {
299
double invSlope = ( p2._x - p1._x ) / ( p2._y - p1._y );
301
double xintersT = ( fudgeBox._max._y - p1._y ) * invSlope + p1._x;
302
if( fudgeBox._min._x <= xintersT && fudgeBox._max._x >= xintersT ) {
303
GEODEBUG( "Top intersection @ " << xintersT );
307
double xintersB = ( fudgeBox._min._y - p1._y ) * invSlope + p1._x;
308
if( fudgeBox._min._x <= xintersB && fudgeBox._max._x >= xintersB ) {
309
GEODEBUG( "Bottom intersection @ " << xintersB );
315
// Do intersection check for horizontal sides
316
if( p1._x != p2._x ) {
318
double slope = ( p2._y - p1._y ) / ( p2._x - p1._x );
320
double yintersR = ( p1._x - fudgeBox._max._x ) * slope + p1._y;
321
if( fudgeBox._min._y <= yintersR && fudgeBox._max._y >= yintersR ) {
322
GEODEBUG( "Right intersection @ " << yintersR );
326
double yintersL = ( p1._x - fudgeBox._min._x ) * slope + p1._y;
327
if( fudgeBox._min._y <= yintersL && fudgeBox._max._y >= yintersL ) {
328
GEODEBUG( "Left intersection @ " << yintersL );
335
else if( fudge == 0 ){
337
// If this is an exact vertex, we won't intersect, so check this
338
if( p._y == p1._y && p._x == p1._x ) return 1;
339
else if( p._y == p2._y && p._x == p2._x ) return 1;
341
// If this is a horizontal line we won't intersect, so check this
342
if( p1._y == p2._y && p._y == p1._y ){
343
// Check that the x-coord lies in the line
344
if( p._x >= std::min( p1._x, p2._x ) && p._x <= std::max( p1._x, p2._x ) ) return 1;
349
// Normal intersection test.
350
// TODO: Invert these for clearer logic?
351
if ( p._y > std::min( p1._y, p2._y ) ) {
352
if ( p._y <= std::max( p1._y, p2._y ) ) {
353
if ( p._x <= std::max( p1._x, p2._x ) ) {
354
if ( p1._y != p2._y ) {
355
double xinters = (p._y-p1._y)*(p2._x-p1._x)/(p2._y-p1._y)+p1._x;
356
// Special case of point on vertical line
357
if ( p1._x == p2._x && p._x == p1._x ){
359
// Need special case for the vertical edges, for example:
364
// if we count exact as intersection, then 1 is in but 2 is out
365
// if we count exact as no-int then 1 is out but 2 is in.
369
else if( p1._x == p2._x || p._x <= xinters ) {
380
if ( counter % 2 == 0 ) {
92
int contains(const Point &p, double fudge) const;
389
95
* Calculate the centroid, or center of mass of the polygon object.
391
Point centroid( void ) {
393
/* Centroid is cached, it won't change betwen points */
394
if ( _centroidCalculated ) {
399
double signedArea = 0.0;
400
double area = 0.0; // Partial signed area
402
/// For all vertices except last
404
for ( i = 0; i < size() - 1; ++i ) {
405
area = _points[i]._x * _points[i+1]._y - _points[i+1]._x * _points[i]._y ;
407
cent._x += ( _points[i]._x + _points[i+1]._x ) * area;
408
cent._y += ( _points[i]._y + _points[i+1]._y ) * area;
412
area = _points[i]._x * _points[0]._y - _points[0]._x * _points[i]._y;
413
cent._x += ( _points[i]._x + _points[0]._x ) * area;
414
cent._y += ( _points[i]._y + _points[0]._y ) * area;
417
cent._x /= ( 6 * signedArea );
418
cent._y /= ( 6 * signedArea );
420
_centroidCalculated = true;
430
_bounds._max = _points[0];
431
_bounds._min = _points[0];
433
for ( int i = 1; i < size(); i++ ) {
435
_bounds._max._x = max( _bounds._max._x, _points[i]._x );
436
_bounds._max._y = max( _bounds._max._y, _points[i]._y );
437
_bounds._min._x = min( _bounds._min._x, _points[i]._x );
438
_bounds._min._y = min( _bounds._min._y, _points[i]._y );
448
100
bool _centroidCalculated;
103
bool _boundsCalculated;
453
104
vector<Point> _points;
455
106
} // namespace mongo