// Test 2dsphere near search, called via find and geoNear.
t = db.geo_s2near
t.drop();

// Make sure that geoNear gives us back loc
goldenPoint = {type: "Point", coordinates: [ 31.0, 41.0]}
t.insert({geo: goldenPoint})
t.ensureIndex({ geo : "2dsphere" })
resNear = db.runCommand({geoNear : t.getName(), near: [30, 40], num: 1, spherical: true, includeLocs: true})
assert.eq(resNear.results[0].loc, goldenPoint)

// FYI:
// One degree of long @ 0 is 111km or so.
// One degree of lat @ 0 is 110km or so.
lat = 0
lng = 0
points = 10
for (var x = -points; x < points; x += 1) {
    for (var y = -points; y < points; y += 1) {
        t.insert({geo : { "type" : "Point", "coordinates" : [lng + x/1000.0, lat + y/1000.0]}})
    }
}

origin = { "type" : "Point", "coordinates": [ lng, lat ] }

t.ensureIndex({ geo : "2dsphere" })

// Near only works when the query is a point.
someline = { "type" : "LineString", "coordinates": [ [ 40, 5], [41, 6]]}
somepoly = { "type" : "Polygon",
             "coordinates" : [ [ [40,5], [40,6], [41,6], [41,5], [40,5]]]}
assert.throws(function() { return t.find({ "geo" : { "$near" : { "$geometry" : someline } } }).count()})
assert.throws(function() { return t.find({ "geo" : { "$near" : { "$geometry" : somepoly } } }).count()})
assert.throws(function() { return db.runCommand({geoNear : t.getName(), near: someline, spherical:true }).results.length})
assert.throws(function() { return db.runCommand({geoNear : t.getName(), near: somepoly, spherical:true }).results.length})

// Do some basic near searches.
res = t.find({ "geo" : { "$near" : { "$geometry" : origin, $maxDistance: 2000} } }).limit(10)
resNear = db.runCommand({geoNear : t.getName(), near: [0,0], num: 10, maxDistance: 2000, spherical: true})
assert.eq(res.itcount(), resNear.results.length, 10)

res = t.find({ "geo" : { "$near" : { "$geometry" : origin } } }).limit(10)
resNear = db.runCommand({geoNear : t.getName(), near: [0,0], num: 10, spherical: true})
assert.eq(res.itcount(), resNear.results.length, 10)

// Find all the points!
res = t.find({ "geo" : { "$near" : { "$geometry" : origin } } }).limit(10000)
resNear = db.runCommand({geoNear : t.getName(), near: [0,0], num: 10000, spherical: true})
assert.eq(resNear.results.length, res.itcount(), (2 * points) * (2 * points))

// longitude goes -180 to 180
// latitude goes -90 to 90
// Let's put in some perverse (polar) data and make sure we get it back.
// Points go long, lat.
t.insert({geo: { "type" : "Point", "coordinates" : [-180, -90]}})
t.insert({geo: { "type" : "Point", "coordinates" : [180, -90]}})
t.insert({geo: { "type" : "Point", "coordinates" : [180, 90]}})
t.insert({geo: { "type" : "Point", "coordinates" : [-180, 90]}})
res = t.find({ "geo" : { "$near" : { "$geometry" : origin } } }).limit(10000)
resNear = db.runCommand({geoNear : t.getName(), near: [0,0], num: 10000, spherical: true})
assert.eq(res.itcount(), resNear.results.length, (2 * points) * (2 * points) + 4)

function testRadAndDegreesOK(distance) {
    // Distance for old style points is radians.
    resRadians = t.find({geo: {$nearSphere: [0,0], $maxDistance: (distance / (6378.1 * 1000))}})
    // Distance for new style points is meters.
    resMeters = t.find({ "geo" : { "$near" : { "$geometry" : origin, $maxDistance: distance} } })
    // And we should get the same # of results no matter what.
    assert.eq(resRadians.itcount(), resMeters.itcount())

    // Also, geoNear should behave the same way.
    resGNMeters = db.runCommand({geoNear : t.getName(), near: origin, maxDistance: distance, spherical: true})
    resGNRadians = db.runCommand({geoNear : t.getName(), near: [0,0], maxDistance: (distance / (6378.1 * 1000)), spherical: true})
    assert.eq(resGNRadians.results.length, resGNMeters.results.length)
    for (var i = 0; i < resGNRadians.length; ++i) {
        // Radius of earth * radians = distance in meters.
        assert.close(resGNRadians.results[i].dis * 6378.1 * 1000, resGNMeters.results[i].dis)
    }
}

testRadAndDegreesOK(1);
testRadAndDegreesOK(10)
testRadAndDegreesOK(50)
testRadAndDegreesOK(10000)
