Move geo filter creation into reusable module.
This commit is contained in:
parent
572445de39
commit
d730aab674
|
@ -133,7 +133,7 @@ Memory.prototype.all = function all(model, filter, callback) {
|
|||
nodes = nodes.sort(sorting.bind(orders));
|
||||
}
|
||||
|
||||
var nearFilter = nearFilter(filter.where);
|
||||
var nearFilter = geo.nearFilter(filter.where);
|
||||
|
||||
// geo sorting
|
||||
if(nearFilter) {
|
||||
|
@ -170,26 +170,6 @@ Memory.prototype.all = function all(model, filter, callback) {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function nearFilter(where) {
|
||||
var result = false;
|
||||
|
||||
if(where && typeof where === 'object') {
|
||||
Object.keys(where).forEach(function (key) {
|
||||
var ex = where[key];
|
||||
|
||||
if(ex && ex.near) {
|
||||
result = {
|
||||
near: ex.near,
|
||||
maxDistance: ex.maxDistance,
|
||||
key: key
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
function applyFilter(filter) {
|
||||
|
|
124
lib/geo.js
124
lib/geo.js
|
@ -1,3 +1,37 @@
|
|||
/**
|
||||
* Dependencies.
|
||||
*/
|
||||
|
||||
var assert = require('assert');
|
||||
|
||||
/*!
|
||||
* Get a near filter from a given where object. For adapter use only.
|
||||
*/
|
||||
|
||||
exports.nearFilter = function nearFilter(where) {
|
||||
var result = false;
|
||||
|
||||
if(where && typeof where === 'object') {
|
||||
Object.keys(where).forEach(function (key) {
|
||||
var ex = where[key];
|
||||
|
||||
if(ex && ex.near) {
|
||||
result = {
|
||||
near: ex.near,
|
||||
maxDistance: ex.maxDistance,
|
||||
key: key
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Filter a set of objects using the given `nearFilter`.
|
||||
*/
|
||||
|
||||
exports.filter = function (arr, filter) {
|
||||
var origin = filter.near;
|
||||
var max = filter.maxDistance > 0 ? filter.maxDistance : false;
|
||||
|
@ -15,12 +49,13 @@ exports.filter = function (arr, filter) {
|
|||
if(typeof loc.lat !== 'number') return;
|
||||
if(typeof loc.lng !== 'number') return;
|
||||
|
||||
var d = distanceBetween(origin, loc);
|
||||
var d = GeoPoint.distanceBetween(origin, loc);
|
||||
|
||||
if(max && d > max) {
|
||||
// dont add
|
||||
} else {
|
||||
distances[obj.id] = d;
|
||||
loc.distance = d;
|
||||
result.push(obj);
|
||||
}
|
||||
});
|
||||
|
@ -34,20 +69,89 @@ exports.filter = function (arr, filter) {
|
|||
var db = distances[objB.id];
|
||||
|
||||
if(db === da) return 0;
|
||||
return da > db ? -1 : 1;
|
||||
return da > db ? 1 : -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var distanceBetween = exports.distanceBetween = function distanceBetween(a, b) {
|
||||
var xs = 0;
|
||||
var ys = 0;
|
||||
xs = a.lat - b.lat;
|
||||
xs = xs * xs;
|
||||
ys = a.lng - b.lng;
|
||||
ys = ys * ys;
|
||||
/**
|
||||
* Export the `GeoPoint` class.
|
||||
*/
|
||||
|
||||
return Math.sqrt( xs + ys );
|
||||
exports.GeoPoint = GeoPoint;
|
||||
|
||||
function GeoPoint(data) {
|
||||
if(!(this instanceof GeoPoint)) {
|
||||
return new GeoPoint(data);
|
||||
}
|
||||
|
||||
assert(typeof data === 'object', 'must provide a lat and lng object when creating a GeoPoint');
|
||||
assert(typeof data.lat === 'number', 'lat must be a number when creating a GeoPoint');
|
||||
assert(typeof data.lng === 'number', 'lng must be a number when creating a GeoPoint');
|
||||
assert(data.lng <= 180, 'lng must be <= 180');
|
||||
assert(data.lng >= -180, 'lng must be >= -180');
|
||||
assert(data.lat <= 90, 'lat must be <= 90');
|
||||
assert(data.lat >= -90, 'lat must be >= -90');
|
||||
|
||||
this.lat = data.lat;
|
||||
this.lng = data.lng;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the spherical distance between two geo points.
|
||||
*/
|
||||
|
||||
GeoPoint.distanceBetween = function distanceBetween(a, b, options) {
|
||||
var x1 = a.lat;
|
||||
var y1 = a.lng;
|
||||
|
||||
var x2 = b.lat;
|
||||
var y2 = b.lng;
|
||||
|
||||
return geoDistance(x1, y1, x2, y2, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the spherical distance to the given point.
|
||||
*/
|
||||
|
||||
GeoPoint.prototype.distanceTo = function (point, options) {
|
||||
return GeoPoint.distanceBetween(this, point, options);
|
||||
}
|
||||
|
||||
// ratio of a circle's circumference to its diameter
|
||||
var PI = 3.1415926535897932384626433832795;
|
||||
|
||||
// factor to convert decimal degrees to radians
|
||||
var DEG2RAD = 0.01745329252;
|
||||
|
||||
// factor to convert decimal degrees to radians
|
||||
var RAD2DEG = 57.29577951308;
|
||||
|
||||
// radius of the earth
|
||||
var EARTH_RADIUS = {
|
||||
kilometers: 6370.99056,
|
||||
meters: 6370990.56,
|
||||
miles: 3958.75,
|
||||
feet: 20902200,
|
||||
radians: 1,
|
||||
degrees: RAD2DEG
|
||||
};
|
||||
|
||||
function geoDistance(x1, y1, x2, y2, options) {
|
||||
// Convert to radians
|
||||
x1 = x1 * DEG2RAD;
|
||||
y1 = y1 * DEG2RAD;
|
||||
x2 = x2 * DEG2RAD;
|
||||
y2 = y2 * DEG2RAD;
|
||||
|
||||
a = Math.pow(Math.sin(( y2-y1 ) / 2.0 ), 2);
|
||||
b = Math.pow(Math.sin(( x2-x1 ) / 2.0 ), 2);
|
||||
c = Math.sqrt( a + Math.cos( y2 ) * Math.cos( y1 ) * b );
|
||||
|
||||
var type = (options && options.type) || 'miles';
|
||||
|
||||
return 2 * Math.asin( c ) * EARTH_RADIUS[type];
|
||||
}
|
|
@ -7,6 +7,7 @@ var ModelBaseClass = require('./model.js');
|
|||
var List = require('./list.js');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var util = require('util');
|
||||
var GeoPoint = require('./geo').GeoPoint;
|
||||
|
||||
/**
|
||||
* Export public API
|
||||
|
@ -46,6 +47,7 @@ ModelBuilder.registerType(Boolean);
|
|||
ModelBuilder.registerType(Date);
|
||||
ModelBuilder.registerType(Buffer, ['Binary']);
|
||||
ModelBuilder.registerType(Array);
|
||||
ModelBuilder.registerType(GeoPoint);
|
||||
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue