From 4d08689aa5fe431a81fb9217f16cd825f4173bfa Mon Sep 17 00:00:00 2001 From: Rich Gwozdz Date: Thu, 15 Oct 2020 07:51:00 -0700 Subject: [PATCH] Refactor SQL filtering (#172) * Breakout geometry filters. * Fix envelope-intersects --- packages/winnow/CHANGELOG.md | 7 + .../filter-and-transform.js | 63 +------- .../filter-and-transform/filters/contains.js | 8 + .../filters/envelope-intersects.js | 30 ++++ .../filters/hashed-objectid-comparator.js | 24 +++ .../lib/filter-and-transform/filters/index.js | 7 + .../filters/intersects.js | 16 ++ .../filter-and-transform/filters/within.js | 9 ++ .../helpers/create-integer-hash.js | 8 + .../{ => helpers}/hash-fixture.json | 0 .../{ => helpers}/hash-function.js | 0 .../filters/constains.spec.js | 68 ++++++++ .../filters/envelope-intersects.spec.js | 149 ++++++++++++++++++ .../hashed-objectid-comparator.spec.js | 43 +++++ .../filters/intersects.spec.js | 77 +++++++++ .../filters/within.spec.js | 68 ++++++++ .../hash-function.spec.js | 2 +- 17 files changed, 523 insertions(+), 56 deletions(-) create mode 100644 packages/winnow/lib/filter-and-transform/filters/contains.js create mode 100644 packages/winnow/lib/filter-and-transform/filters/envelope-intersects.js create mode 100644 packages/winnow/lib/filter-and-transform/filters/hashed-objectid-comparator.js create mode 100644 packages/winnow/lib/filter-and-transform/filters/index.js create mode 100644 packages/winnow/lib/filter-and-transform/filters/intersects.js create mode 100644 packages/winnow/lib/filter-and-transform/filters/within.js create mode 100644 packages/winnow/lib/filter-and-transform/helpers/create-integer-hash.js rename packages/winnow/lib/filter-and-transform/{ => helpers}/hash-fixture.json (100%) rename packages/winnow/lib/filter-and-transform/{ => helpers}/hash-function.js (100%) create mode 100644 packages/winnow/test/unit/filter-and-transform/filters/constains.spec.js create mode 100644 packages/winnow/test/unit/filter-and-transform/filters/envelope-intersects.spec.js create mode 100644 packages/winnow/test/unit/filter-and-transform/filters/hashed-objectid-comparator.spec.js create mode 100644 packages/winnow/test/unit/filter-and-transform/filters/intersects.spec.js create mode 100644 packages/winnow/test/unit/filter-and-transform/filters/within.spec.js diff --git a/packages/winnow/CHANGELOG.md b/packages/winnow/CHANGELOG.md index 5e993666a..c7f4c7fbd 100644 --- a/packages/winnow/CHANGELOG.md +++ b/packages/winnow/CHANGELOG.md @@ -2,6 +2,13 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). +## Unreleased +### Fixed +* Fixes a bug in the "envelope-intersects" operation. Research indicates that the Esri envelope-intersects operation should check that an "Envelope of Query Geometry Intersects Envelope of Target Geometry" (see [here](http://resources.esri.com/help/9.3/ArcGISDesktop/ArcObjects/esriGeoDatabase/esriSpatialRelEnum.htm)). + +### Added +* Adds support for the `!=` operator in the hashed OBJECTID comparison. + ## [2.1.0] - 10-06-2020 ### Added * Use environment variable to force javascript hashing of feature for OBJECTID. OBJECTID_FEATURE_HASH=javascript diff --git a/packages/winnow/lib/filter-and-transform/filter-and-transform.js b/packages/winnow/lib/filter-and-transform/filter-and-transform.js index e0a944380..8bfe525de 100644 --- a/packages/winnow/lib/filter-and-transform/filter-and-transform.js +++ b/packages/winnow/lib/filter-and-transform/filter-and-transform.js @@ -1,48 +1,24 @@ -const { - within, - contains, - intersects, - calculateBounds -} = require('@terraformer/spatial') +const { within, contains, intersects, envelopeIntersects, hashedObjectIdComparator } = require('./filters') +const createIntHash = require('./helpers/create-integer-hash') const convertToEsri = require('../geometry/convert-to-esri') -const convertFromEsri = require('../geometry/transfrom-esri-geometry-to-geojson-geometry') -const transformArray = require('../geometry/transform-coordinate-array-to-polygon') const sql = require('alasql') const geohash = require('ngeohash') const centroid = require('@turf/centroid').default const _ = require('lodash') const projectCoordinates = require('../geometry/project-coordinates') const reducePrecision = require('../geometry/reduce-precision') -const hashFunction = require('./hash-function') sql.MAXSQLCACHESIZE = 0 -sql.fn.ST_Within = function (feature = {}, filterGeom = {}) { - if (!(feature && feature.type && feature.coordinates && feature.coordinates.length > 0)) return false - return within(feature, filterGeom) -} +sql.fn.ST_Within = within -sql.fn.ST_Contains = function (feature = {}, filterGeom = {}) { - if (!(feature && feature.type && feature.coordinates && feature.coordinates.length > 0)) return false - return contains(filterGeom, feature) -} +sql.fn.ST_Contains = contains -sql.fn.ST_Intersects = function (feature = {}, filterGeom = {}) { - if (!feature) return false - if (!(feature.type || feature.coordinates)) feature = convertFromEsri(feature) // TODO: remove ? temporary esri geometry conversion - if (!(feature.type && feature.coordinates && feature.coordinates.length > 0)) return false - if (feature.type === 'Point') return sql.fn.ST_Contains(feature, filterGeom) - return intersects(filterGeom, feature) -} +sql.fn.ST_Intersects = intersects -sql.fn.ST_EnvelopeIntersects = function (feature = {}, filterGeom = {}) { - if (!feature) return false - if (!(feature.type || feature.coordinates)) feature = convertFromEsri(feature) // TODO: remove ? temporary esri geometry conversion - if (!(feature.type && feature.coordinates && feature.coordinates.length > 0)) return false - if (feature.type === 'Point') return sql.fn.ST_Contains(feature, filterGeom) - const envelope = transformArray(calculateBounds(feature)) - return intersects(filterGeom, envelope) -} +sql.fn.ST_EnvelopeIntersects = envelopeIntersects + +sql.fn.hashedObjectIdComparator = hashedObjectIdComparator sql.fn.geohash = function (geometry = {}, precision) { if (!geometry || !geometry.type || !geometry.coordinates) return @@ -142,27 +118,4 @@ function esriFy (properties, geometry, dateFields, requiresObjectId, idField) { return properties } -/** - * - */ -sql.fn.hashedObjectIdComparator = function (properties, geometry, objectId, operator) { - const hash = createIntHash(JSON.stringify({ properties, geometry })) - if (operator === '=' && hash === objectId) return true - else if (operator === '>' && hash > objectId) return true - else if (operator === '<' && hash < objectId) return true - else if (operator === '>=' && hash >= objectId) return true - else if (operator === '<=' && hash <= objectId) return true - return false -} - -/** - * Create integer hash in range of 0 - 2147483647 from string - * @param {*} inputStr - any string - */ -function createIntHash (inputStr) { - // Hash to 32 bit unsigned integer - const hash = hashFunction(inputStr) - // Normalize to range of postive values of signed integer - return Math.round((hash / 4294967295) * (2147483647)) -} module.exports = sql diff --git a/packages/winnow/lib/filter-and-transform/filters/contains.js b/packages/winnow/lib/filter-and-transform/filters/contains.js new file mode 100644 index 000000000..48b2f601e --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/contains.js @@ -0,0 +1,8 @@ +const _ = require('lodash') +const { contains } = require('@terraformer/spatial') +module.exports = function (featureGeometry = {}, filterGeometry = {}) { + if (_.isEmpty(featureGeometry)) return false + const { type, coordinates = [] } = featureGeometry + if (!type || !coordinates || coordinates.length === 0) return false + return contains(filterGeometry, featureGeometry) +} diff --git a/packages/winnow/lib/filter-and-transform/filters/envelope-intersects.js b/packages/winnow/lib/filter-and-transform/filters/envelope-intersects.js new file mode 100644 index 000000000..62d111473 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/envelope-intersects.js @@ -0,0 +1,30 @@ +const _ = require('lodash') +const { calculateBounds, intersects, contains } = require('@terraformer/spatial') +const transformArray = require('../../geometry/transform-coordinate-array-to-polygon') +const convertFromEsri = require('../../geometry/transfrom-esri-geometry-to-geojson-geometry') + +module.exports = function (featureGeometry = {}, filterGeometry = {}) { + if (_.isEmpty(featureGeometry) || _.isEmpty(filterGeometry)) return false + + const normalizedFeatureGeometry = isGeoJsonGeometry(featureGeometry) ? featureGeometry : convertFromEsri(featureGeometry) + + const { type, coordinates = [] } = normalizedFeatureGeometry + + if (!type || coordinates.length === 0) return false + + const geometryFilterEnvelope = convertGeometryToEnvelope(filterGeometry) + + if (type === 'Point') return contains(geometryFilterEnvelope, normalizedFeatureGeometry) + + const featureEnvelope = convertGeometryToEnvelope(normalizedFeatureGeometry) + return intersects(geometryFilterEnvelope, featureEnvelope) +} + +function convertGeometryToEnvelope (geometry) { + const bounds = calculateBounds(geometry) + return transformArray(bounds) +} + +function isGeoJsonGeometry ({ type, coordinates }) { + return type && coordinates +} diff --git a/packages/winnow/lib/filter-and-transform/filters/hashed-objectid-comparator.js b/packages/winnow/lib/filter-and-transform/filters/hashed-objectid-comparator.js new file mode 100644 index 000000000..d4a89b483 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/hashed-objectid-comparator.js @@ -0,0 +1,24 @@ +const createIntegerHash = require('../helpers/create-integer-hash') + +/** + * This function is used when the where option includes an OBJECTID, but the data + * contains no such property. In such cases, it is assumed that the client has been + * leveraging winnow's "esriFy" feature that creates OBJECTID on the fly by + * doing a numeric hash of a feature. In order to filter by OBJECTID, we have recreate + * the numeric hash on the fly and compare it to the passed in OBJECTID. + * + * @param {object} properties GeoJSON feature properties + * @param {*} geometry GeoJSON feature properties + * @param {*} objectId the objectId the feature is being compared to. Presumed to have been created by feature hashing + * @param {*} operator the predicate operator + */ +module.exports = function (properties, geometry, objectId, operator) { + const hashedFeature = createIntegerHash(JSON.stringify({ properties, geometry })) + if (operator === '=' && hashedFeature === objectId) return true + if (operator === '!=' && hashedFeature !== objectId) return true + if (operator === '>' && hashedFeature > objectId) return true + if (operator === '<' && hashedFeature < objectId) return true + if (operator === '>=' && hashedFeature >= objectId) return true + if (operator === '<=' && hashedFeature <= objectId) return true + return false +} diff --git a/packages/winnow/lib/filter-and-transform/filters/index.js b/packages/winnow/lib/filter-and-transform/filters/index.js new file mode 100644 index 000000000..8bdfd0c00 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/index.js @@ -0,0 +1,7 @@ +module.exports = { + within: require('./within'), + contains: require('./contains'), + intersects: require('./intersects'), + envelopeIntersects: require('./envelope-intersects'), + hashedObjectIdComparator: require('./hashed-objectid-comparator') +} diff --git a/packages/winnow/lib/filter-and-transform/filters/intersects.js b/packages/winnow/lib/filter-and-transform/filters/intersects.js new file mode 100644 index 000000000..561388f17 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/intersects.js @@ -0,0 +1,16 @@ +const _ = require('lodash') +const { intersects, contains } = require('@terraformer/spatial') +const convertFromEsri = require('../../geometry/transfrom-esri-geometry-to-geojson-geometry') + +module.exports = function (featureGeometry = {}, filterGeometry = {}) { + if (_.isEmpty(featureGeometry)) return false + const geometry = isGeoJsonGeometry(featureGeometry) ? featureGeometry : convertFromEsri(featureGeometry) + const { type, coordinates = [] } = geometry + if (!type || !coordinates || coordinates.length === 0) return false + if (type === 'Point') return contains(filterGeometry, geometry) + return intersects(filterGeometry, geometry) +} + +function isGeoJsonGeometry ({ type, coordinates }) { + return type && coordinates +} diff --git a/packages/winnow/lib/filter-and-transform/filters/within.js b/packages/winnow/lib/filter-and-transform/filters/within.js new file mode 100644 index 000000000..9a4c955a4 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/filters/within.js @@ -0,0 +1,9 @@ +const _ = require('lodash') +const { within } = require('@terraformer/spatial') + +module.exports = function (featureGeometry, filterGeometry = {}) { + if (_.isEmpty(featureGeometry)) return false + const { type, coordinates = [] } = featureGeometry + if (!type || !coordinates || coordinates.length === 0) return false + return within(featureGeometry, filterGeometry) +} diff --git a/packages/winnow/lib/filter-and-transform/helpers/create-integer-hash.js b/packages/winnow/lib/filter-and-transform/helpers/create-integer-hash.js new file mode 100644 index 000000000..6edc57680 --- /dev/null +++ b/packages/winnow/lib/filter-and-transform/helpers/create-integer-hash.js @@ -0,0 +1,8 @@ +const hashFunction = require('./hash-function') + +module.exports = function createIntegerHash (inputStr) { + // Hash to 32 bit unsigned integer + const hash = hashFunction(inputStr) + // Normalize to range of postive values of signed integer + return Math.round((hash / 4294967295) * (2147483647)) +} diff --git a/packages/winnow/lib/filter-and-transform/hash-fixture.json b/packages/winnow/lib/filter-and-transform/helpers/hash-fixture.json similarity index 100% rename from packages/winnow/lib/filter-and-transform/hash-fixture.json rename to packages/winnow/lib/filter-and-transform/helpers/hash-fixture.json diff --git a/packages/winnow/lib/filter-and-transform/hash-function.js b/packages/winnow/lib/filter-and-transform/helpers/hash-function.js similarity index 100% rename from packages/winnow/lib/filter-and-transform/hash-function.js rename to packages/winnow/lib/filter-and-transform/helpers/hash-function.js diff --git a/packages/winnow/test/unit/filter-and-transform/filters/constains.spec.js b/packages/winnow/test/unit/filter-and-transform/filters/constains.spec.js new file mode 100644 index 000000000..6e9e5808f --- /dev/null +++ b/packages/winnow/test/unit/filter-and-transform/filters/constains.spec.js @@ -0,0 +1,68 @@ +const test = require('tape') +const contains = require('../../../../lib/filter-and-transform/filters/contains') + +test('contains: empty input', t => { + const result = contains() + t.equals(result, false) + t.end() +}) + +test('contains: empty object input', t => { + const result = contains({}, {}) + t.equals(result, false) + t.end() +}) + +test('contains: null input', t => { + const result = contains(null, {}) + t.equals(result, false) + t.end() +}) + +test('contains: null input', t => { + const result = contains({}, null) + t.equals(result, false) + t.end() +}) + +test('contains: missing geometry type', t => { + const result = contains({ coordinates: [44, 84] }, {}) + t.equals(result, false) + t.end() +}) + +test('contains: missing coordinates', t => { + const result = contains({ type: 'Point' }, {}) + t.equals(result, false) + t.end() +}) + +test('contains: missing empty coordinates', t => { + const result = contains({ type: 'Point', coordinates: [] }, {}) + t.equals(result, false) + t.end() +}) + +test('contains: missing filter geometry', t => { + const result = contains({ type: 'Point', coordinates: [44, -84.5] }) + t.equals(result, false) + t.end() +}) + +test('contains: true', t => { + const result = contains({ type: 'Point', coordinates: [44, -84.5] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) + +test('contains: false', t => { + const result = contains({ type: 'Point', coordinates: [0, 0] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, false) + t.end() +}) diff --git a/packages/winnow/test/unit/filter-and-transform/filters/envelope-intersects.spec.js b/packages/winnow/test/unit/filter-and-transform/filters/envelope-intersects.spec.js new file mode 100644 index 000000000..fb6ca378a --- /dev/null +++ b/packages/winnow/test/unit/filter-and-transform/filters/envelope-intersects.spec.js @@ -0,0 +1,149 @@ +const test = require('tape') +const envelopeIntersects = require('../../../../lib/filter-and-transform/filters/envelope-intersects') + +test('envelopeIntersects: empty input', t => { + const result = envelopeIntersects() + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: empty object input', t => { + const result = envelopeIntersects({}, {}) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: null input', t => { + const result = envelopeIntersects(null, {}) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: null input', t => { + const result = envelopeIntersects({}, null) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: missing geometry type', t => { + const result = envelopeIntersects({ coordinates: [44, 84] }, {}) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: missing coordinates', t => { + const result = envelopeIntersects({ type: 'Point' }, {}) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: missing empty coordinates', t => { + const result = envelopeIntersects({ type: 'Point', coordinates: [] }, {}) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: missing filter geometry', t => { + const result = envelopeIntersects({ type: 'Point', coordinates: [44, -84.5] }) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: Point inside polygon, true', t => { + const result = envelopeIntersects({ type: 'Point', coordinates: [44, -84.5] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) + +test('envelopeIntersects: LineString outside polygon, false', t => { + const result = envelopeIntersects({ type: 'LineString', coordinates: [[17.41, 52.22], [17.42, 52.22]] }, { + type: 'Polygon', + coordinates: [[[17.2, 52.2], [17.4, 52.2], [17.4, 52.3], [17.2, 52.3], [17.2, 52.2]]] + }) + t.equals(result, false) + t.end() +}) + +test('envelopeIntersects: Point inside polygon, Esri Geometry, true', t => { + const result = envelopeIntersects({ x: 44, y: -84.5 }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) + +test('envelopeIntersects: Point inside polygon envelope, true', t => { + const result = envelopeIntersects({ type: 'Point', coordinates: [17.505, 52.029] }, { + type: 'Polygon', + coordinates: [ + [ + [ + 17.52, + 52.037 + ], + [ + 17.50, + 52.02 + ], + [ + 17.551345825195312, + 52.01 + ], + [ + 17.56988525390625, + 52.02 + ], + [ + 17.538986206054688, + 52.03 + ], + [ + 17.52, + 52.037 + ] + ] + ] + }) + t.equals(result, true) + t.end() +}) + +test('envelopeIntersects: Point outside polygon envelope, false', t => { + const result = envelopeIntersects({ type: 'Point', coordinates: [20, 53] }, { + type: 'Polygon', + coordinates: [ + [ + [ + 17.52, + 52.037 + ], + [ + 17.50, + 52.02 + ], + [ + 17.551345825195312, + 52.01 + ], + [ + 17.56988525390625, + 52.02 + ], + [ + 17.538986206054688, + 52.03 + ], + [ + 17.52, + 52.037 + ] + ] + ] + }) + t.equals(result, false) + t.end() +}) diff --git a/packages/winnow/test/unit/filter-and-transform/filters/hashed-objectid-comparator.spec.js b/packages/winnow/test/unit/filter-and-transform/filters/hashed-objectid-comparator.spec.js new file mode 100644 index 000000000..4656c1671 --- /dev/null +++ b/packages/winnow/test/unit/filter-and-transform/filters/hashed-objectid-comparator.spec.js @@ -0,0 +1,43 @@ +const test = require('tape') +const hashedObjectIdComparator = require('../../../../lib/filter-and-transform/filters/hashed-objectid-comparator') +const createIntegerHash = require('../../../../lib/filter-and-transform/helpers/create-integer-hash') + +const properties = { foo: 'bar' } +const geometry = { type: 'Point', coordinates: [44, -84.5] } +const objectId = createIntegerHash(JSON.stringify({ properties, geometry })) + +test('hashedObjectIdComparator: =, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, objectId, '=') + t.equals(result, true) + t.end() +}) + +test('hashedObjectIdComparator: !=, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, -9999, '!=') + t.equals(result, true) + t.end() +}) + +test('hashedObjectIdComparator: >, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, 0, '>') + t.equals(result, true) + t.end() +}) + +test('hashedObjectIdComparator: >=, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, 0, '>=') + t.equals(result, true) + t.end() +}) + +test('hashedObjectIdComparator: >, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, 9999999999, '<') + t.equals(result, true) + t.end() +}) + +test('hashedObjectIdComparator: >=, should return true', t => { + const result = hashedObjectIdComparator(properties, geometry, 9999999999, '<=') + t.equals(result, true) + t.end() +}) diff --git a/packages/winnow/test/unit/filter-and-transform/filters/intersects.spec.js b/packages/winnow/test/unit/filter-and-transform/filters/intersects.spec.js new file mode 100644 index 000000000..1ba54810a --- /dev/null +++ b/packages/winnow/test/unit/filter-and-transform/filters/intersects.spec.js @@ -0,0 +1,77 @@ +const test = require('tape') +const intersects = require('../../../../lib/filter-and-transform/filters/intersects') + +test('intersects: empty input', t => { + const result = intersects() + t.equals(result, false) + t.end() +}) + +test('intersects: empty object input', t => { + const result = intersects({}, {}) + t.equals(result, false) + t.end() +}) + +test('intersects: null input', t => { + const result = intersects(null, {}) + t.equals(result, false) + t.end() +}) + +test('intersects: null input', t => { + const result = intersects({}, null) + t.equals(result, false) + t.end() +}) + +test('intersects: missing geometry type', t => { + const result = intersects({ coordinates: [44, 84] }, {}) + t.equals(result, false) + t.end() +}) + +test('intersects: missing coordinates', t => { + const result = intersects({ type: 'Point' }, {}) + t.equals(result, false) + t.end() +}) + +test('intersects: missing empty coordinates', t => { + const result = intersects({ type: 'Point', coordinates: [] }, {}) + t.equals(result, false) + t.end() +}) + +test('intersects: missing filter geometry', t => { + const result = intersects({ type: 'Point', coordinates: [44, -84.5] }) + t.equals(result, false) + t.end() +}) + +test('intersects: Point inside polygon, true', t => { + const result = intersects({ type: 'Point', coordinates: [44, -84.5] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) + +test('intersects: LineString outside polygon, false', t => { + const result = intersects({ type: 'LineString', coordinates: [[17.41, 52.22], [17.42, 52.22]] }, { + type: 'Polygon', + coordinates: [[[17.2, 52.2], [17.4, 52.2], [17.4, 52.3], [17.2, 52.3], [17.2, 52.2]]] + }) + t.equals(result, false) + t.end() +}) + +test('intersects: Point inside polygon, Esri Geometry, true', t => { + const result = intersects({ x: 44, y: -84.5 }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) diff --git a/packages/winnow/test/unit/filter-and-transform/filters/within.spec.js b/packages/winnow/test/unit/filter-and-transform/filters/within.spec.js new file mode 100644 index 000000000..f61e704ed --- /dev/null +++ b/packages/winnow/test/unit/filter-and-transform/filters/within.spec.js @@ -0,0 +1,68 @@ +const test = require('tape') +const within = require('../../../../lib/filter-and-transform/filters/within') + +test('within: empty input', t => { + const result = within() + t.equals(result, false) + t.end() +}) + +test('within: empty object input', t => { + const result = within({}, {}) + t.equals(result, false) + t.end() +}) + +test('within: null input', t => { + const result = within(null, {}) + t.equals(result, false) + t.end() +}) + +test('within: null input', t => { + const result = within({}, null) + t.equals(result, false) + t.end() +}) + +test('within: missing geometry type', t => { + const result = within({ coordinates: [44, 84] }, {}) + t.equals(result, false) + t.end() +}) + +test('within: missing coordinates', t => { + const result = within({ type: 'Point' }, {}) + t.equals(result, false) + t.end() +}) + +test('within: missing empty coordinates', t => { + const result = within({ type: 'Point', coordinates: [] }, {}) + t.equals(result, false) + t.end() +}) + +test('within: missing filter geometry', t => { + const result = within({ type: 'Point', coordinates: [44, -84.5] }) + t.equals(result, false) + t.end() +}) + +test('within: true', t => { + const result = within({ type: 'Point', coordinates: [44, -84.5] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, true) + t.end() +}) + +test('within: false', t => { + const result = within({ type: 'Point', coordinates: [0, 0] }, { + type: 'Polygon', + coordinates: [[[44, -85], [45, -85], [45, -84], [44, -84], [44, -85]]] + }) + t.equals(result, false) + t.end() +}) diff --git a/packages/winnow/test/unit/filter-and-transform/hash-function.spec.js b/packages/winnow/test/unit/filter-and-transform/hash-function.spec.js index f0aacfcb5..692a8711d 100644 --- a/packages/winnow/test/unit/filter-and-transform/hash-function.spec.js +++ b/packages/winnow/test/unit/filter-and-transform/hash-function.spec.js @@ -1,6 +1,6 @@ const test = require('tape') const proxyquire = require('proxyquire') -const modulePath = '../../../lib/filter-and-transform/hash-function' +const modulePath = '../../../lib/filter-and-transform/helpers/hash-function' const stub = { 'string-hash': () => { return 'string-hash' }, 'farmhash': { // eslint-disable-line