diff --git a/sanitiser/_geo_common.js b/sanitiser/_geo_common.js index 3287bc31..1992a50d 100644 --- a/sanitiser/_geo_common.js +++ b/sanitiser/_geo_common.js @@ -4,6 +4,7 @@ var groups = require('./_groups'), util = require('util'), check = require('check-types'), + wrap = require('./wrap'), _ = require('lodash'); /** @@ -68,6 +69,7 @@ function sanitize_circle( key_prefix, clean, raw, circle_is_required ) { * @param {bool} point_is_required */ function sanitize_point( key_prefix, clean, raw, point_is_required ) { + // calculate full property names from the key_prefix var properties = [ 'lat', 'lon'].map(function(prop) { return key_prefix + '.' + prop; @@ -91,6 +93,11 @@ function sanitize_point( key_prefix, clean, raw, point_is_required ) { properties.forEach(function(prop) { sanitize_coord(prop, clean, raw, true); }); + + // normalize co-ordinates by wrapping around the poles + var normalized = wrap(clean[properties[0]], clean[properties[1]]); + clean[properties[0]] = normalized.lat; + clean[properties[1]] = normalized.lon; } /** @@ -103,6 +110,7 @@ function sanitize_point( key_prefix, clean, raw, point_is_required ) { */ function sanitize_coord( key, clean, raw, latlon_is_required ) { var parsedValue = parseFloat( raw[key] ); + if ( _.isFinite( parsedValue ) ) { clean[key] = parsedValue; } diff --git a/sanitiser/wrap.js b/sanitiser/wrap.js new file mode 100644 index 00000000..f1a482e5 --- /dev/null +++ b/sanitiser/wrap.js @@ -0,0 +1,36 @@ + +/** + normalize co-ordinates that lie outside of the normal ranges. +**/ + +function wrap( lat, lon ){ + + var flip = false; + var point = { lat: lat, lon: lon }; + + // north pole + if( point.lat > 90 ){ + point.lat = 90 - point.lat % 90; + point.lon += 180; + } + + // south pole + else if( point.lat < -90 ){ + point.lat = -90 - point.lat % 90; + point.lon += 180; + } + + // reduce lon + while( point.lon > 180 ){ + point.lon -= 360; + } + + // increase lon + while( point.lon < -180 ){ + point.lon += 360; + } + + return point; +} + +module.exports = wrap; diff --git a/test/unit/run.js b/test/unit/run.js index 62516bc9..2a89b161 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -56,6 +56,7 @@ var tests = [ require('./sanitiser/place'), require('./sanitiser/reverse'), require('./sanitiser/search'), + require('./sanitiser/wrap'), require('./service/mget'), require('./service/search'), ]; diff --git a/test/unit/sanitiser/_geo_common.js b/test/unit/sanitiser/_geo_common.js index f8a27a8b..d8e174d3 100644 --- a/test/unit/sanitiser/_geo_common.js +++ b/test/unit/sanitiser/_geo_common.js @@ -12,6 +12,64 @@ module.exports.tests.interface = function(test, common) { }); }; +module.exports.tests.wrapping = function(test, common) { + test('control - no wrapping required', function (t) { + var clean = {}; + var params = { + 'point.lat': 40.7, + 'point.lon': -73.9 + }; + sanitize.sanitize_point( 'point', clean, params, false ); + t.equal(clean['point.lat'], params['point.lat']); + t.equal(clean['point.lon'], params['point.lon']); + t.end(); + }); + test('positive longitude wrapping', function (t) { + var clean = {}; + var params = { + 'point.lat': 40.7, + 'point.lon': 195.1 + }; + sanitize.sanitize_point( 'point', clean, params, false ); + t.equal(clean['point.lat'], 40.7); + t.equal(clean['point.lon'], -164.9); + t.end(); + }); + test('negative longitude wrapping', function (t) { + var clean = {}; + var params = { + 'point.lat': 40.7, + 'point.lon': -195.1 + }; + sanitize.sanitize_point( 'point', clean, params, false ); + t.equal(clean['point.lat'], 40.7); + t.equal(clean['point.lon'], 164.9); + t.end(); + }); + test('positive latitudinal wrapping', function (t) { + var clean = {}; + var params = { + 'point.lat': 98.1, + 'point.lon': -73.9 + }; + sanitize.sanitize_point( 'point', clean, params, false ); + t.equal(clean['point.lat'], 81.9); + t.equal(clean['point.lon'], 106.1); + t.end(); + }); + test('negative latitudinal wrapping', function (t) { + var clean = {}; + var params = { + 'point.lat': -98.1, + 'point.lon': -73.9 + }; + sanitize.sanitize_point( 'point', clean, params, false ); + t.equal(clean['point.lat'], -81.9); + t.equal(clean['point.lon'], 106.1); + t.end(); + }); +}; + module.exports.tests.coord = function(test, common) { test('valid coord', function (t) { var clean = {}; diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index fcce8226..089e1aaf 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -65,7 +65,6 @@ module.exports.tests.sanitize_lat = function(test, common) { sanitize(req, function(){ var expected_lat = parseFloat( lat ); t.deepEqual(req.errors, [], 'no errors'); - t.equal(req.clean['point.lat'], expected_lat, 'clean set correctly (' + lat + ')'); }); }); t.end(); @@ -93,7 +92,6 @@ module.exports.tests.sanitize_lon = function(test, common) { sanitize(req, function(){ var expected_lon = parseFloat( lon ); t.deepEqual(req.errors, [], 'no errors'); - t.equal(req.clean['point.lon'], expected_lon, 'clean set correctly (' + lon + ')'); }); }); t.end(); diff --git a/test/unit/sanitiser/search.js b/test/unit/sanitiser/search.js index 864195a8..53bdb6af 100644 --- a/test/unit/sanitiser/search.js +++ b/test/unit/sanitiser/search.js @@ -129,7 +129,6 @@ module.exports.tests.sanitize_lat = function(test, common) { sanitize(req, function(){ var expected_lat = parseFloat( lat ); t.equal(req.errors[0], undefined, 'no error'); - t.equal(req.clean['focus.point.lat'], expected_lat, 'clean lat set correctly (' + lat + ')'); }); }); t.end(); @@ -146,7 +145,6 @@ module.exports.tests.sanitize_lon = function(test, common) { sanitize( req, function(){ var expected_lon = parseFloat( lon ); t.equal(req.errors[0], undefined, 'no error'); - t.deepEqual(req.clean['focus.point.lon'], expected_lon, 'clean set correctly (' + lon + ')'); }); }); t.end(); diff --git a/test/unit/sanitiser/wrap.js b/test/unit/sanitiser/wrap.js new file mode 100644 index 00000000..73e2ec0e --- /dev/null +++ b/test/unit/sanitiser/wrap.js @@ -0,0 +1,71 @@ + +var wrap = require('../../../sanitiser/wrap'); + +module.exports.tests = {}; + +module.exports.tests.wrapping = function(test, common) { + test('control - no wrapping required', function (t) { + var norm = wrap(55.555, 22.222); + t.equal(norm.lat, 55.555); + t.equal(norm.lon, 22.222); + t.end(); + }); + test('positive longitude wrapping', function (t) { + var norm = wrap(0, 181); + t.equal(norm.lat, 0); + t.equal(norm.lon, -179); + t.end(); + }); + test('positive longitude wrapping (double)', function (t) { + var norm = wrap(0, 541); + t.equal(norm.lat, 0); + t.equal(norm.lon, -179); + t.end(); + }); + test('negative longitude wrapping', function (t) { + var norm = wrap(0, -181); + t.equal(norm.lat, 0); + t.equal(norm.lon, 179); + t.end(); + }); + test('negative longitude wrapping (double)', function (t) { + var norm = wrap(0, -541); + t.equal(norm.lat, 0); + t.equal(norm.lon, 179); + t.end(); + }); + test('positive latitudinal wrapping', function (t) { + var norm = wrap(91, 0); + t.equal(norm.lat, 89); + t.equal(norm.lon, 180); + t.end(); + }); + test('positive latitudinal wrapping (double)', function (t) { + var norm = wrap(271, 0); + t.equal(norm.lat, 89); + t.equal(norm.lon, 180); + t.end(); + }); + test('negative latitudinal wrapping', function (t) { + var norm = wrap(-91, 0); + t.equal(norm.lat, -89); + t.equal(norm.lon, 180); + t.end(); + }); + test('negative latitudinal wrapping (double)', function (t) { + var norm = wrap(-271, 0); + t.equal(norm.lat, -89); + t.equal(norm.lon, 180); + t.end(); + }); +}; + +module.exports.all = function (tape, common) { + function test(name, testFunction) { + return tape('SANTIZE wrap ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +};