Permalink
Browse files

Working $centerSphere

  • Loading branch information...
1 parent 4e234c4 commit 5fd62e8edb11dd8a7954420bad78e498b71cd7c7 @RedBeard0531 RedBeard0531 committed Aug 24, 2010
Showing with 63 additions and 24 deletions.
  1. +14 −9 db/geo/2d.cpp
  2. +49 −15 jstests/geo_center_sphere1.js
View
@@ -1141,7 +1141,7 @@ namespace mongo {
GeoCircleBrowse( const Geo2dType * g , const BSONObj& circle , BSONObj filter = BSONObj() , const string& type="$center")
: GeoBrowse( g , "circle" , filter ){
-
+
uassert( 13060 , "$center needs 2 fields (middle,max distance)" , circle.nFields() == 2 );
BSONObjIterator i(circle);
BSONElement center = i.next();
@@ -1160,20 +1160,24 @@ namespace mongo {
_xScanDistance = _maxDistance;
_yScanDistance = _maxDistance;
} else if (type == "$centerSphere") {
- // current algorithm scans more buckets as you get further from equator
- uassert(13437, "Spherical distance is currently limited to latitudes from 80S to 80N",
- _startPt._y >= -80 && _startPt._y <= 80);
- uassert(13439, "Spherical MaxDistance > PI. Are you sure you are using radians?", _maxDistance < (M_PI*1.05));
+ uassert(13451, "Spherical MaxDistance > PI. Are you sure you are using radians?", _maxDistance < M_PI);
_type = GEO_SPHERE;
- _xScanDistance = rad2deg(_maxDistance) / cos(_startPt._y * (M_PI/180));
_yScanDistance = rad2deg(_maxDistance);
+ // TODO: this overestimates for large _maxDistance far from the equator
+ _xScanDistance = rad2deg(_maxDistance) / min(cos(deg2rad(_startPt._y + _yScanDistance)),
+ cos(deg2rad(_startPt._y - _yScanDistance)));
+
+ uassert(13452, "Spherical distance would require wrapping, which isn't implemented yet",
+ (_startPt._x + _xScanDistance < 180) && (_startPt._x - _xScanDistance > -180) &&
+ (_startPt._y + _yScanDistance < 90) && (_startPt._y - _yScanDistance > -90));
+
GEODEBUGPRINT(_maxDistance);
GEODEBUGPRINT(_xScanDistance);
GEODEBUGPRINT(_yScanDistance);
} else {
- uassert(13438, "invalid $center query type: " + type, false);
+ uassert(13450, "invalid $center query type: " + type, false);
}
ok();
@@ -1184,6 +1188,7 @@ namespace mongo {
}
virtual void fillStack(){
+
if ( _state == START ){
if ( ! BtreeLocation::initial( *_id , _spec , _min , _max ,
_prefix , _found , this ) ){
@@ -1269,7 +1274,7 @@ namespace mongo {
if (fabs(ll._x - _startPt._x) <= _xScanDistance) return true;
if (fabs(ll._y - _startPt._y) <= _yScanDistance) return true;
- GeoHash trHash = _prefix;
+ GeoHash trHash = prefix;
trHash.move( 1 , 1 );
Point tr (_g, trHash);
@@ -1298,7 +1303,7 @@ namespace mongo {
d = spheredist_deg(_startPt, Point(_g, h));
break;
}
-
+
GEODEBUG( "\t " << h << "\t" << d );
return d <= _maxDistance;
}
@@ -2,36 +2,43 @@
t = db.geo_center_sphere1;
t.drop();
+skip = 3 // lower for more rigor, higher for more speed (tested with .5, .678, 1, 2, 3, and 4)
+
searches = [
- // y , x rad
- [ [ 5 , 5 ] , 0.03 ] ,
- [ [ 5 , 5 ] , 0.01 ] ,
- [ [ 5 , 5 ] , 0.05 ] ,
- [ [ 0 , 5 ] , 0.05 ] ,
+ // x , y rad
+ [ [ 5 , 0 ] , 0.05 ] , // ~200 miles
+ [ [ 135 , 0 ] , 0.05 ] ,
+
[ [ 5 , 70 ] , 0.05 ] ,
- [ [ 5 , -70 ] , 0.05 ] ,
[ [ 135 , 70 ] , 0.05 ] ,
- [ [ 135 , -70 ] , 0.05 ] ,
+ [ [ 5 , 85 ] , 0.05 ] ,
+
+ [ [ 20 , 0 ] , 0.25 ] , // ~1000 miles
+ [ [ 20 , -45 ] , 0.25 ] ,
+ [ [ -20 , 60 ] , 0.25 ] ,
+ [ [ -20 , -70 ] , 0.25 ] ,
];
correct = searches.map( function(z){ return []; } );
num = 0;
-for ( x=-179; x<=179; x++ ){
- for ( y=-89; y<=89; y++ ){
+for ( x=-179; x<=179; x += skip ){
+ for ( y=-89; y<=89; y += skip ){
o = { _id : num++ , loc : [ x , y ] }
t.save( o )
for ( i=0; i<searches.length; i++ ){
- if ( Geo.sphereDistance( [ x , y ] , searches[i][0] ) <= searches[i][1] )
+ if ( Geo.sphereDistance( [ x , y ] , searches[i][0] ) <= searches[i][1])
correct[i].push( o );
}
}
+ gc(); // needed with low skip values
}
t.ensureIndex( { loc : "2d" } );
for ( i=0; i<searches.length; i++ ){
- //print( tojson( searches[i] ) + "\t" + correct[i].length )
+ print('------------');
+ print( tojson( searches[i] ) + "\t" + correct[i].length )
q = { loc : { $within : { $centerSphere : searches[i] } } }
//correct[i].forEach( printjson )
@@ -43,10 +50,37 @@ for ( i=0; i<searches.length; i++ ){
//printjson( Array.sort( correct[i].map( function(z){ return z._id; } ) ) )
//printjson( Array.sort( t.find(q).map( function(z){ return z._id; } ) ) )
- //print(correct[i].length);
- assert.eq( correct[i].length , t.find( q ).itcount() , "itcount : " + tojson( searches[i] ) );
- assert.eq( correct[i].length , t.find( q ).count() , "count : " + tojson( searches[i] ) );
- assert.gt( correct[i].length * 2 , t.find(q).explain().nscanned , "nscanned : " + tojson( searches[i] ) )
+ var numExpected = correct[i].length
+ var x = correct[i].map( function(z){ return z._id; } )
+ var y = t.find(q).map( function(z){ return z._id; } )
+
+ missing = [];
+ epsilon = 0.001; // allow tenth of a percent error due to conversions
+ for (var j=0; j<x.length; j++){
+ if (!Array.contains(y, x[j])){
+ missing.push(x[j]);
+ var obj = t.findOne({_id: x[j]});
+ var dist = Geo.sphereDistance(searches[i][0], obj.loc);
+ print("missing: " + tojson(obj) + " " + dist)
+ if ((Math.abs(dist - searches[i][1]) / dist) < epsilon)
+ numExpected -= 1;
+ }
+ }
+ for (var j=0; j<y.length; j++){
+ if (!Array.contains(x, y[j])){
+ missing.push(y[j]);
+ var obj = t.findOne({_id: y[j]});
+ var dist = Geo.sphereDistance(searches[i][0], obj.loc);
+ print("extra: " + tojson(obj) + " " + dist)
+ if ((Math.abs(dist - searches[i][1]) / dist) < epsilon)
+ numExpected += 1;
+ }
+ }
+
+
+ assert.eq( numExpected , t.find( q ).itcount() , "itcount : " + tojson( searches[i] ) );
+ assert.eq( numExpected , t.find( q ).count() , "count : " + tojson( searches[i] ) );
+ assert.gt( numExpected * 2 , t.find(q).explain().nscanned , "nscanned : " + tojson( searches[i] ) )
}

0 comments on commit 5fd62e8

Please sign in to comment.