Can I use other coordinate types to create a 2d index in MongoDB? - mongodb

I am building a MongoDB database of property data in the Netherlands. I want to be able to perform geospatial queries on my dataset. Each document does not have longitude and latitude coordinates, but something called "Rijksdriehoeks" coordinates. These Rijksdriehoeks coordinates do not conform to MongoDB's requirements for 2d coordinate pairs, in that they can exceed values of 600.000 (this system uses meters for units). This is a problem, because creating a 2d index requires all coordinate pairs to use longitude and latitude and thus to have values between [-180, 180] and [-90, 90], respectively.
I need to create a geo index for this dataset to reduce query time to acceptable levels, but creating a "legacy" 2d index will give this error because of the issue described above:
> db.Bag3DMembers.createIndex( { "properties.bbox" : "2d" })
{
"ok" : 0,
"errmsg" : "point not in interval of [ -180, 180 ] :: caused by :: { _id: ObjectId('5dfa7596cc0b7324e5331509'), type: \"Feature\", properties: { identificatie: \"0003100000117485\", aanduidingrecordinactief: false, aanduidingrecordcorrectie: 0, officieel: false, inonderzoek: false, documentnummer: \"FB 2010/PANDEN001\", documentdatum: \"2010-07-20\", bouwjaar: \"1991-01-01\", begindatumtijdvakgeldigheid: null, einddatumtijdvakgeldigheid: null, gemeentecode: \"0003\", ground-000: -0.43, ground-010: -0.34, ground-020: -0.31, ground-030: -0.3, ground-040: -0.27, ground-050: -0.25, roof-025: 10.58, rmse-025: 1.5, roof-050: 13.43, rmse-050: 1.28, roof-075: 13.46, rmse-075: 1.28, roof-090: 13.52, rmse-090: 1.27, roof-095: 13.87, rmse-095: 1.27, roof-099: 14.35, rmse-099: 1.27, roof_flat: false, nr_ground_pts: 220, nr_roof_pts: 6816, ahn_file_date: null, ahn_version: 2, height_valid: true, tile_id: \"07fz1\", gid: \"1\", bbox: [ [ 254052.046875, 593486.3125 ], [ 254076.625, 593504.6875 ] ] }, geometry: { type: \"Polygon\", coordinates: [ [ [ 254059.737, 593504.637, 0.0 ], [ 254059.227, 593500.0, 0.0 ], [ 254059.216, 593499.899, 0.0 ], [ 254058.242, 593500.0, 0.0 ], [ 254057.914, 593500.034, 0.0 ], [ 254057.893, 593500.0, 0.0 ], [ 254057.807, 593499.863, 0.0 ], [ 254052.074, 593490.692, 0.0 ], [ 254052.182, 593490.639, 0.0 ], [ 254058.138, 593490.002, 0.0 ], [ 254057.932, 593488.165, 0.0 ], [ 254074.487, 593486.4129999999, 0.0 ], [ 254075.981, 593500.0, 0.0 ], [ 254076.265, 593502.583, 0.0 ], [ 254076.561, 593502.55, 0.0 ], [ 254076.594, 593502.847, 0.0 ], [ 254059.737, 593504.637, 0.0 ] ] ] } }",
"code" : 13027,
"codeName" : "Location13027"
}
Is there any way I can persuade MongoDB to accept my "Rijksdriehoeks" coordinates or will I need to convert them to longitude and latitude values?

Check documentation Create a 2d Index:
On 2d indexes you can change the location range.
You can build a 2d geospatial index with a location range other than
the default. Use the min and max options when creating the index.
Would be
db.Bag3DMembers.createIndex(
{ "properties.bbox" : "2d" },
{ min: -10000, max: 630000, bits: 24 }
)

Related

mongodb 2dsphere index fails on MultiPolygon (malformed geometry)

I am struggling to undestand why the following indexing fails:
> db.mycollection.ensureIndex({geometry:"2dsphere"})
{
"err" : "Can't extract geo keys from object, malformed geometry?:{ type: \"MultiPolygon\", coordinates: [ [ [ [ -0.097262623621224, 51.52157883707353 ], [ -0.096489562220012, 51.52027612781475 ], [ -0.095242927823793, 51.52050373756695 ], [ -0.094744446266, 51.5205960952269 ], [ -0.094366848238175, 51.5206711083638 ], [ -0.094478602055745, 51.51976097300852 ], [ -0.095268219478419, 51.51892749907517 ], [ -0.095057671259463, 51.51823866169356 ], [ -0.09590752332937, 51.51675447483272 ], [ -0.094917254103398, 51.51658818255699 ], [ -0.095455170681833, 51.51544167621788 ], [ -0.09577124973446099, 51.51496874589772 ], [ -0.096562908994151, 51.5148429136635 ], [ -0.097957187488107, 51.51553859434178 ], [ -0.097828201440347, 51.51660172221631 ], [ -0.09972288755426, 51.5167693068687 ], [ -0.099643129935966, 51.51745174289048 ], [ -0.099140106492407, 51.51776283078856 ], [ -0.098174109996819, 51.51789906140922 ], [ -0.098017592779996, 51.5175476311896 ], [ -0.097562458108715, 51.51768194540703 ], [ -0.097737151858934, 51.519666041172 ], [ -0.0978402555155, 51.51992164186168 ], [ -0.098548930349501, 51.51973516284531 ], [ -0.098498300867191, 51.52053989289254 ], [ -0.09761991242012499, 51.52069031743765 ], [ -0.097262623621224, 51.52157883707353 ] ] ] ] }",
"code" : 16572,
"n" : 0,
"connectionId" : 61,
"ok" : 1
}
I checked all the related question on SO but couldn't figure out the problem. They all pointed out to the lon,lat order and the format of coordinates. I am using Mongodb 2.4.
one possible explanation can be this but I'm not sure
MongoDB version 2.6 still fails on 2dsphere geoindexing citing inability to extract geokeys and possible malformed geometry
The Multipolygon geometry type is not supported in MongoDB 2.4 (see Geospatial enhancements in MongoDB 2.6).
Please note that as of this writing, MongoDB 2.4 is not a supported version anymore. If this is for a new project, I would recommend using the latest version (currently MongoDB 3.6.2) to ensure that you are not affected by any historical issues that were fixed in newer versions.

Search points located within given distance from a path in mongoDB

Below is the biz document, in it address.location is where I saved the coordinates.
{
"_id" : ObjectId("59944c26085e1140d4575393"),
"category" : "place",
"address" : {
"streetAddress" : "19210 Clay Road",
"city" : "Katy",
"zipcode" : "77449",
"state" : "Texas",
"country" : "United States",
"countryCode" : "US",
"locality" : "Katy, TX",
"location" : {
"type" : "Point",
"coordinates" : [
-95.7043599,
29.832139
]
},
"fomattedAddress" : "19210 Clay Rd, Katy, TX 77449"
}
}
With the below logic I calculated the polygon
Coordinate[] coordinates = new Coordinate[] {new Coordinate(-84.529793, 33.961886), new Coordinate( -84.521698, 33.952987),
new Coordinate(-84.517242, 33.944534), new Coordinate(-84.513290, 33.937264), new Coordinate( -84.498865, 33.915202), new Coordinate(-84.529793, 33.961886)};
GeometryFactory geometryFactory = new GeometryFactory();
LinearRing linear = new GeometryFactory().createLinearRing(coordinates);
Polygon polygon = new Polygon(linear, null, geometryFactory);
Coordinate[] polyCoordinates = polygon.getCoordinates();
With the below mongo query I am trying to get the businesses inside the polygon, but I get no results, am i doing somthing wrong please guide me
db.business.find(
{
"address.location.coordinates": {
$geoIntersects: {
$geometry: {
type: "Polygon" ,
coordinates: [
[ [ -84.529793, 33.961886 ], [ -84.521698, 33.952987 ], [ -84.517242, 33.944534 ], [-84.51329, 33.937264] , [ -84.498865, 33.915202 ], [-84.529793, 33.961886] ]
]
}
}
}
}
)
To find all points within certain distance along the route you need to build a poly around the route.
On the image below I assumed the whole route (green) is a straight line for brevity and clarity. Your query is an $or of 3 shapes - blue start and end point circles with radius R, and a red poly that joins both:
Following javascript code https://jsfiddle.net/961kcxr3/ calculates coordinates for a segment using basic flat geometry. This is what you need to do on application level using your language of choice:
const routeCoords [ [ -84.529793, 33.961886 ], [ -84.521698, 33.952987 ], [ -84.517242, 33.944534 ], [-84.51329, 33.937264] , [ -84.498865, 33.915202 ] ] ];
const R = 1000; // 1 km - radius to search within, adjust to your needs
const M = 1/98000; // radius to radians multiplier
const A = routeCoords[0];
const B = routeCoords[4];
const a = Math.atan2(B[0] - A[0], B[1] - A[1]) + Math.PI / 2;
var polyCoords = [
[
A[0] - Math.sin(a) * R * M,
A[1] - Math.cos(a) * R * M
], [
B[0] - Math.sin(a) * R * M,
B[1] - Math.cos(a) * R * M
], [
B[0] + Math.sin(a) * R * M,
B[1] + Math.cos(a) * R * M
], [
A[0] + Math.sin(a) * R * M,
A[1] + Math.cos(a) * R * M
]
];
Which gives you coordinates of the poly:
[
[-84.516182, 33.970903],
[-84.485254, 33.924219],
[-84.512476, 33.906185],
[-84.543404, 33.952869]
]
And the resulting query:
db.business.find(
{ $or: [
{ "address.location.coordinates": {
$geoWithin: {
$geometry: {
type: "Polygon" ,
coordinates: [ [
[-84.516182, 33.970903],
[-84.485254, 33.924219],
[-84.512476, 33.906185],
[-84.543404, 33.952869],
[-84.516182, 33.970903] // the first point to close the poly
] ]
}
},
} },
{ "address.location.coordinates": {
$geoWithin: {
$centerSphere: [ [ -84.529793, 33.961886 ], 1 / 6378.1 ]
}, // 1 km radius, adjust to your needs
} },
{ "address.location.coordinates": {
$geoWithin: {
$centerSphere: [ [ -84.498865, 33.915202 ], 1 / 6378.1 ]
}, // 1 km radius, adjust to your needs
} }
] }
)
The real-life code should calculate polygons and circles for each segment. It can result with quite large query, so be reasonable balancing between accuracy and speed.
Some cases for optimisation:
if length of a segment is significantly less than the radius R you can skip the poly and use 2 or 1 circle for the segment.
if angle a is the same for 2 consecutive segments, you can join the ends and treat them as a single segment the same way I joined 4 segments in the example above

Can't extract geo keys' even though the GeoJSON is valid

I have a collection in MongoDB with a 2dsphere index. The object I want to save looks like this:
{
"type" : "Polygon",
"coordinates" : [
[
[
5.052617929724351,
52.64653192570052
],
[
5.051738165167465,
52.64765805672784
],
[
5.054162882116928,
52.64831549553909
],
[
5.054592035559312,
52.64780777138566
],
[
5.055364511755601,
52.64790541110375
],
[
5.056094072607651,
52.64688343792051
],
[
5.054237983969346,
52.64661654927096
],
[
5.052617929724351,
52.64653192570052
]
]
]
}
According to http://geojsonlint.com/ this is perfectly valid GeoJSON. However MongoDB says it can't extract the geo keys because the GeoJSON might be malformed.
Can anyone help me out and spot the mistake?
This is the MongoDB error I get:
insertDocument :: caused by :: 16755 Can't extract geo keys from object, malformed geometry?
The problem is that you are not providing the name of the top level object that the GeoJSON would be assigned to.
You must have created the "2dsphere" index on the "coordinates" field. Instead you want to create it on the field that this entire GeoJSON value will be assigned to.
db.geo.createIndex({"location":"2dsphere"})
db.geo.insert({"location" : {
"type" : "Polygon",
"coordinates" : [
[ <list of your-coord-pairs> ]
]
}})
WriteResult({ "nInserted" : 1 })

Different semantics in $not $geoWithin with Polygon geometries between MongoDB 2.4 and 2.6

I have run the following experiment, comparing how MongoDB 2.4 and MongoDB 2.6 behave regarding the $geoWithin selector combined with $not with Polygons (i.e. "outside polygon" query). I'm including the particular versions (three numbers), alghouth I guess it would happend the same with other minor versions of 2.4 and 2.6.
Two documents (A and B) are created in a given collection: A with p field set to coordinates [1, 1] and B without p field. Next, I create a 2dsphere index in p and do a query for the area outside a triangle which vertices are [0, 0], [0, 4]and [4, 0]. Note that A is inside that polygon (so it is not supposed to be got with this query).
With 2.4.9:
db.x.insert({k: "A", p: [1,1]})
db.x.insert({k: "B"})
db.x.ensureIndex({"p": "2dsphere"})
db.x.find({p: { $not: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [ 0, 0 ], [ 0, 4 ], [ 4, 0 ], [ 0, 0 ] ] ] } } } }})
--> no result
Makes sense: A is not returned (as it is inside the polygon) and B is not returned (given that it doesn't have a p field).
Next, testing with 2.6.1 the same script:
db.x.insert({k: "A", p: [1,1]})
db.x.insert({k: "B"})
db.x.ensureIndex({"p": "2dsphere"})
db.x.find({p: { $not: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [ 0, 0 ], [ 0, 4 ], [ 4, 0 ], [ 0, 0 ] ] ] } } } }})
-> result: B
It seems that in 2.6 semantics have changed, so when the 2dsphere-indexed field is not in a given document, that document is considered outside any possible polygon.
Changing semantics between versions is ok as long as some mechanism in the new version allows to configure behaviour in the old way. I thought that mechanism was using { "2dsphereIndexVersion" : 1 } at index creation time (based on what I read here, maybe I misunderstood that information...). However, the result (with 2.6.1 again) is the same:
db.x.insert({k: "A", p: [1,1]})
db.x.insert({k: "B"})
db.x.ensureIndex({"p": "2dsphere"}, { "2dsphereIndexVersion" : 1 })
db.x.find({p: { $not: { $geoWithin: { $geometry: { type: "Polygon", coordinates: [ [ [ 0, 0 ], [ 0, 4 ], [ 4, 0 ], [ 0, 0 ] ] ] } } } }})
-> result B
Thus, is there any way of using MongoDB 2.6 with the same semantics that MongoDB 2.4 in the sense that any document without the 2dsphere-indexed not to be returned in "outside poylgon" queries?
The query result in 2.6 is right - the query result in 2.4 I think I would call incorrect. Technically, your query asks for documents that do not match the $geoWithin condition. The "k" : "B" document does not match the $geoWithin condition, so it should be returned by the query. You can drop results without the p field using $exists:
db.x.find({
"p" : {
"$exists" : true,
"$not" : { "$geoWithin" : {
"$geometry" : {
"type": "Polygon",
"coordinates" : [ [ [ 0, 0 ], [ 0, 4 ], [ 4, 0 ], [ 0, 0 ] ] ]
}
} } }
})
Also note that 1) your $not query isn't actually using the geo index, as you can check with an explain, and 2) when using a 2dsphere index you should store points as GeoJSON
{
"k" : "A",
"p" : {
"type" : "Point",
"coordinates" : [1,1]
}
}
Technically it's required in MongoDB >= 2.6, and the docs say it should be an error not to use GeoJSON, but it seems to work for us.

Why am I getting values outside polygon with geoIntersect

I am using mongodb with polygons stored with geometry in this format
"geometry" : { "type" : "Polygon", "coordinates" : [ [ [ 2.0315, 85.8546219515 ], [ 2.0315, 86.1673780485 ], [ 1.7185, 86.1673780485 ], [ 1.7185, 85.8546219515 ], [ 2.0315, 85.8546219515 ] ] ] }
I am trying to use geoIntersect and query a rectangle polygon using something like this:
find({geometry:{$geoIntersects:{$geometry:{"type": "Polygon", "coordinates":[ [[12,20], [13,20], [13,18], [12,18], [12,20]]]}}}})
When I do small queries like that, the results are fine, but when I expand to really large boxes:
find({geometry:{$geoIntersects:{$geometry:{"type": "Polygon", "coordinates":[ [[-166.2890625,46.55886030311719], [-166.2890625,47.39834920035926], [12.12890625,47.39834920035926], [12.12890625,46.55886030311719], [-166.2890625,46.55886030311719]]]}}}})
then I start getting really odd results like
"geometry" : { "type" : "Polygon", "coordinates" : [ [ [ 2.9684999999999997, 85.54162195149999 ], [ 2.9684999999999997, 85.8543780485 ], [ 2.6555, 85.8543780485 ], [ 2.6555, 85.54162195149999 ], [ 2.9684999999999997, 85.54162195149999 ] ] ] }
I am only returned a few polygons (4 total) from the db that are actually inside my input polygon:
"geometry":{"type":"Polygon","coordinates":[[[12.343499999999999,47.1416219515],[12.343499999999999,47.454378048500004],[12.0305,47.454378048500004],[12.0305,47.1416219515],[12.343499999999999,47.1416219515]]]}}
"geometry":{"type":"Polygon","coordinates":[[[12.343499999999999,46.829621951499995],[12.343499999999999,47.1423780485],[12.0305,47.1423780485],[12.0305,46.829621951499995],[12.343499999999999,46.829621951499995]]]}}
"geometry":{"type":"Polygon","coordinates":[[[-166.0935,46.5166219515],[-166.0935,46.829378048500004],[-166.4065,46.829378048500004],[-166.4065,46.5166219515],[-166.0935,46.5166219515]]]}}
"geometry":{"type":"Polygon","coordinates":[[[-166.0935,46.829621951499995],[-166.0935,47.1423780485],[-166.4065,47.1423780485],[-166.4065,46.829621951499995],[-166.0935,46.829621951499995]]]}}
I shouldn't have any latitudes in the 80's with my input. I don't know where these are coming from. They make up the majority of what is returned. (847/851 results)
I don't think the polygon fits in one hemisphere. From the $geoIntersects docs,
Any geometry specified with GeoJSON to $geoIntersects queries, must fit within a single hemisphere.