Browse Source

Merge pull request #274 from pelias/243-reverse-confidence-scores

243 reverse confidence scores
improved_bias
Diana Shkolnikov 9 years ago
parent
commit
55afbb7558
  1. 68
      middleware/confidenceScoreReverse.js
  2. 11
      middleware/distance.js
  3. 5
      routes/v1.js
  4. 178
      test/unit/middleware/confidenceScoreReverse.js
  5. 4
      test/unit/middleware/distance.js
  6. 1
      test/unit/run.js

68
middleware/confidenceScoreReverse.js

@ -0,0 +1,68 @@
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 SCORES = {
EXACT: 1.0,
EXCELLENT: 0.9,
GOOD: 0.8,
OKAY: 0.7,
POOR: 0.6,
NONE: 0.5,
INVALID: 0.0
};
var BUCKETS = {
_1_METER: 1,
_10_METERS: 10,
_100_METERS: 100,
_250_METERS: 250,
_1_KILOMETER: 1000
};
function setup() {
return computeScores;
}
function computeScores(req, res, next) {
// do nothing if no result data set
if (!res.data || !res.data) {
return next();
}
// loop through data items and determine confidence scores
res.data = res.data.map(computeConfidenceScore);
next();
}
function computeConfidenceScore(hit) {
// non-number or invalid distance should be given confidence 0.0
if (!_.isFinite(hit.distance) || hit.distance < 0) {
hit.confidence = SCORES.INVALID;
return hit;
}
var distance = hit.distance * 1000.0;
// figure out which range the distance lies in and assign confidence accordingly
if (distance < BUCKETS._1_METER) {
hit.confidence = SCORES.EXACT;
} else if (distance < BUCKETS._10_METERS) {
hit.confidence = SCORES.EXCELLENT;
} else if (distance < BUCKETS._100_METERS) {
hit.confidence = SCORES.GOOD;
} else if (distance < BUCKETS._250_METERS) {
hit.confidence = SCORES.OKAY;
} else if (distance < BUCKETS._1_KILOMETER) {
hit.confidence = SCORES.POOR;
} else {
hit.confidence = SCORES.NONE;
}
return hit;
}
module.exports = setup;

11
middleware/distance.js

@ -1,4 +1,5 @@
var geolib = require('geolib');
var check = require('check-types');
function setup() {
@ -13,14 +14,20 @@ function computeDistances(req, res, next) {
return next();
}
if ( !(req.clean.hasOwnProperty('lat') && req.clean.hasOwnProperty('lon')) ) {
if (!(check.number(req.clean['point.lat']) &&
check.number(req.clean['point.lon']))) {
return next();
}
var point = {
latitude: req.clean['point.lat'],
longitude: req.clean['point.lon']
};
res.data.forEach(function (place) {
// the result of getDistance is in meters, so convert to kilometers
place.distance = geolib.getDistance(
{ latitude: req.clean.lat, longitude: req.clean.lon },
point,
{ latitude: place.center_point.lat, longitude: place.center_point.lon }
) / 1000;
});

5
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')
@ -75,8 +76,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(),
postProc.renamePlacenames(),
postProc.geocodeJSON(peliasConfig, base),
postProc.sendJSON

178
test/unit/middleware/confidenceScoreReverse.js

@ -0,0 +1,178 @@
var confidenceScoreReverse = require('../../../middleware/confidenceScoreReverse')();
module.exports.tests = {};
module.exports.tests.confidenceScoreReverse = function(test, common) {
test('res without results should not throw exception', function(t) {
var res = {};
try {
confidenceScoreReverse(null, res, function() {});
}
catch (e) {
t.fail('an exception should not have been thrown');
}
finally {
t.end();
}
});
test('res.results without data should not throw exception', function(t) {
var res = {
results: {}
};
try {
confidenceScoreReverse(null, res, function() {});
}
catch (e) {
t.fail('an exception should not have been thrown');
}
finally {
t.end();
}
});
test('0m <= distance < 1m should be given score 1.0', function(t) {
var res = {
data: [
{ distance: 0.0000 / 1000.0 },
{ distance: 0.9999 / 1000.0 }
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 1.0, 'score should be exact confidence');
t.equal(res.data[1].confidence, 1.0, 'score should be exact confidence');
t.end();
});
});
test('1m <= distance < 10m should be given score 0.9', function(t) {
var res = {
data: [
{ distance: 1.0000 / 1000.0 },
{ distance: 9.9999 / 1000.0 }
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.9, 'score should be excellent confidence');
t.equal(res.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 res = {
data: [
{ distance: 10.0000 / 1000.0 },
{ distance: 99.9999 / 1000.0 }
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.8, 'score should be good confidence');
t.equal(res.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 res = {
data: [
{ distance: 100.0000 / 1000.0 },
{ distance: 249.9999 / 1000.0 }
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.7, 'score should be okay confidence');
t.equal(res.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 res = {
data: [
{distance: 250.0000 / 1000.0},
{distance: 999.9999 / 1000.0}
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.6, 'score should be poor confidence');
t.equal(res.data[1].confidence, 0.6, 'score should be poor confidence');
t.end();
});
});
test('distance >= 1000m should be given score 0.5', function(t) {
var res = {
data: [
{distance: 1000.0 / 1000.0},
{distance: 2000.0 / 1000.0}
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.5, 'score should be least confidence');
t.equal(res.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 res = {
data: [
{ distance: -1.0000 / 1000.0 }
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.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 res = {
data: [
{},
{distance: []},
{distance: {}},
{distance: 'this is not a number'}
]
};
confidenceScoreReverse(null, res, function() {
t.equal(res.data[0].confidence, 0.0, 'score should be 0.0 confidence');
t.equal(res.data[1].confidence, 0.0, 'score should be 0.0 confidence');
t.equal(res.data[2].confidence, 0.0, 'score should be 0.0 confidence');
t.equal(res.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);
}
};

4
test/unit/middleware/distance.js

@ -6,8 +6,8 @@ module.exports.tests.computeDistance = function(test, common) {
test('valid lat/lon and results', function(t) {
var req = {
clean: {
lat: 45,
lon: -77
'point.lat': 45,
'point.lon': -77
}
};
var res = {

1
test/unit/run.js

@ -28,6 +28,7 @@ var tests = [
require('./helper/types'),
require('./sanitiser/_geo_common'),
require('./middleware/distance'),
require('./middleware/confidenceScoreReverse'),
require('./sanitiser/_size'),
];

Loading…
Cancel
Save