Reputation: 3747
I've got a collection that looks like this :
[
{
"roadname": "foo",
"data": [
{
"val": 50,
"loc": {
"type": "Point",
"coordinates": [3.197033554, 50.64611712]
}
}
},
{
"val": NULL,
"loc": {
"type": "Point",
"coordinates": [3.197740735, 50.6460058]
}
}
}
]
},
{
"roadname": "foo",
"data": [
{
"val": 50,
"loc": {
"type": "Point",
"coordinates": [3.32456512, 50.2744516]
}
}
}
]
},
{
"roadname": "bar",
"data": [
{
"val": 145,
"loc": {
"type": "Point",
"coordinates": [3.198408689, 50.64586985]
}
}
}
]
}
]
I'm displaying every data.loc
on a map, and that leads me to this :
. (Point color represent the
val
field)
EDIT3: To clarify my database structure, here is a representation of the exact database. Each gray line represent a root element from the provided dataset :
I would like to "group point that are near (using data.loc
), and have the same parent name
" , and aggregate their val
(say in a average to make it simple), in order to display something like this :
EDIT3: It's important to understand that the points I'm trying to aggregate don't share any common property or ancestors. Their ONLY common denomitator is their spatial proximity
I know near
, geonear
and group
aggregation, but I'm just not able to find a solution to do this.
I'd like to use a pure mongodb solution. If it's not possible, I could use turf.js or another library also, but I'm just struggling at finding a viable and scalable way to do this.
EDIT: The main root elements on the collection represent a road, so all the points on a road have all the same parent roadname
.
EDIT2: Data can be found here
Upvotes: 1
Views: 546
Reputation: 634
I think this is going to work for you. I imported your dataset aggregatingpointsdata.json into a mongo collection called roads
.
This is what one cluster would look like in mongo. So, each document represents a cluster, or one element in your array from the dataset you provided. One thing to keep in mind is that this will only work if there is some identifier for a cluster of points that should be grouped together. In my example, it is the _id
.
> db.roads.findOne()
{
"_id" : ObjectId("583ee50bd7c4d711d45c7757"),
"roadname" : "RD700",
"data" : [
{
"val" : null,
"loc" : {
"type" : "Point",
"coordinates" : [
3.197033554,
50.64611712
]
}
},
{
"val" : null,
"loc" : {
"type" : "Point",
"coordinates" : [
3.197740735,
50.6460058
]
}
},
{
"val" : 145,
"loc" : {
"type" : "Point",
"coordinates" : [
3.198408689,
50.64586985
]
}
},
]
}
This is the mongo aggregation you can run that will return an average of each cluster. I even made it to return the same GeoJSON format.
db.roads.aggregate([
//unravel the cluster here
{ $unwind: "$data" },
//project the coordinates to more readable names while preserving roadname and GeoJSON type.
{ $project: {roadname: 1, type: "$data.loc.type", val : "$data.val", lng: { $arrayElemAt: ["$data.loc.coordinates",0]}, lat: { $arrayElemAt: ["$data.loc.coordinates",-1]}}},
//group on cluster id while preserving type and roadname. Take avergae of value, lat and long.
{ $group: { _id : "$_id", roadname: {$first:"$roadname"}, type: {$first: "$type"}, avgLng: { $avg: "$lng" },avgLat: { $avg: "$lat" }, avgVal: { $avg : "$val" }}},
//re-project to fit the similar format of original collection
{ $project: { _id: 1, roadname: 1, data : { val: "$avgVal", loc: {type : "$type", coordinates: ["$avgLng", "$avgLat"]}} }}
])
You can also add an extra $out
to the end of the aggregation pipeline to move all these averaged centroids to another more manageable collection.
Here is what the results look like after the aggregration pipeline call shown above.
[
{
"_id": ObjectId("583ee50bd7c4d711d45c775c"),
"roadname": "RD700",
"data": {
"val": 144.03703703703704,
"loc": {
"type": "Point",
"coordinates": [
3.2232721289257142,
50.67602178708569
]
}
}
},
{
"_id": ObjectId("583ee50bd7c4d711d45c775b"),
"roadname": "RD700",
"data": {
"val": 170.0344827586207,
"loc": {
"type": "Point",
"coordinates": [
3.22367598656322,
50.67626952408046
]
}
}
},
...
]
Upvotes: 1