[SERVER-27968] $geoWithin with $centerSphere does not find GeoJSON documents other than Points Created: 09/Feb/17  Updated: 12/Oct/17  Resolved: 10/Oct/17

Status: Closed
Project: Core Server
Component/s: Geo, Querying
Affects Version/s: 3.4.0
Fix Version/s: 3.6.0-rc0

Type: Bug Priority: Major - P3
Reporter: Derick Rethans Assignee: Wan Bachtiar
Resolution: Done Votes: 2
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Attachments: File geotest.js    
Issue Links:
Backports
Related
related to SERVER-30390 Add support for $geoIntersects with g... Backlog
is related to COMPASS-237 Extend query building capabilities of... Closed
Backwards Compatibility: Fully Compatible
Operating System: ALL
Backport Requested:
v3.4
Sprint: Query 2017-11-13
Participants:

 Description   

Compare:

> db.poiConcat.find( { ty: 1, l: { $geoWithin: { $centerSphere: [ [ -80.1745962, 26.148911 ], 50000000 ] } } } ).limit(1);
{ "_id" : "n99364450", "ty" : NumberLong(1), "l" : { "type" : "Point", "coordinates" : [ -80.3810378, 25.5883277 ] }, "ts" : [ "amenity=parking" ], "m" : { "v" : NumberLong(4), "cs" : NumberLong(18737061), "uid" : NumberLong(1795637), "ts" : NumberLong(1383685090) } }

> db.poiConcat.find( { ty: 2, l: { $geoWithin: { $centerSphere: [ [ -80.1745962, 26.148911 ], 500000 ] } } } ).limit(1);

With a distance of 500km, it should certainly find documents. The range below (1 degree) is much less (about 100km):

> > db.poiConcat.find( { ty: 2, l: { $geoWithin: { $geometry : { type: 'Polygon', coordinates : [ [ [ -80.1745962, 26.148911 ], [ -79, 26 ], [ -79, 25 ], [ -80, 25 ], [ -80.1745962, 26.148911 ] ] ] } } } } ).limit(1);
 
{ "_id" : "w414476448", "ty" : NumberLong(2), "l" : { "type" : "Polygon", "coordinates" : [ [ [ -80.1091137, 26.0908518 ], [ -80.1090739, 26.0907767 ], [ -80.1092148, 26.0907164 ], [ -80.1092547, 26.0907915 ], [ -80.1091137, 26.0908518 ] ] ] }, "ts" : [ "name=Jetty Pavilion", "amenity=shelter", "building=yes" ], "m" : { "v" : NumberLong(1), "cs" : NumberLong(38985825), "uid" : NumberLong(684651), "ts" : NumberLong(1461953735) } }



 Comments   
Comment by Githook User [ 10/Oct/17 ]

Author:

{'email': 'tess.avitabile@mongodb.com', 'name': 'Tess Avitabile', 'username': 'tessavitabile'}

Message: SERVER-27968 $geoWithin with $centerSphere should return LineString and Polygon geometries
Branch: master
https://github.com/mongodb/mongo/commit/f864f1ee2cea8bcd9ee4a34776ad069ab7208ff4

Comment by Githook User [ 10/Oct/17 ]

Author:

{'email': 'ramon@mongodb.com', 'name': 'Ramon Fernandez'}

Message: Revert "SERVER-27968 $geoWithin with $centerSphere should return LineString and Polygon geometries"

This reverts commit f24f7830d01193db30102b8381eeaf4c011d0ea9.
Branch: master
https://github.com/mongodb/mongo/commit/27214e46685c094b8a025214b02daf3716514fc7

Comment by Githook User [ 10/Oct/17 ]

Author:

{'email': 'sindbach@gmail.com', 'name': 'Wan Bachtiar', 'username': 'sindbach'}

Message: SERVER-27968 $geoWithin with $centerSphere should return LineString and Polygon geometries

Closes #1184

Signed-off-by: Tess Avitabile <tess.avitabile@mongodb.com>
Branch: master
https://github.com/mongodb/mongo/commit/f24f7830d01193db30102b8381eeaf4c011d0ea9

Comment by Wan Bachtiar [ 06/Oct/17 ]

PR: https://github.com/mongodb/mongo/pull/1184

Comment by Tess Avitabile (Inactive) [ 14/Mar/17 ]

Inspecting the code, it looks to me like this is intentional. For $geoWithin queries, we only expect to match "LineString" and "Polygon" GeoJSON documents when the query geometry is a "Polygon" or "MultiPolygon":

Matching for points is done here:

bool GeometryContainer::contains(const S2Cell& otherCell, const S2Point& otherPoint) const {
    if (NULL != _polygon && (NULL != _polygon->s2Polygon)) {
        return containsPoint(*_polygon->s2Polygon, otherCell, otherPoint);
    }
    ...
    // This case checks if the point is contained within the $centerSphere.
    if (NULL != _cap && (_cap->crs == SPHERE)) {
        return _cap->cap.MayIntersect(otherCell);
    }
    ...
    return false;
}

Matching for lines is done here. There is no case for $centerSphere, so we reach the end of the function:

bool GeometryContainer::contains(const S2Polyline& otherLine) const {
    if (NULL != _polygon && NULL != _polygon->s2Polygon) {
        return containsLine(*_polygon->s2Polygon, otherLine);
    }
    ...
    return false;
}

Matching for polygons is done here. There is no case for $centerSphere, so we reach the end of the function:

bool GeometryContainer::contains(const S2Polygon& otherPolygon) const {
    if (NULL != _polygon && NULL != _polygon->s2Polygon) {
        return containsPolygon(*_polygon->s2Polygon, otherPolygon);
    }
    ...
    return false;
}

We could consider implementing this feature and/or documenting the current behavior. david.storch, do you have thoughts on how we should proceed?

Comment by Wan Bachtiar [ 17/Feb/17 ]

A couple of things to point out based on the above example documents:

A) The last entry for Polygon is not a valid GeoJSON polygon. A GeoJSON single ring polygon need to be enclosed in another array. i.e.

db.c.insert( { type: "Polygon", coordinates: [ [ [1,1], [2,2], [2, 1], [1,1] ] ] } )

https://docs.mongodb.com/manual/reference/geojson/#polygons-with-a-single-ring

B) The 'GeoJSON' documents are on top level and querying by specifying field coordinates is being treated it as if they were an array of legacy coordinates. You can observe this, by correcting the polygon document as per point A, the query below would only return 'Point' and 'LineString'. (Because the GeoJSON polygon, being an array of array of pair coordinates no longer satisfies the legacy coordinate format)

db.c.find( { coordinates: { $geoWithin: { $centerSphere: [ [1, 1], 1/3963.2 ] } } } )

Let's change the document structure, by taking a similar structure example of GeoJSON documents as per documentation: https://docs.mongodb.com/manual/tutorial/geospatial-tutorial/#exploring-the-data

db.c.insert( { name:"ImAPoint", location:{type: "Point", coordinates: [1, 1]}});
db.c.insert( {name:"ImALineString", location:{ type: "LineString", coordinates: [ [1,1], [2,2] ] }} );
db.c.insert( {name:"ImAPolygon", location:{ type: "Polygon", coordinates: [[ [1,1], [2,2], [2, 1], [1,1] ]] }} );

The below query :

db.c.find( { location: { $geoWithin: { $centerSphere: [ [1, 1], 1/3963.2 ] } } } );

Would only return 'Point', where it should have returned all three valid GeoJSON documents.

I've also attached a jstest file for testing convenience.

Comment by Kyle Suarez [ 16/Feb/17 ]

derick, MongoDB does indeed support finding all types of GeoJSON with $centerSphere, according to the documentation. After talking to siyuan.zhou, I think there's something weird about your query: the second argument to $centerSphere is the radius specified in radians. If you want to find things within a distance of 500 kilometers, you should specify the radius as 500/6378.1, since the equatorial radius of the Earth is approximately 3,963.2 miles or 6,378.1 kilometers (cf. this docs page).

I am not good at all at spherical geometry, though, and it might still be that there's a bug. However, a sanity check locally confirms that $centerSphere can indeed find all sorts of GeoJSON:

> db.c.insert( { type: "Point", coordinates: [ 1, 1 ] } )
WriteResult({ "nInserted" : 1 })
> db.c.insert( { type: "LineString", coordinates: [ [1,1], [2,2] ] } )
WriteResult({ "nInserted" : 1 })
> db.c.insert( { type: "Polygon", coordinates: [ [1,1], [2,2], [2, 1], [1,1] ] } )
WriteResult({ "nInserted" : 1 })
 
> db.c.find( { coordinates: { $geoWithin: { $centerSphere: [ [1, 1], 1/3963.2 ] } } } )
{ "_id" : ObjectId("58a6237d12eca20248bb3f1f"), "type" : "Point", "coordinates" : [ 1, 1 ] }
{ "_id" : ObjectId("58a6239512eca20248bb3f20"), "type" : "LineString", "coordinates" : [ [ 1, 1 ], [ 2, 2 ] ] }
{ "_id" : ObjectId("58a623c012eca20248bb3f21"), "type" : "Polygon", "coordinates" : [ [ 1, 1 ], [ 2, 2 ], [ 2, 1 ], [ 1, 1 ] ] }

Comment by Kyle Suarez [ 16/Feb/17 ]

siyuan.zhou, is this expected behavior?

Generated at Thu Feb 08 04:16:45 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.