From 0b5172a4525fbec97f8c27905be08814cbdf885a Mon Sep 17 00:00:00 2001 From: Stephen Hess Date: Wed, 16 Sep 2015 16:54:36 -0400 Subject: [PATCH] initial stab at confidence score calculation based on distance from point.lat and point.lon --- middleware/confidenceScoreReverse.js | 60 ++++++ routes/v1.js | 5 +- .../unit/middleware/confidenceScoreReverse.js | 185 ++++++++++++++++++ test/unit/run.js | 1 + 4 files changed, 250 insertions(+), 1 deletion(-) create mode 100644 middleware/confidenceScoreReverse.js create mode 100644 test/unit/middleware/confidenceScoreReverse.js diff --git a/middleware/confidenceScoreReverse.js b/middleware/confidenceScoreReverse.js new file mode 100644 index 00000000..46e188f1 --- /dev/null +++ b/middleware/confidenceScoreReverse.js @@ -0,0 +1,60 @@ +var logger = require('pelias-logger').get('api'); +var _ = require('lodash'); + +// these are subjective terms, but wanted to add shortcuts to denote something +// about importance +var confidence = { + exact: 1.0, + excellent: 0.9, + good: 0.8, + okay: 0.7, + poor: 0.6, + none: 0.5 +}; + +function setup(peliasConfig) { + return computeScores; +} + +function computeScores(req, res, next) { + // do nothing if no result data set + if (!req.results || !req.results.data) { + return next(); + } + + // loop through data items and determine confidence scores + req.results.data = req.results.data.map(computeConfidenceScore.bind(null, req)); + + next(); +} + +function computeConfidenceScore(req, hit) { + // non-number or invalid distance should be given confidence 0.0 + if (typeof hit.distance !== 'number' || hit.distance < 0) { + hit.confidence = 0.0; + return hit; + } + + var meters = hit.distance * 1000; + + // figure out which range the distance lies in and assign confidence accordingly + // TODO: this could probably be made more node-y with a map of function->number + if (meters === 0) { + hit.confidence = confidence.exact; + } else if (_.inRange(meters, 0, 10)) { + hit.confidence = confidence.excellent; + } else if (_.inRange(meters, 10, 100)) { + hit.confidence = confidence.good; + } else if (_.inRange(meters, 100, 250)) { + hit.confidence = confidence.okay; + } else if (_.inRange(meters, 250, 1000)) { + hit.confidence = confidence.poor; + } else { + hit.confidence = confidence.none; + } + + return hit; + +} + +module.exports = setup; diff --git a/routes/v1.js b/routes/v1.js index 97ddcc0b..6bc72183 100644 --- a/routes/v1.js +++ b/routes/v1.js @@ -29,6 +29,7 @@ var controllers = { var postProc = { distances: require('../middleware/distance'), confidenceScores: require('../middleware/confidenceScore'), + confidenceScoresReverse: require('../middleware/confidenceScoreReverse'), renamePlacenames: require('../middleware/renamePlacenames'), geocodeJSON: require('../middleware/geocodeJSON'), sendJSON: require('../middleware/sendJSON') @@ -73,8 +74,10 @@ function addRoutes(app, peliasConfig) { sanitisers.reverse.middleware, middleware.types, controllers.search(undefined, reverseQuery), - // TODO: add confidence scores postProc.distances(), + // reverse confidence scoring depends on distance from origin + // so it must be calculated first + postProc.confidenceScoresReverse(peliasConfig), postProc.renamePlacenames(), postProc.geocodeJSON(peliasConfig), postProc.sendJSON diff --git a/test/unit/middleware/confidenceScoreReverse.js b/test/unit/middleware/confidenceScoreReverse.js new file mode 100644 index 00000000..41a288b2 --- /dev/null +++ b/test/unit/middleware/confidenceScoreReverse.js @@ -0,0 +1,185 @@ +var confidenceScoreReverse = require('../../../middleware/confidenceScoreReverse')(); + +module.exports.tests = {}; + +module.exports.tests.confidenceScoreReverse = function(test, common) { + test('distance == 0m should be given score 1.0', function(t) { + var req = { + results: { + data: [ + { + distance: 0 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 1.0, 'score should be exact confidence'); + t.end(); + }); + + }); + + test('0m < distance < 10m should be given score 0.9', function(t) { + var req = { + results: { + data: [ + { + distance: 0.0001 + }, + { + distance: 0.0099 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.9, 'score should be excellent confidence'); + t.equal(req.results.data[1].confidence, 0.9, 'score should be excellent confidence'); + t.end(); + }); + + }); + + test('10m <= distance < 100m should be given score 0.8', function(t) { + var req = { + results: { + data: [ + { + distance: 0.010 + }, + { + distance: 0.099 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.8, 'score should be good confidence'); + t.equal(req.results.data[1].confidence, 0.8, 'score should be good confidence'); + t.end(); + }); + + }); + + test('100m <= distance < 250m should be given score 0.7', function(t) { + var req = { + results: { + data: [ + { + distance: 0.100 + }, + { + distance: 0.249 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.7, 'score should be okay confidence'); + t.equal(req.results.data[1].confidence, 0.7, 'score should be okay confidence'); + t.end(); + }); + + }); + + test('250m <= distance < 1000m should be given score 0.6', function(t) { + var req = { + results: { + data: [ + { + distance: 0.250 + }, + { + distance: 0.999 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.6, 'score should be poor confidence'); + t.equal(req.results.data[1].confidence, 0.6, 'score should be poor confidence'); + t.end(); + }); + + }); + + test('distance >= 1000m should be given score 0.5', function(t) { + var req = { + results: { + data: [ + { + distance: 1 + }, + { + distance: 2 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.5, 'score should be least confidence'); + t.equal(req.results.data[1].confidence, 0.5, 'score should be least confidence'); + t.end(); + }); + + }); + + test('distance < 0 (invalid) should be given score 0.0', function(t) { + var req = { + results: { + data: [ + { + distance: -0.0001 + } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.0, 'score should be 0.0 confidence'); + t.end(); + }); + + }); + + test('non-number-type (invalid) distance should be given score 0.0', function(t) { + var req = { + results: { + data: [ + {}, + { distance: [] }, + { distance: {} }, + { distance: 'this is not a number' } + ] + } + }; + + confidenceScoreReverse(req, null, function() { + t.equal(req.results.data[0].confidence, 0.0, 'score should be 0.0 confidence'); + t.equal(req.results.data[1].confidence, 0.0, 'score should be 0.0 confidence'); + t.equal(req.results.data[2].confidence, 0.0, 'score should be 0.0 confidence'); + t.equal(req.results.data[3].confidence, 0.0, 'score should be 0.0 confidence'); + t.end(); + }); + + }); + +}; + +module.exports.all = function (tape, common) { + + function test(name, testFunction) { + return tape('[middleware] confidenceScoreReverse: ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/run.js b/test/unit/run.js index 3c24d0fb..f54b77f8 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -27,6 +27,7 @@ var tests = [ require('./helper/types'), require('./sanitiser/_geo_common'), require('./middleware/distance'), + require('./middleware/confidenceScoreReverse'), ]; tests.map(function(t) {