Browse Source

Merge pull request #608 from pelias/coordinate_wrapping

wrap coordinates at poles
pull/509/merge
Peter Johnson 8 years ago committed by GitHub
parent
commit
935ac4f1b7
  1. 8
      sanitiser/_geo_common.js
  2. 50
      sanitiser/wrap.js
  3. 1
      test/unit/run.js
  4. 59
      test/unit/sanitiser/_geo_common.js
  5. 2
      test/unit/sanitiser/reverse.js
  6. 2
      test/unit/sanitiser/search.js
  7. 191
      test/unit/sanitiser/wrap.js

8
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;
}

50
sanitiser/wrap.js

@ -0,0 +1,50 @@
/**
normalize co-ordinates that lie outside of the normal ranges.
longitude wrapping simply requires adding +- 360 to the value until it comes
in to range.
for the latitude values we need to flip the longitude whenever the latitude
crosses a pole.
**/
function wrap( lat, lon ){
var point = { lat: lat, lon: lon };
var quadrant = Math.floor( Math.abs(lat) / 90) % 4;
var pole = ( lat > 0 ) ? 90 : -90;
var offset = lat % 90;
switch( quadrant ){
case 0:
point.lat = offset;
break;
case 1:
point.lat = pole - offset;
point.lon += 180;
break;
case 2:
point.lat = -offset;
point.lon += 180;
break;
case 3:
point.lat = -pole + offset;
break;
}
// reduce lon
while( point.lon > 180 ){
point.lon -= 360;
}
// increase lon
while( point.lon < -180 ){
point.lon += 360;
}
return point;
}
module.exports = wrap;

1
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'),
];

59
test/unit/sanitiser/_geo_common.js

@ -12,6 +12,65 @@ module.exports.tests.interface = function(test, common) {
});
};
// @note: for better coverage see unit tests for 'wrap.js'.
module.exports.tests.wrapping = function(test, common) {
test('control - no wrapping required', function (t) {
var clean = {};
var params = {
'point.lat': +1.1,
'point.lon': -1.1
};
sanitize.sanitize_point( 'point', clean, params, false );
t.equal(clean['point.lat'], +1.1, 'not changed');
t.equal(clean['point.lon'], -1.1, 'not changed');
t.end();
});
test('positive longitude wrapping', function (t) {
var clean = {};
var params = {
'point.lat': +1.1,
'point.lon': +181.1
};
sanitize.sanitize_point( 'point', clean, params, false );
t.equal(clean['point.lat'], +1.1, 'not changed');
t.equal(clean['point.lon'], -178.9, 'equal to (-180 + 1.1)');
t.end();
});
test('negative longitude wrapping', function (t) {
var clean = {};
var params = {
'point.lat': -1.1,
'point.lon': -181.1
};
sanitize.sanitize_point( 'point', clean, params, false );
t.equal(clean['point.lat'], -1.1, 'not changed');
t.equal(clean['point.lon'], +178.9, 'equal to (+180 - 1.1)');
t.end();
});
test('positive latitudinal wrapping', function (t) {
var clean = {};
var params = {
'point.lat': 91.1,
'point.lon': 1.1
};
sanitize.sanitize_point( 'point', clean, params, false );
t.equal(clean['point.lat'], +88.9, 'equal to (+90 - 1.1)');
t.equal(clean['point.lon'], -178.9, 'equal to (-180 + 1.1)'); // polar flip
t.end();
});
test('negative latitudinal wrapping', function (t) {
var clean = {};
var params = {
'point.lat': -91.1,
'point.lon': -1.1
};
sanitize.sanitize_point( 'point', clean, params, false );
t.equal(clean['point.lat'], -88.9, 'equal to (-90 + 1.1)');
t.equal(clean['point.lon'], +178.9, 'equal to (+180 - 1.1)'); // polar flip
t.end();
});
};
module.exports.tests.coord = function(test, common) {
test('valid coord', function (t) {
var clean = {};

2
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();

2
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();

191
test/unit/sanitiser/wrap.js

@ -0,0 +1,191 @@
var wrap = require('../../../sanitiser/wrap');
module.exports.tests = {};
module.exports.tests.control = 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();
});
};
module.exports.tests.latitude_positive = function(test, common) {
test('positive latitude wrapping - 1 degree', function (t) {
var norm = wrap(1, 0);
t.equal(norm.lat, 1);
t.equal(norm.lon, 0);
t.end();
});
test('positive latitude wrapping - 91 degrees', function (t) {
var norm = wrap(91, 0);
t.equal(norm.lat, 89);
t.equal(norm.lon, 180);
t.end();
});
test('positive latitude wrapping - 181 degrees', function (t) {
var norm = wrap(181, 0);
t.equal(norm.lat, -1);
t.equal(norm.lon, 180);
t.end();
});
test('positive latitude wrapping - 271 degrees', function (t) {
var norm = wrap(271, 0);
t.equal(norm.lat, -89);
t.equal(norm.lon, 0);
t.end();
});
test('positive latitude wrapping - 361 degrees', function (t) {
var norm = wrap(361, 0);
t.equal(norm.lat, 1);
t.equal(norm.lon, 0);
t.end();
});
test('positive latitude wrapping - 631 degrees', function (t) {
var norm = wrap(631, 0);
t.equal(norm.lat, -89);
t.equal(norm.lon, 0);
t.end();
});
test('positive latitude wrapping - 721 degrees', function (t) {
var norm = wrap(721, 0);
t.equal(norm.lat, 1);
t.equal(norm.lon, 0);
t.end();
});
};
module.exports.tests.latitude_negative = function(test, common) {
test('negative latitude wrapping - 1 degree', function (t) {
var norm = wrap(-1, 0);
t.equal(norm.lat, -1);
t.equal(norm.lon, 0);
t.end();
});
test('negative latitude wrapping - 91 degrees', function (t) {
var norm = wrap(-91, 0);
t.equal(norm.lat, -89);
t.equal(norm.lon, 180);
t.end();
});
test('negative latitude wrapping - 181 degrees', function (t) {
var norm = wrap(-181, 0);
t.equal(norm.lat, 1);
t.equal(norm.lon, 180);
t.end();
});
test('negative latitude wrapping - 271 degrees', function (t) {
var norm = wrap(-271, 0);
t.equal(norm.lat, 89);
t.equal(norm.lon, 0);
t.end();
});
test('negative latitude wrapping - 361 degrees', function (t) {
var norm = wrap(-361, 0);
t.equal(norm.lat, -1);
t.equal(norm.lon, 0);
t.end();
});
test('negative latitude wrapping - 631 degrees', function (t) {
var norm = wrap(-631, 0);
t.equal(norm.lat, 89);
t.equal(norm.lon, 0);
t.end();
});
test('positive latitude wrapping - 721 degrees', function (t) {
var norm = wrap(721, 0);
t.equal(norm.lat, 1);
t.equal(norm.lon, 0);
t.end();
});
};
module.exports.tests.longitude_positive = function(test, common) {
test('positive longitude wrapping - 1 degree', function (t) {
var norm = wrap(0, 1);
t.equal(norm.lat, 0);
t.equal(norm.lon, 1);
t.end();
});
test('positive longitude wrapping - 181 degrees', function (t) {
var norm = wrap(0, 181);
t.equal(norm.lat, 0);
t.equal(norm.lon, -179);
t.end();
});
test('positive longitude wrapping - 271 degrees', function (t) {
var norm = wrap(0, 271);
t.equal(norm.lat, 0);
t.equal(norm.lon, -89);
t.end();
});
test('positive longitude wrapping - 361 degrees', function (t) {
var norm = wrap(0, 361);
t.equal(norm.lat, 0);
t.equal(norm.lon, 1);
t.end();
});
test('positive longitude wrapping - 631 degrees', function (t) {
var norm = wrap(0, 631);
t.equal(norm.lat, 0);
t.equal(norm.lon, -89);
t.end();
});
test('positive longitude wrapping - 721 degrees', function (t) {
var norm = wrap(0, 721);
t.equal(norm.lat, 0);
t.equal(norm.lon, 1);
t.end();
});
};
module.exports.tests.longitude_negative = function(test, common) {
test('negative longitude wrapping - 1 degree', function (t) {
var norm = wrap(0, -1);
t.equal(norm.lat, 0);
t.equal(norm.lon, -1);
t.end();
});
test('negative longitude wrapping - 181 degrees', function (t) {
var norm = wrap(0, -181);
t.equal(norm.lat, 0);
t.equal(norm.lon, 179);
t.end();
});
test('negative longitude wrapping - 271 degrees', function (t) {
var norm = wrap(0, -271);
t.equal(norm.lat, 0);
t.equal(norm.lon, 89);
t.end();
});
test('negative longitude wrapping - 361 degrees', function (t) {
var norm = wrap(0, -361);
t.equal(norm.lat, 0);
t.equal(norm.lon, -1);
t.end();
});
test('negative longitude wrapping - 631 degrees', function (t) {
var norm = wrap(0, -631);
t.equal(norm.lat, 0);
t.equal(norm.lon, 89);
t.end();
});
test('negative longitude wrapping - 721 degrees', function (t) {
var norm = wrap(0, -721);
t.equal(norm.lat, 0);
t.equal(norm.lon, -1);
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);
}
};
Loading…
Cancel
Save