From 15c63e405cf09b3c3984c090fd1ab6e80d7a0cb4 Mon Sep 17 00:00:00 2001 From: Harish Krishna Date: Wed, 17 Sep 2014 19:52:28 -0400 Subject: [PATCH] adding a naive reverse api endpoint (also, shares most of the search controller). updated search query to include a filter and a sort (calculating a bounding box, given a lat/lon) --- controller/search.js | 7 +++-- index.js | 5 +++- middleware/reverse.js | 7 +++++ middleware/search.js | 64 +++++++++++++++++++++++++++++++++++++++++++ query/reverse.js | 31 +++++++++++++++++++++ query/search.js | 43 +++++++++++++++++++---------- 6 files changed, 138 insertions(+), 19 deletions(-) create mode 100644 middleware/reverse.js create mode 100644 middleware/search.js create mode 100644 query/reverse.js diff --git a/controller/search.js b/controller/search.js index dd021120..d4dfcbd2 100644 --- a/controller/search.js +++ b/controller/search.js @@ -1,9 +1,10 @@ -var query = require('../query/search'), - backend = require('../src/backend'); +var backend = require('../src/backend'); function controller( req, res, next ){ - + var required_query = req.required_query ? req.required_query : 'search'; + var query = require('../query/' + required_query); + // backend command var cmd = { index: 'pelias', diff --git a/index.js b/index.js index 39207611..8b7c6c95 100644 --- a/index.js +++ b/index.js @@ -16,7 +16,10 @@ app.get( '/', require('./controller/index') ); app.get( '/suggest', require('./sanitiser/sanitise'), require('./controller/suggest') ); // search API -app.get( '/search', require('./sanitiser/sanitise'), require('./controller/search') ); +app.get( '/search', require('./sanitiser/sanitise'), require('./middleware/search'), require('./controller/search') ); + +// reverse API +app.get( '/reverse', require('./sanitiser/sanitise'), require('./middleware/reverse'), require('./controller/search') ); /** ----------------------- error middleware ----------------------- **/ diff --git a/middleware/reverse.js b/middleware/reverse.js new file mode 100644 index 00000000..6275ade5 --- /dev/null +++ b/middleware/reverse.js @@ -0,0 +1,7 @@ +// middleware +function middleware(req, res, next){ + req.required_query = "reverse"; + next(); +} + +module.exports = middleware; \ No newline at end of file diff --git a/middleware/search.js b/middleware/search.js new file mode 100644 index 00000000..d22b3414 --- /dev/null +++ b/middleware/search.js @@ -0,0 +1,64 @@ +function deg2rad(degrees) { + return Math.PI*degrees/180; +} + +function rad2deg(radians) { + return 180.0*radians/Math.PI; +} + +// Semi-axes of WGS-84 geoidal reference +var WGS84_a = 6378137.0; // Major semiaxis [m] +var WGS84_b = 6356752.3; // Minor semiaxis [m] + +// Earth radius at a given latitude, according to the WGS-84 ellipsoid [m] +function WGS84EarthRadius(lat){ + // http://en.wikipedia.org/wiki/Earth_radius + var An = WGS84_a*WGS84_a * Math.cos(lat); + var Bn = WGS84_b*WGS84_b * Math.sin(lat); + var Ad = WGS84_a * Math.cos(lat); + var Bd = WGS84_b * Math.sin(lat); + return Math.sqrt( (An*An + Bn*Bn)/(Ad*Ad + Bd*Bd) ); +} + +// Bounding box surrounding the point at given coordinates, +// assuming local approximation of Earth surface as a sphere +// of radius given by WGS84 +function boundingBox(latitudeInDegrees, longitudeInDegrees, halfSideInKm) { + var lat = deg2rad(latitudeInDegrees); + var lon = deg2rad(longitudeInDegrees); + var halfSide = 1000*halfSideInKm; + + // Radius of Earth at given latitude + var radius = WGS84EarthRadius(lat); + // Radius of the parallel at given latitude + var pradius = radius*Math.cos(lat); + + var latMin = lat - halfSide/radius; + var latMax = lat + halfSide/radius; + var lonMin = lon - halfSide/pradius; + var lonMax = lon + halfSide/pradius; + + return { + 'top_left': { + 'lat': rad2deg(latMin), + 'lon': rad2deg(lonMin) + }, + 'bottom_right': { + 'lat': rad2deg(latMax), + 'lon': rad2deg(lonMax) + } + }; +} + + + +// middleware +function middleware(req, res, next){ + req.clean = req.clean || {}; + // ideally, bbox should be part of the req (and not to be calculated) + // TBD + req.clean.bbox = boundingBox(req.query.lat, req.query.lon, 2000); + next(); +} + +module.exports = middleware; \ No newline at end of file diff --git a/query/reverse.js b/query/reverse.js new file mode 100644 index 00000000..4fb98484 --- /dev/null +++ b/query/reverse.js @@ -0,0 +1,31 @@ +var logger = require('../src/logger'); + +// Build pelias search query +function generate( params ){ + + var cmd = { + "query":{ + "filtered" : { + "query" : { + "match_all" : {} + }, + "filter" : { + "geo_distance" : { + "distance" : "1km", + "center_point" : { + "lat": params.lat, + "lon": params.lon + } + } + } + } + }, + "size": 1 + }; + + logger.log( 'cmd', JSON.stringify( cmd, null, 2 ) ); + return cmd; + +} + +module.exports = generate; \ No newline at end of file diff --git a/query/search.js b/query/search.js index e17d0838..a7eb0596 100644 --- a/query/search.js +++ b/query/search.js @@ -5,23 +5,36 @@ function generate( params ){ var cmd = { "query":{ - "filtered" : { - "query" : { - "match" : { - "name.default": params.input - } - }, - "filter" : { - "geo_distance" : { - "distance" : "200km", - "center_point" : { - "lat": params.lat, - "lon": params.lon - } + "query_string" : { + "query": params.input, + "fields": ['name.default'], + "default_operator": 'OR' + } + }, + "filter": { + "geo_bounding_box": { + "center_point": { + "top_left": { + "lat": params.bbox.top_left.lat, + "lon": params.bbox.top_left.lon + }, + "bottom_right": { + "lat": params.bbox.bottom_right.lat, + "lon": params.bbox.bottom_right.lon } + } } - } - }, + }, + "sort" : [{ + "_geo_distance" : { + "center_point" : { + "lat": params.lat, + "lon": params.lon + }, + "order": 'asc', + "unit": 'km' + } + }], "size": 30 };