0
0
mirror of https://github.com/mongodb/mongo.git synced 2024-12-01 01:21:03 +01:00

check spherical earth query bounds SERVER-2980

This commit is contained in:
gregs 2011-05-06 10:22:08 -04:00
parent be124009c2
commit aa13edaae8
3 changed files with 69 additions and 10 deletions

View File

@ -1599,12 +1599,14 @@ namespace mongo {
double approxDistance( const GeoHash& h ) {
double approxDistance = -1;
Point p( _g, h );
switch (_type) {
case GEO_PLAIN:
approxDistance = _near.distance( Point( _g, h ) );
approxDistance = _near.distance( p );
break;
case GEO_SPHERE:
approxDistance = spheredist_deg( _near, Point( _g, h ) );
checkEarthBounds( p );
approxDistance = spheredist_deg( _near, p );
break;
default: assert( false );
}
@ -1636,14 +1638,17 @@ namespace mongo {
double exactDistance = -1;
bool exactWithin = false;
Point p( loc );
// Get the appropriate distance for the type
switch ( _type ) {
case GEO_PLAIN:
exactDistance = _near.distance( Point( loc ) );
exactWithin = _near.distanceWithin( Point( loc ), _maxDistance );
exactDistance = _near.distance( p );
exactWithin = _near.distanceWithin( p, _maxDistance );
break;
case GEO_SPHERE:
exactDistance = spheredist_deg( _near, Point( loc ) );
checkEarthBounds( p );
exactDistance = spheredist_deg( _near, p );
exactWithin = ( exactDistance <= _maxDistance );
break;
default: assert( false );
@ -1723,6 +1728,7 @@ namespace mongo {
_scanDistance = maxDistance + _spec->_error;
}
else if (type == GEO_SPHERE) {
checkEarthBounds( startPt );
// TODO: consider splitting into x and y scan distances
_scanDistance = computeXScanDistance( startPt._y, rad2deg( _maxDistance ) + _spec->_error );
}
@ -1927,6 +1933,7 @@ namespace mongo {
// Same, but compute maxDistance using spherical transform
uassert(13461, "Spherical MaxDistance > PI. Are you sure you are using radians?", _maxDistance < M_PI);
checkEarthBounds( _startPt );
_type = GEO_SPHERE;
_yScanDistance = rad2deg( _maxDistance ) + _g->_error;
@ -1973,10 +1980,13 @@ namespace mongo {
d = _g->distance( _start , h );
error = _g->_error;
break;
case GEO_SPHERE:
d = spheredist_deg( _startPt, Point( _g, h ) );
case GEO_SPHERE: {
Point p( _g, h );
checkEarthBounds( p );
d = spheredist_deg( _startPt, p );
error = _g->_errorSphere;
break;
}
default: assert( false );
}
@ -1991,17 +2001,19 @@ namespace mongo {
GEODEBUG( "Inexact distance : " << d << " vs " << _maxDistance << " from " << ( *i ).toString() << " due to error " << error );
Point p( *i );
// Exact distance checks.
switch (_type) {
case GEO_PLAIN: {
if( _startPt.distanceWithin( Point( *i ), _maxDistance ) ) return true;
if( _startPt.distanceWithin( p, _maxDistance ) ) return true;
break;
}
case GEO_SPHERE:
// Ignore all locations not hashed to the key's hash, since spherical calcs are
// more expensive.
if( _g->_hash( *i ) != h ) break;
if( spheredist_deg( _startPt , Point( *i ) ) <= _maxDistance ) return true;
checkEarthBounds( p );
if( spheredist_deg( _startPt , p ) <= _maxDistance ) return true;
break;
default: assert( false );
}
@ -2373,7 +2385,6 @@ namespace mongo {
if ( cmdObj["spherical"].trueValue() )
type = GEO_SPHERE;
// We're returning exact distances, so don't evaluate lazily.
GeoSearch gs( g , n , numWanted , filter , maxDistance , type );
if ( cmdObj["start"].type() == String) {

View File

@ -454,6 +454,12 @@ namespace mongo {
extern const double EARTH_RADIUS_KM;
extern const double EARTH_RADIUS_MILES;
// Technically lat/long bounds, not really tied to earth radius.
inline void checkEarthBounds( Point p ) {
uassert( 14808, str::stream() << "point " << p.toString() << " must be in earth-like bounds of long : [-180, 180), lat : [-90, 90] ",
p._x >= -180 && p._x < 180 && p._y >= -90 && p._y <= 90 );
}
inline double deg2rad(double deg) { return deg * (M_PI/180); }
inline double rad2deg(double rad) { return rad * (180/M_PI); }

42
jstests/geo_oob_sphere.js Normal file
View File

@ -0,0 +1,42 @@
//
// Ensures spherical queries report invalid latitude values in points and center positions
//
t = db.geooobsphere
t.drop();
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 89 } })
t.insert({ loc : { x : 30, y : 91 } })
t.ensureIndex({ loc : "2d" })
assert.isnull( db.getLastError() )
t.find({ loc : { $nearSphere : [ 30, 91 ], $maxDistance : 0.25 } }).count()
var err = db.getLastError()
assert( err != null )
printjson( err )
t.find({ loc : { $nearSphere : [ 30, 89 ], $maxDistance : 0.25 } }).count()
var err = db.getLastError()
assert( err != null )
printjson( err )
t.find({ loc : { $within : { $centerSphere : [[ -180, -91 ], 0.25] } } }).count()
var err = db.getLastError()
assert( err != null )
printjson( err )
db.runCommand({ geoNear : "geooobsphere", near : [179, -91], maxDistance : 0.25, spherical : true })
var err = db.getLastError()
assert( err != null )
printjson( err )
db.runCommand({ geoNear : "geooobsphere", near : [30, 89], maxDistance : 0.25, spherical : true })
var err = db.getLastError()
assert( err != null )
printjson( err )