-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(isValid3DBoundingBox): add new matcher
Verifies a three dimensional bounding box meets WGS-84 and GeoJSON validity requirements. Resolves: #7
- Loading branch information
1 parent
3ee9e3e
commit 6ee8cc6
Showing
9 changed files
with
337 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
const { valid2DBoundingBox } = require('./valid2DBoundingBox') | ||
/** | ||
* Verifies a three dimensional bounding box meets WGS-84 and GeoJSON validity requirements. | ||
* | ||
* @memberof Core.BoundingBoxes | ||
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/7 | ||
* @param {GeoJSON-bbox} bboxArray A six element WGS-84 array of numbers in format [west, south, depth, east, north, altitude]. | ||
* @returns {boolean} True if a valid 3D GeoJSON coordinate. If invalid, it will throw an error. | ||
* @throws {Error} Input must be an array of only four elements of type number | ||
* @throws {Error} Depth and altitude must be numeric. | ||
* @throws {RangeError} Longitude must be a number between -180 and 180 | ||
* @throws {RangeError} Latitude must be a number between -90 and 90 | ||
* @throws {RangeError} North must be greater than or equal to south | ||
* @throws {RangeError} Altitude must be greater than or equal to depth | ||
* @example | ||
* const goodBBox = valid3DBoundingBox([-10, -20, -100, 20, 10, 0]) // true | ||
* const crossesAntimeridian = valid3DBoundingBox([170, -20, -22.5, 20, -170, 12345.678]) // true | ||
* @example | ||
* const badExample1 valid3DBoundingBox([-10, -91, 0, 10, 20, 0]) // throws error for south being out of range | ||
* const badExample2 valid3DBoundingBox([-10, -10, "0", 10, 20, 0]) // throws error for non-numeric value | ||
*/ | ||
function valid3DBoundingBox(bboxArray) { | ||
if (!Array.isArray(bboxArray) || bboxArray.length !== 6) { | ||
throw new Error('Bounding box must be an array of only six elments.') | ||
} | ||
|
||
// Reuse functionality from 2D bounding box. The lat/lon values must satisfy the same criteria | ||
valid2DBoundingBox([bboxArray[0], bboxArray[1], bboxArray[3], bboxArray[4]]) | ||
|
||
if ( | ||
typeof bboxArray[2] !== 'number' || | ||
typeof bboxArray[5] !== 'number' || | ||
// eslint-disable-next-line no-self-compare | ||
bboxArray[2] !== bboxArray[2] || // Accounts for NaN | ||
// eslint-disable-next-line no-self-compare | ||
bboxArray[5] !== bboxArray[5] // Accounts for NaN | ||
) { | ||
throw new Error('Northern value must be a number between -90 and 90.') | ||
} | ||
|
||
if (bboxArray[5] < bboxArray[2]) { | ||
throw new Error('Altitude value must be greater than depth.') | ||
} | ||
|
||
return true | ||
} | ||
|
||
exports.valid3DBoundingBox = valid3DBoundingBox |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
const { valid3DBoundingBox } = require('../../core/boundingBoxes/valid3DBoundingBox') | ||
|
||
// eslint-disable-next-line jsdoc/require-returns | ||
/** | ||
* Verifies a three dimensional bounding box meets WGS-84 and GeoJSON validity requirements. | ||
* | ||
* @memberof BoundingBoxes | ||
* @see https://github.com/M-Scott-Lassiter/jest-geojson/issues/7 | ||
* @param {number[]} bboxArray A six element WGS-84 array of numbers in format [west, south, depth, east, north, altitude]. | ||
* Longitude must be between -180 to 180. | ||
* Latitude must be between -90 to 90. | ||
* North must be greater than or equal to south. | ||
* Altitude must be greater than or equal to depth. | ||
* Bounding boxes that cross the antimeridian will have an eastern value less than the western value. | ||
* @example | ||
* expect([-10, -20, -100, 20, 10, 0]).isValid3DBoundingBox() | ||
* expect([170, -20, -22.5, 20, -170, 12345.678]).isValid3DBoundingBox() // Crosses antimeridian | ||
* @example | ||
* expect([-10, -91, 0, 10, 20, 0]).not.isValid3DBoundingBox() // south out of range | ||
* expect([-20, 10, -10, 20]).not.isValid3DBoundingBox() //2D bounding box | ||
* expect([-10, -10, "0", 10, 20, 0]).not.isValid2DBoundingBox() // Non-numeric value | ||
*/ | ||
function isValid3DBoundingBox(bboxArray) { | ||
const { printReceived, matcherHint } = this.utils | ||
const passMessage = | ||
// eslint-disable-next-line prefer-template | ||
matcherHint( | ||
'.not.isValid2DBoundingBox', | ||
'[west, south, depth, east, north, altitude]', | ||
'' | ||
) + | ||
'\n\n' + | ||
`Expected input to not be a six element array of numbers with longitude between (-90 to 90), | ||
latitude between (-180 to 180), northern boundary greater than southern boundary, | ||
and altitude greater than depth.\n\n` + | ||
`Received: ${printReceived(bboxArray)}` | ||
|
||
/** | ||
* Combines a custom error message with built in Jest tools to provide a more descriptive error | ||
* meessage to the end user. | ||
* | ||
* @param {string} errorMessage Error message text to return to the user | ||
* @returns {string} Concatenated Jest test result string | ||
*/ | ||
function failMessage(errorMessage) { | ||
return ( | ||
// eslint-disable-next-line prefer-template, no-unused-expressions | ||
matcherHint( | ||
'.isValid2DBoundingBox', | ||
'[west, south, depth, east, north, altitude]', | ||
'' | ||
) + | ||
'\n\n' + | ||
`${errorMessage}\n\n` + | ||
`Received: ${printReceived(bboxArray)}` | ||
) | ||
} | ||
|
||
try { | ||
valid3DBoundingBox(bboxArray) | ||
} catch (err) { | ||
return { pass: false, message: () => failMessage(err.message) } | ||
} | ||
return { pass: true, message: () => passMessage } | ||
} | ||
|
||
exports.isValid3DBoundingBox = isValid3DBoundingBox |
19 changes: 19 additions & 0 deletions
19
tests/boundingBoxes/__snapshots__/isValid3DBoundingBox.test.js.snap
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`Error Snapshot Testing. Throws error: expect([10, 10, 0, 20, 20, 0]).not.isValid3DBoundingBox 1`] = ` | ||
"[2mexpect([22m[31m[west, south, depth, east, north, altitude][39m[2m).not.isValid2DBoundingBox()[22m | ||
Expected input to not be a six element array of numbers with longitude between (-90 to 90), | ||
latitude between (-180 to 180), northern boundary greater than southern boundary, | ||
and altitude greater than depth. | ||
Received: [31m[10, 10, 0, 20, 20, 0][39m" | ||
`; | ||
|
||
exports[`Error Snapshot Testing. Throws error: expect(false).isValid3DBoundingBox() 1`] = ` | ||
"[2mexpect([22m[31m[west, south, depth, east, north, altitude][39m[2m).isValid2DBoundingBox()[22m | ||
Bounding box must be an array of only six elments. | ||
Received: [31mfalse[39m" | ||
`; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
const goodBBoxes = [ | ||
[[-20, 10, 0, -10, 20, 0]], | ||
[[10, 10, 0, 20, 20, 0]], | ||
[[-20, -20, 0, -10, -10, 0]], | ||
[[10, -20, 0, 20, -10, 0]], | ||
[[-10, -20, 0, 20, -10, 0]], | ||
[[-10, -20, 0, 20, 10, 0]], | ||
[[170, -20, 0, -170, 20, 0]], | ||
[[-10, -20, -100, 20, 10, 0]], | ||
[[-10, -20, -500, 20, 10, -50]], | ||
[[170, -20, 0, -170, 20, 100]], | ||
[[-10, -20, 50, 20, 10, 500]], | ||
[[-10, -20, -22.5, 20, 10, 12345.678]] | ||
] | ||
const goodBoundaryCoordinates = [ | ||
[[-180, 10, 0, 20, 20, 0]], | ||
[[10, 10, 0, 180, 20, 0]], | ||
[[-180, 80, 0, 180, 90, 0]], | ||
[[-180, -90, 0, 180, -80, 0]], | ||
[[10, 80, 0, 20, 90, 0]], | ||
[[-45, -90, 0, -80, -80, 0]], | ||
[[-180, 10, 0, 180, 20, 0]], | ||
[[-10, -90, 0, 10, 90, 0]], | ||
[[-180, -90, 0, 180, 90, 0]], | ||
[[-180, -90, -100000, 180, 90, 100000]], | ||
[[-10, -20, 0, 10, -20, 0]], | ||
[[-10, 20, 0, 10, 20, 0]], | ||
[[-10, -20, 0, -10, 20, 0]], | ||
[[10, -20, 0, 10, 20, 0]], | ||
[[-10, -20, -220, -10, 20, -220]], | ||
[[-10, -20, 330, -10, 20, 330]], | ||
[[0, 0, 0, 0, 0, 0]] | ||
] | ||
const coordinatesOutOfRange = [ | ||
[[-10, -90.0000001, 0, 10, 0, 0]], | ||
[[-10, 0, 0, 10, 90.0000001, 0]], | ||
[[-10, -90000, 0, 10, 0, 0]], | ||
[[-10, 0, 0, 10, 90000, 0]], | ||
[[-180.0000001, -10, 0, -160, 10, 0]], | ||
[[160, -10, 0, 180.0000001, 10, 0]], | ||
[[-1800000, -10, 0, -160, 10, 0]], | ||
[[160, -10, 0, 1800000, 10, 0]], | ||
[[-181, -10, 0, 181, 10, 0]], | ||
[[-10, -91, 0, 10, 91, 0]], | ||
[[-181, -91, 0, 10, 10, 0]], | ||
[[-10, -10, 0, 181, 91, 0]], | ||
[[-181, -91, 0, 181, 91, 0]] | ||
] | ||
const invalidInputValues = [ | ||
undefined, | ||
null, | ||
true, | ||
false, | ||
200, | ||
-200, | ||
Infinity, | ||
-Infinity, | ||
NaN, | ||
{ coordinates: [0, 0] }, | ||
'', | ||
'Random Coordinate', | ||
'[10, 10, 0, 20, 20, 0]' | ||
] | ||
const invalidAltitudeValues = [ | ||
undefined, | ||
null, | ||
true, | ||
false, | ||
NaN, | ||
{ coordinates: [0, 0] }, | ||
'', | ||
'Random Coordinate', | ||
'[10, 10, 0, 20, 20, 0]' | ||
] | ||
|
||
describe('Valid Use Cases', () => { | ||
describe('Expect to pass with good coordinates:', () => { | ||
test.each([...goodBBoxes])('expect(%p)', (bboxArray) => { | ||
expect(bboxArray).isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to pass with good boundary coordinates:', () => { | ||
test.each([...goodBoundaryCoordinates])('expect(%p)', (bboxArray) => { | ||
expect(bboxArray).isValid3DBoundingBox() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('Inalid Use Cases', () => { | ||
describe('Expect to fail with bad inputs:', () => { | ||
test.each([...invalidInputValues])('expect(%p)', (badInput) => { | ||
expect(badInput).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with incorrect number of array elements:', () => { | ||
test.each([ | ||
[[]], | ||
[[20]], | ||
[[20, 10]], | ||
[[20, 30, 0]], | ||
[[-10, 30, -5, 40]], | ||
[[20, 30, 0, 20, 30]], | ||
[[20, 30, 0, 20, 30, 0, 2]], | ||
[[20, 30, 0, 20, 30, 0, 20, 30, 0]] | ||
])('expect(%p)', (badInput) => { | ||
expect(badInput).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with out of range coordinate:', () => { | ||
test.each([...coordinatesOutOfRange])('expect(%p)', (coordinate) => { | ||
expect(coordinate).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with illogical BBox:', () => { | ||
test('Northern boundary less than southern: expect([-10, 20, 0, 10, -20, 0])', () => { | ||
expect([-10, 20, 0, 10, -20, 0]).not.isValid3DBoundingBox() | ||
}) | ||
|
||
test('Altitude less than depth: expect([-10, -20, 200, 20, 10, 150])', () => { | ||
expect([-10, -20, 200, 20, 10, 150]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Passing Bad Individual Coordinate Values', () => { | ||
describe('Expect to fail with bad western value:', () => { | ||
test.each([...invalidInputValues])('expect([%p, -10, 0, 10, 10, 0])', (input) => { | ||
expect([input, -10, 0, 10, 10, 0]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad southern value:', () => { | ||
test.each([...invalidInputValues])('expect([-10, %p, 0, 10, 10, 0])', (input) => { | ||
expect([-10, input, 0, 10, 10, 0]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad depth value:', () => { | ||
test.each([...invalidAltitudeValues])('expect([-10, -10, %p, 10, 10, 0])', (input) => { | ||
expect([-10, -10, input, 10, 10, 0]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad eastern value:', () => { | ||
test.each([...invalidInputValues])('expect([-10, -10, 0, %p, 10, 0])', (input) => { | ||
expect([-10, -10, 0, input, 10, 0]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad northern value:', () => { | ||
test.each([...invalidInputValues])('expect([-10, -10, 0, 10, %p, 0])', (input) => { | ||
expect([-10, -10, 0, 10, input, 0]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad altitude value:', () => { | ||
test.each([...invalidAltitudeValues])('expect([-10, -10, 0, 10, 10, %p])', (input) => { | ||
expect([-10, -10, 0, 10, 10, input]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
|
||
describe('Expect to fail with bad inputs for all values:', () => { | ||
test.each([...invalidInputValues])('expect([%p, %p, %p, %p, %p, %p])', (input) => { | ||
expect([input, input, input, input, input, input]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('Expect to fail when BBox values are arrays of otherwise valid numbers:', () => { | ||
test('expect([[-20], [10], [0], [-10], [20], [0]])', () => { | ||
expect([[-20], [10], [0], [-10], [20], [0]]).not.isValid3DBoundingBox() | ||
}) | ||
}) | ||
}) | ||
|
||
describe('Error Snapshot Testing. Throws error:', () => { | ||
test('expect([10, 10, 0, 20, 20, 0]).not.isValid3DBoundingBox', () => { | ||
expect(() => | ||
expect([10, 10, 0, 20, 20, 0]).not.isValid3DBoundingBox() | ||
).toThrowErrorMatchingSnapshot() | ||
}) | ||
|
||
test('expect(false).isValid3DBoundingBox()', () => { | ||
expect(() => expect(false).isValid3DBoundingBox()).toThrowErrorMatchingSnapshot() | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters