1

Here's the pitch. I have a collection of Circles which have mainly two attributes: location is a Point and radius is a distance in meters.

Users also have a profile.location Point attribute.

In my publications, I want to find all the Circles that the user is "in", ergo the ones he or she is near enough according to each Circle's radius attribute. To sum it up, here's how it would look like:

Meteor.publish('circles', function() {
    var curUser = Meteor.users.findOne({_id:this.userId});
    if (curUser) {
    return Circles.find({
        location: {$near: 
            {$geometry:curUser.profile.location,$maxDistance:self.radius} // HERE
        }
    }));
    }
    this.ready();
});

Except self.radius is a completely made-up term on my behalf. But is it possible to achieve something like this?

POST-SOLVING edit:

Thanks to Electric Jesus, I have my matchings working perfectly with polygons, since circles are not GeoJSON types as of yet. (therefore they are not single attributes that can be queried, sort of) So I converted my circles into polygons! Here is a JavaScript function to do this:

function generateGeoJSONCircle(center, radius, numSides) {

  var points = [];
  var earthRadius = 6371;
  var halfsides = numSides / 2;

  //angular distance covered on earth's surface
  var d = parseFloat(radius / 1000.) / earthRadius;

  var lat = (center.coordinates[1] * Math.PI) / 180;
  var lon = (center.coordinates[0] * Math.PI) / 180;

  for(var i = 0; i < numSides; i++) {
    var gpos = {};
    var bearing = i * Math.PI / halfsides; //rad
    gpos.latitude = Math.asin(Math.sin(lat) * Math.cos(d) + Math.cos(lat) * Math.sin(d) * Math.cos(bearing));
    gpos.longitude = ((lon + Math.atan2(Math.sin(bearing) * Math.sin(d) * Math.cos(lat), Math.cos(d) - Math.sin(lat) * Math.sin(gpos.latitude))) * 180) / Math.PI;
    gpos.latitude = (gpos.latitude * 180) / Math.PI;
    points.push([gpos.longitude, gpos.latitude]);
  };

  points.push(points[0]);
  return {
    type: 'Polygon',
    coordinates: [ points ]
  };
}

Here you go. I don't really know how many sides I should use, so I left an argument for that too. From there, you can use Electric Jesus's answer to get where I was going. Don't forget to put a 2dsphere index on your polygon!

Circles._ensureIndex({'polygonConversion': "2dsphere"});
SylvainB
  • 4,765
  • 2
  • 26
  • 39
  • Is this a dupe of your earlier [Sort users by distance from current user reactively in Meteor?](http://stackoverflow.com/questions/26348465/sort-users-by-distance-from-current-user-reactively-in-meteor) question? – Dan Dascalescu Oct 27 '14 at 22:13
  • No. This previous one was answered. The issue here is more complex. – SylvainB Oct 27 '14 at 22:30

3 Answers3

0

You can try an additively weighted voronoi diagram. The distance function is the euklidian distance minus the weight. Sites with bigger weights and nearby other sites get sorted into the same cell.

Micromega
  • 12,486
  • 7
  • 35
  • 72
0

Mongo's geospatial operators include $centerSphere, which returns documents that are within the bounds of a circle:

Entities.find({
  location : { 
    $geoWithin : { 
      $centerSphere: [ 
        [ curUser.profile.location.lng , curUser.profile.location.lat ],
        radius / 6378100  // convert radians to meters by dividing by the Earth's radius
      ]
    }
  }
} );
Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
  • What is `radius` in your example? – SylvainB Oct 27 '14 at 23:02
  • No, this is not what I am looking for. I want to find all circles (which are of various radius each) which contain `curUser.profile.location` within them. Not what "circle centers" are within a fixed distance from the current user. – SylvainB Oct 27 '14 at 23:06
0

No, the Geo indexes would never work in a way that you demand it to be dynamic according to a document's 'radius'. You must convert your circles into Polygon geometries and use the $geoIntersects query to find which Circle (Polygon geometry) intersects with your current location/location parameter (Point geometry).

var location = {
  longitude: -1.85549,
  latitude: 52.9445
}

// create a circle with a Polygon geometry location, with converted coordinates
Circles.insert({name: "My Circle 1", loc: {
        type: "Polygon",
        coordinates: [[[ ... ]]], 
     }
});

// finding one or more Circles that intersect with current location.
Circles.find({loc: {
  $geoIntersects: {
    $geometry: {
      type: "Point" coordinates: [location.longitude, location.latitude]
    }
  }
}});
Seth Malaki
  • 4,436
  • 23
  • 48