How find all overlapping circles from radius of central circle?
Asked Answered
S

2

5

How to do an intersection or overlap query in mongo shell - what circles overlap my search region? Within relate only to the center position but doesn't include radius of the other circles in searched scope.

Mongo:

# My bad conception:
var search = [[30, 30], 10]
db.places.find({circle : {"$within" : {"$center" : [search]}}})

Now I can obtain only this circles within central point lies in searched area of circle:

Ruby:

# field :circle, type: Circle # eg. [ [ 30, 30 ], 10 ]
field :radius, type: Integer
field :location, :type => Array, :spatial => true
spatial_index :location

Places.within_circle(location: [ [ 30, 30 ], 10 ])

# {"$query"=>{"location"=>{"$within"=>{"$center"=>[[30, 30], 10]}}}

I created example data with additional location (special index) and radius instead circle because circle isn't supported by mongodb geo index:

{ "_id" : 1, "name" : "a", "circle" : [ [ 5, 5 ], 40 ], "latlng" : [ 5, 5 ], "radius" : 40 }
{ "_id" : 2, "name" : "b", "circle" : [ [ 10, 10 ], 5 ], "latlng" : [ 10, 10 ], "radius" : 5 }
{ "_id" : 3, "name" : "c", "circle" : [ [ 20, 20 ], 5 ], "latlng" : [ 20, 20 ], "radius" : 5 }
{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}
{ "_id" : 5, "name" : "e", "circle" : [ [ 80, 80 ], 30 ], "latlng" : [ 80, 80 ], "radius" : 30}
{ "_id" : 6, "name" : "f", "circle" : [ [ 80, 80 ], 20 ], "latlng" : [ 80, 80 ], "radius" : 20}

Desired query result:

{ "_id" : 1, "name" : "a", "circle" : [ [ 5, 5 ], 40 ], "latlng" : [ 5, 5 ], "radius" : 40 }
{ "_id" : 3, "name" : "c", "circle" : [ [ 20, 20 ], 5 ], "latlng" : [ 20, 20 ], "radius" : 5 }
{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}
{ "_id" : 5, "name" : "e", "circle" : [ [ 80, 80 ], 30 ], "latlng" : [ 80, 80 ], "radius" : 30}

Solution below assumes that I get all rows and then filter on the ruby side my radius but it returns only:

{ "_id" : 4, "name" : "d", "circle" : [ [ 30, 30 ], 50 ], "latlng" : [ 30, 30 ], "radius" : 50}
Stygian answered 15/10, 2012 at 8:55 Comment(4)
I think you want back "a", "c" and "d". "e" does not overlap with your circle.Flavine
I could not check my code the first time I answered. Checked it and fixed it now. You can see by yourself. As Asya Kamsky said, you expect only a, c and d. Otherwise the information you gave us are wrong.Paradigm
Actually I made a mistake at the "e". Both solutions are good, but now I see that for current time will use ruby. I'm afraid that on larger size of records my program may not work too fast, so I asked also about the approach in mongo. Thank You!Stygian
Please note that as of 2.4 (March 2013) $geoIntersects operator is a available to provide this functionality for GeoJSON and 2dSphere indexes.Flavine
P
5

I'm not familiar with mongodb but I assume that in [[x, y], r] values mean

x: value of the center on the axis x. y: value of the center on the axis y. r: circle radius.

Say you have circle S which is your search and a random circle A. Then you could calcule the distance between both circles' center (S.center and A.center) and see if it is inferior to those both circles radius added (S.r + A.r).

def distance_between(a, b)
  ((b.first - a.first)**2 + (b.last - a.last)**2)**0.5
end

elements = [{ _id: 1, name: "a", circle: [ [ 5, 5 ], 40 ] },
            { _id: 2, name: "b", circle: [ [ 10, 10 ], 5 ] },
            { _id: 3, name: "c", circle: [ [ 20, 20 ], 5 ] },
            { _id: 4, name: "d", circle: [ [ 30, 30 ], 50 ] },
            { _id: 5, name: "e", circle: [ [ 80, 80 ], 30 ] },
            { _id: 6, name: "f", circle: [ [ 80, 80 ], 20 ] }]
search = [[30, 30], 10]

elements.select do |elem| circle = elem[:circle]
  distance_between(circle.first, search.first) <= circle.last + search.last
end

#{:_id=>1, :name=>"a", :circle=>[[5, 5], 40]}
#{:_id=>3, :name=>"c", :circle=>[[20, 20], 5]}
#{:_id=>4, :name=>"d", :circle=>[[30, 30], 50]}
Paradigm answered 15/10, 2012 at 14:33 Comment(0)
F
3

Unfortunately there is currently no capability in mongo to directly query about overlapping objects, only points within objects.

@oldergod's answer describes the algorithm to calculate whether two circles overlap.

Here is a work-around in the shell based on that calculation:

function distance(a, b) {
    return Math.pow(Math.pow(a[0] - b[0], 2) + Math.pow(a[1] - b[1], 2), 0.5);
}

On your sample data inserted into collection 'circle':

> db.circle.find().forEach(
    function(c) {  
        if ( (distance(c.latlng, search.latlng) < c.radius + search.radius) ) 
          {   
               print(c.name); 
          } 
    } )
a
c
d
> 
Flavine answered 20/10, 2012 at 22:55 Comment(1)
Please note that as of 2.4 (March 2013) $geoIntersects operator is a available to provide this functionality for GeoJSON and 2dSphere indexes.Flavine

© 2022 - 2024 — McMap. All rights reserved.