@ideditor/location-conflation
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -19,2 +19,16 @@ # What's New | ||
# 0.5.0 | ||
##### 2020-Aug-25 | ||
* :warning: Refactor - API now has: | ||
* `validateLocation` / `validateLocationSet` - fast, return stable ids | ||
* `resolveLocation` / `resolveLocationSet` - slower, resolve GeoJSON features | ||
* All functions now return similar result objects | ||
* :warning: Introduce strict / non-strict modes. Location-conflation defaults to "strict" mode now. | ||
* In strict mode, any invalid location or locationSet throws an error. | ||
* In non strict mode, invalid locations are ignored, and locationSets that include nothing are assumed to include the entire world. | ||
* Can change modes by calling `.strict(val)`, for example:<br/> | ||
`const loco = new LocationConflation(features).strict(false); // not strict` | ||
* Add tests and document everything | ||
# 0.4.0 | ||
@@ -31,3 +45,3 @@ ##### 2020-Aug-20 | ||
## 0.3.0 | ||
# 0.3.0 | ||
##### 2020-Feb-13 | ||
@@ -46,3 +60,3 @@ * Use uppercase Wikidata identifiers, lowercase geojson filenames | ||
## 0.2.0 | ||
# 0.2.0 | ||
##### 2020-Jan-15 | ||
@@ -52,4 +66,4 @@ * Ensure that feature results always have both `id` and `properties.id` | ||
## 0.1.0 | ||
# 0.1.0 | ||
##### 2020-Jan-13 | ||
* Initial release |
{ | ||
"name": "@ideditor/location-conflation", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"license": "ISC", | ||
@@ -5,0 +5,0 @@ "repository": "github:ideditor/location-conflation", |
197
README.md
@@ -18,3 +18,3 @@ [![npm version](https://badge.fury.io/js/%40ideditor%2Flocation-conflation.svg)](https://badge.fury.io/js/%40ideditor%2Flocation-conflation) | ||
You can define a *location set* as an Object with `include` and `exclude` properties: | ||
You can define a *locationSet* as an Object with `include` and `exclude` properties: | ||
```js | ||
@@ -57,3 +57,3 @@ let locationSet = { | ||
```html | ||
<script type="module" src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0/index.min.mjs"></script> | ||
<script type="module" src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0.5/index.min.mjs"></script> | ||
``` | ||
@@ -63,3 +63,3 @@ | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0/dist/index.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0.5/dist/index.min.js"></script> | ||
``` | ||
@@ -69,3 +69,3 @@ | ||
```html | ||
<script src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0/dist/index.es5.min.js"></script> | ||
<script src="https://cdn.jsdelivr.net/npm/@ideditor/location-conflation@0.5/dist/index.es5.min.js"></script> | ||
``` | ||
@@ -75,3 +75,3 @@ | ||
### Examples | ||
## Examples | ||
```js | ||
@@ -85,3 +85,4 @@ const LocationConflation = require('@ideditor/location-conflation'); | ||
```js | ||
let result = loco.resolveLocationSet({ include: ['039'] }); // 039 = Southern Europe | ||
let locationSet = { include: ['039'] }; // 039 = Southern Europe | ||
let result = loco.resolveLocationSet(locationSet); | ||
``` | ||
@@ -93,3 +94,4 @@ <img width="800px" alt="Southern Europe" src="https://raw.githubusercontent.com/ideditor/location-conflation/master/docs/images/example1.png"/> | ||
```js | ||
let result = loco.resolveLocationSet({ include: ['039','015'] }); // 015 = Northern Africa | ||
let locationSet = { include: ['039','015'] }; // 015 = Northern Africa | ||
let result = loco.resolveLocationSet(locationSet); | ||
``` | ||
@@ -101,3 +103,4 @@ <img width="800px" alt="Southern Europe and Northern Africa" src="https://raw.githubusercontent.com/ideditor/location-conflation/master/docs/images/example2.png"/> | ||
```js | ||
let result = loco.resolveLocationSet({ include: ['039','015'], exclude: ['eg','sd'] }); | ||
let locationSet = { include: ['039','015'], exclude: ['eg','sd'] }; | ||
let result = loco.resolveLocationSet(locationSet); | ||
``` | ||
@@ -114,15 +117,166 @@ <img width="800px" alt="Southern Europe and Northern Africa, excluding Egypt and Sudan" src="https://raw.githubusercontent.com/ideditor/location-conflation/master/docs/images/example3.png"/> | ||
| ||
### Other Fun facts | ||
* This library is a wrapper around | ||
* [country-coder](https://github.com/ideditor/country-coder) for world boundaries, and | ||
* [polygon-clipping](https://github.com/mfogel/polygon-clipping) for union/difference functions | ||
* Results contain an `area` property containing the approximate size of the feature in km²<br/>(This is helpful for sorting features) | ||
* Results contain a stable `id` in the form `+[included]-[excluded]`<br/>(e.g. "+[015,039]-[eg,sd]") | ||
* Results are cached, so if you ask for the same thing multiple times we don't repeat the expensive clipping operations. | ||
## API Reference | ||
* [constructor](#constructor) | ||
* [validateLocation](#validateLocation) | ||
* [validateLocationSet](#validateLocationSet) | ||
* [resolveLocation](#resolveLocation) | ||
* [resolveLocationSet](#resolveLocationSet) | ||
* [strict](#strict) | ||
* [stringify](#stringify) | ||
* [cache](#cache) | ||
| ||
#### Prerequisites | ||
<a name="constructor" href="#constructor">#</a> const <i>loco</i> = new <b>LocationConflation</b>(<i>featureCollection</i>) | ||
Constructs a new LocationConflation instance. | ||
Optionally pass a GeoJSON FeatureCollection of known features which can be used later as locations. | ||
Each feature *must* have a filename-like `id`, for example: `example.geojson` | ||
```js | ||
{ | ||
"type": "FeatureCollection" | ||
"features": [ | ||
{ | ||
"type": "Feature", | ||
"id": "example.geojson", | ||
"properties": { … }, | ||
"geometry": { … } | ||
} | ||
] | ||
} | ||
``` | ||
| ||
<a name="validateLocation" href="#validateLocation">#</a> <i>loco</i>.<b>validateLocation</b>(<i>location</i>) | ||
Validates a given location. The "locations" can be: | ||
* Points as `[longitude, latitude]` coordinate pairs. _Example: `[8.67039, 49.41882]`_ | ||
* Filenames for known `.geojson` features. _Example: `"de-hamburg.geojson"`_ | ||
* Strings recognized by the [country-coder library](https://github.com/ideditor/country-coder#readme). _Example: `"de"`_ | ||
If the location is valid, returns a result `Object` like: | ||
```js | ||
{ | ||
type: 'point', 'geojson', or 'countrycoder' | ||
location: the queried location | ||
id: the stable identifier for the feature | ||
} | ||
``` | ||
If the location is invalid, | ||
* _in strict mode_, throws an error | ||
* _in non-strict mode_, returns `null` | ||
| ||
<a name="validateLocationSet" href="#validateLocationSet">#</a> <i>loco</i>.<b>validateLocationSet</b>(<i>locationSet</i>) | ||
Validates a given locationSet. Pass a locationSet `Object` like: | ||
```js | ||
{ | ||
include: [ Array of locations ], | ||
exclude: [ Array of locations ] | ||
} | ||
``` | ||
If the locationSet is valid, returns a result `Object` like: | ||
```js | ||
{ | ||
type: 'locationset' | ||
locationSet: the queried locationSet | ||
id: the stable identifier for the feature | ||
} | ||
``` | ||
If the locationSet is invalid or contains any invalid locations, | ||
* _in strict mode_, throws an error | ||
* _in non-strict mode_, invalid locations are quietly ignored. A locationSet with nothing included will be considered valid, and will validate as if it were a locationSet covering the entire world. `{ type: 'locationset', locationSet: ['Q2'], id: +[Q2] }` | ||
| ||
<a name="resolveLocation" href="#resolveLocation">#</a> <i>loco</i>.<b>resolveLocation</b>(<i>location</i>) | ||
Resolves a given location into a GeoJSON feature. This is similar to [validateLocation](#validateLocation), but runs slower and includes the actual GeoJSON in the result. Results are cached, so if you ask for the same thing multiple times we don't repeat the expensive clipping operations. | ||
The returned GeoJSON feature will also have an `area` property containing the approximate size of the feature in km². (This is helpful for sorting features) | ||
If the location is valid, returns a result `Object` like: | ||
```js | ||
{ | ||
type: 'point', 'geojson', or 'countrycoder' | ||
location: the queried location | ||
id: the stable identifier for the feature | ||
feature: the resolved GeoJSON feature | ||
} | ||
``` | ||
If the location is invalid, | ||
* _in strict mode_, throws an error | ||
* _in non-strict mode_, returns `null` | ||
| ||
<a name="resolveLocationSet" href="#resolveLocationSet">#</a> <i>loco</i>.<b>resolveLocationSet</b>(<i>locationSet</i>) | ||
Resolves a given locationSet into a GeoJSON feature. This is similar to [validateLocationSet](#validateLocationSet), but runs slower and includes the actual GeoJSON in the result. Results are cached, so if you ask for the same thing multiple times we don't repeat the expensive clipping operations. | ||
The returned GeoJSON feature will also have an `area` property containing the approximate size of the feature in km². (This is helpful for sorting features) | ||
If the locationSet is valid, returns a result `Object` like: | ||
```js | ||
{ | ||
type: 'locationset' | ||
locationSet: the queried locationSet | ||
id: the stable identifier for the feature | ||
feature: the resolved GeoJSON feature | ||
} | ||
``` | ||
If the locationSet is invalid or contains any invalid locations, | ||
* _in strict mode_, throws an error | ||
* _in non-strict mode_, invalid locations are quietly ignored. A locationSet with nothing included will be considered valid, and will validate as if it were a locationSet covering the entire world. `{ type: 'locationset', locationSet: ['Q2'], id: +[Q2] }` | ||
| ||
<a name="strict" href="#strict">#</a> <i>loco</i>.<b>strict</b>(<i>val</i>) | ||
Get/set "strict mode". New instances of LocationConflation start out in strict mode by default. | ||
* In strict mode, any invalid location or locationSet throws an error. | ||
* In non strict mode, invalid locations are ignored, and locationSets that include nothing are assumed to include the entire world. | ||
```js | ||
loco.strict(false); // pass a true/false value to set the strict mode | ||
const isStrict = loco.strict(); // pass no value to return the current value | ||
``` | ||
| ||
<a name="stringify" href="#stringify">#</a> <i>loco</i>.<b>stringify</b>(<i>object</i>, <i>options</i>) | ||
Convenience method that wraps [json-stringify-pretty-compact](https://www.npmjs.com/package/@aitodotai/json-stringify-pretty-compact) to stringify the given object. Optional `options` parameter gets passed through to json-stringify-pretty-compact. | ||
```js | ||
loco.stringify(someGeoJson, { maxLength: 100 }); // Make it pretty! | ||
``` | ||
<a name="cache" href="#cache">#</a> <i>loco</i>.<b>cache</b>() | ||
Convenience method to access the internal feature `_cache`. You probably shouldn't use it except for debugging. | ||
| ||
## Contributing | ||
### Prerequisites | ||
* [Node.js](https://nodejs.org/) version 10 or newer | ||
@@ -132,3 +286,3 @@ * [`git`](https://www.atlassian.com/git/tutorials/install-git/) for your platform | ||
#### Installing | ||
### Installing | ||
@@ -141,3 +295,3 @@ * Clone this project, for example: | ||
#### Building | ||
### Building | ||
@@ -147,2 +301,9 @@ * `npm run build` | ||
### Thanks! | ||
**location-conflation** is really just a wrapper around these other great projects: | ||
* [country-coder](https://github.com/ideditor/country-coder) for world boundaries, and | ||
* [polygon-clipping](https://github.com/mfogel/polygon-clipping) for union/difference functions | ||
### License | ||
@@ -149,0 +310,0 @@ |
@@ -6,2 +6,3 @@ const test = require('tap').test; | ||
const loco = new LocationConflation(features); | ||
const locoNS = new LocationConflation(features).strict(false); | ||
@@ -13,17 +14,24 @@ | ||
t.test('a valid [lon,lat] coordinate pair returns a feature match', t => { | ||
const result = loco.resolveLocation([0, 0]); | ||
const location = [0, 0]; | ||
const result = loco.resolveLocation(location); | ||
t.notEqual(result, null); | ||
t.equal(result.type, 'point'); | ||
t.type(result.feature, 'object'); | ||
t.type(result.feature.properties, 'object'); | ||
t.equal(result.feature.id, '[0,0]'); // has an id | ||
t.equal(result.feature.properties.id, '[0,0]'); // has an id property | ||
t.match(result.feature.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.location, location); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '[0,0]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '[0,0]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('an invalid [lon,lat] coordinate pair returns a null match', t => { | ||
const result = loco.resolveLocation([]); | ||
t.equal(result, null); | ||
t.test('(strict mode) an invalid [lon,lat] coordinate pair throws an error', t => { | ||
const location = []; | ||
t.throws(() => loco.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid [lon,lat] coordinate pair returns falsy', t => { | ||
const location = []; | ||
t.notOk(locoNS.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.end(); | ||
@@ -35,39 +43,50 @@ }); | ||
t.test('a known `.geojson` filename with id returns a feature match', t => { | ||
const result = loco.resolveLocation('dc_metro.geojson'); | ||
const location = 'dc_metro.geojson'; | ||
const result = loco.resolveLocation(location); | ||
t.notEqual(result, null); | ||
t.equal(result.type, 'geojson'); | ||
t.type(result.feature, 'object'); | ||
t.type(result.feature.properties, 'object'); | ||
t.equal(result.feature.id, 'dc_metro.geojson'); // has an id | ||
t.equal(result.feature.properties.id, 'dc_metro.geojson'); // has an id property | ||
t.match(result.feature.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.location, location); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'dc_metro.geojson'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'dc_metro.geojson'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('a known `.geojson` filename with id property returns a feature match', t => { | ||
const result = loco.resolveLocation('philly_metro.geojson'); | ||
const location = 'philly_metro.geojson'; | ||
const result = loco.resolveLocation(location); | ||
t.notEqual(result, null); | ||
t.equal(result.type, 'geojson'); | ||
t.type(result.feature, 'object'); | ||
t.type(result.feature.properties, 'object'); | ||
t.equal(result.feature.id, 'philly_metro.geojson'); // has an id | ||
t.equal(result.feature.properties.id, 'philly_metro.geojson'); // has an id property | ||
t.match(result.feature.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.location, location); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'philly_metro.geojson'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'philly_metro.geojson'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('`.geojson` identifiers compare as lowercase', t => { | ||
const result = loco.resolveLocation('PHiLLy_MeTRo.GeoJSoN'); | ||
const location = 'PHiLLy_MeTRo.GeoJSoN'; | ||
const result = loco.resolveLocation(location); | ||
t.notEqual(result, null); | ||
t.equal(result.type, 'geojson'); | ||
t.type(result.feature, 'object'); | ||
t.type(result.feature.properties, 'object'); | ||
t.equal(result.feature.id, 'philly_metro.geojson'); // has an id | ||
t.equal(result.feature.properties.id, 'philly_metro.geojson'); // has an id property | ||
t.match(result.feature.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.location, location); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'philly_metro.geojson'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'philly_metro.geojson'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('an invalid `.geojson` filename returns a null match', t => { | ||
const result = loco.resolveLocation('fake.geojson'); | ||
t.equal(result, null); | ||
t.test('(strict mode) an invalid `.geojson` filename throws an error', t => { | ||
const location = 'fake.geojson'; | ||
t.throws(() => loco.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid `.geojson` filename returns falsy', t => { | ||
const location = 'fake.geojson'; | ||
t.notOk(locoNS.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.end(); | ||
@@ -79,17 +98,24 @@ }); | ||
t.test('a valid country coder feature identifier returns a feature match', t => { | ||
const result = loco.resolveLocation('gb'); | ||
const location = 'gb'; | ||
const result = loco.resolveLocation(location); | ||
t.notEqual(result, null); | ||
t.equal(result.type, 'countrycoder'); | ||
t.type(result.feature, 'object'); | ||
t.type(result.feature.properties, 'object'); | ||
t.equal(result.feature.id, 'Q145'); // has an id | ||
t.equal(result.feature.properties.id, 'Q145'); // has an id property | ||
t.match(result.feature.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.location, location); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'Q145'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'Q145'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('an invalid country coder feature identifier returns a null match', t => { | ||
const result = loco.resolveLocation('fake'); | ||
t.equal(result, null); | ||
t.test('(strict mode) an invalid country coder feature identifier throws an error', t => { | ||
const location = 'fake'; | ||
t.throws(() => loco.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid country coder feature identifier returns falsy', t => { | ||
const location = 'fake'; | ||
t.notOk(locoNS.resolveLocation(location)); | ||
t.end(); | ||
}); | ||
t.end(); | ||
@@ -96,0 +122,0 @@ }); |
@@ -6,92 +6,186 @@ const test = require('tap').test; | ||
const loco = new LocationConflation(features); | ||
const locoNS = new LocationConflation(features).strict(false); | ||
test('resolveLocationSet', t => { | ||
t.test('empty include defaults to world (Q2)', t => { | ||
const locationSet = { }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, 'Q2'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.end(); | ||
}); | ||
t.test('sorts included countrycoder locations', t => { | ||
const locationSet = { include: ['013', '005'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q18,Q27611]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('empty locationSet', t => { | ||
t.test('(strict mode) empty locationSet throws an error', t => { | ||
const locationSet = { }; | ||
t.throws(() => loco.resolveLocationSet(locationSet)); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) empty locationSet defaults to world (Q2)', t => { | ||
const locationSet = { }; | ||
const result = locoNS.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'Q2'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'Q2'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.test('sorts excluded countrycoder locations', t => { | ||
const locationSet = { exclude: ['013', '005'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q2]-[Q18,Q27611]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('country coder feature identifiers', t => { | ||
t.test('sorts included countrycoder locations', t => { | ||
const locationSet = { include: ['013', '005'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q18,Q27611]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q18,Q27611]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q18,Q27611]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('sorts excluded countrycoder locations', t => { | ||
const locationSet = { include: ['001'], exclude: ['013', '005'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]-[Q18,Q27611]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q2]-[Q18,Q27611]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q2]-[Q18,Q27611]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.test('sorts included .geojson locations', t => { | ||
const locationSet = { include: ['philly_metro.geojson', 'dc_metro.geojson'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[dc_metro.geojson,philly_metro.geojson]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('`.geojson` filenames', t => { | ||
t.test('sorts included .geojson locations', t => { | ||
const locationSet = { include: ['philly_metro.geojson', 'dc_metro.geojson'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[dc_metro.geojson,philly_metro.geojson]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[dc_metro.geojson,philly_metro.geojson]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[dc_metro.geojson,philly_metro.geojson]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a `numeric` area property | ||
t.end(); | ||
}); | ||
t.test('sorts excluded .geojson locations', t => { | ||
const locationSet = { include: ['001'], exclude: ['philly_metro.geojson', 'dc_metro.geojson'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]-[dc_metro.geojson,philly_metro.geojson]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q2]-[dc_metro.geojson,philly_metro.geojson]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q2]-[dc_metro.geojson,philly_metro.geojson]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.test('sorts excluded .geojson locations', t => { | ||
const locationSet = { exclude: ['philly_metro.geojson', 'dc_metro.geojson'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q2]-[dc_metro.geojson,philly_metro.geojson]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('points', t => { | ||
t.test('sorts included point locations', t => { | ||
const locationSet = { include: [[1, 0], [0, 1], [1, 1], [0, 0]] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[[0,0],[0,1],[1,0],[1,1]]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[[0,0],[0,1],[1,0],[1,1]]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[[0,0],[0,1],[1,0],[1,1]]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('sorts excluded point locations', t => { | ||
const locationSet = { include: ['001'], exclude: [[1, 0], [0, 1], [1, 1], [0, 0]] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]-[[0,0],[0,1],[1,0],[1,1]]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q2]-[[0,0],[0,1],[1,0],[1,1]]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q2]-[[0,0],[0,1],[1,0],[1,1]]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); | ||
t.test('sorts included point locations', t => { | ||
const locationSet = { include: [[1, 0], [0, 1], [1, 1], [0, 0]] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[[0,0],[0,1],[1,0],[1,1]]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('(strict mode) included junk locations throw an error', t => { | ||
const locationSet = { include: ['fake', 'null'] }; | ||
t.throws(() => loco.resolveLocationSet(locationSet)); | ||
t.end(); | ||
}); | ||
t.test('sorts excluded point locations', t => { | ||
const locationSet = { exclude: [[1, 0], [0, 1], [1, 1], [0, 0]] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.test('(non strict mode) ignores included junk locations', t => { | ||
const locationSet = { include: ['fake', 'null'] }; | ||
const result = locoNS.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q2]-[[0,0],[0,1],[1,0],[1,1]]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'Q2'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'Q2'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('ignores included junk locations', t => { | ||
const locationSet = { include: ['fake', 'null'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, 'Q2'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.test('(strict mode) excluded junk locations throw an error', t => { | ||
const locationSet = { include: ['001'], exclude: ['fake', 'null'] }; | ||
t.throws(() => loco.resolveLocationSet(locationSet)); | ||
t.end(); | ||
}); | ||
t.test('ignores excluded junk locations', t => { | ||
const locationSet = { exclude: ['fake', 'null'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.test('(non strict mode) ignores excluded junk locations', t => { | ||
const locationSet = { include: ['001'], exclude: ['fake', 'null'] }; | ||
const result = locoNS.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, 'Q2'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, 'Q2'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, 'Q2'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
@@ -103,6 +197,12 @@ }); | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q16,philly_metro.geojson,[0,0]]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q16,philly_metro.geojson,[0,0]]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q16,philly_metro.geojson,[0,0]]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q16,philly_metro.geojson,[0,0]]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
@@ -112,22 +212,18 @@ }); | ||
t.test('sorts excluded countrycoder < geojson < point', t => { | ||
const locationSet = { exclude: ['philly_metro.geojson', [0,0], 'ca'] }; | ||
const locationSet = { include: ['001'], exclude: ['philly_metro.geojson', [0,0], 'ca'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.notEqual(result, null); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q2]-[Q16,philly_metro.geojson,[0,0]]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.equal(result.type, 'locationset'); | ||
t.equal(result.locationSet, locationSet); | ||
t.equal(result.id, '+[Q2]-[Q16,philly_metro.geojson,[0,0]]'); | ||
t.type(result.feature, 'object'); // result includes a `feature` | ||
t.equal(result.feature.id, '+[Q2]-[Q16,philly_metro.geojson,[0,0]]'); // feature has an `id` | ||
t.type(result.feature.properties, 'object'); // feature has `properties` | ||
t.equal(result.feature.properties.id, '+[Q2]-[Q16,philly_metro.geojson,[0,0]]'); // properties has an `id` property | ||
t.match(result.feature.properties, { area: /\d+/ }); // properties has a numeric `area` property | ||
t.end(); | ||
}); | ||
t.test('force lowercase', t => { | ||
const locationSet = { include: ['US'], exclude: ['PR'] }; | ||
const result = loco.resolveLocationSet(locationSet); | ||
t.type(result, 'object'); | ||
t.type(result.properties, 'object'); | ||
t.equal(result.properties.id, '+[Q30]-[Q1183]'); | ||
t.match(result.properties, { area: /\d+/ }); // has a numeric area property | ||
t.end(); | ||
}); | ||
t.end(); | ||
}); |
@@ -6,2 +6,3 @@ const test = require('tap').test; | ||
const loco = new LocationConflation(features); | ||
const locoNS = new LocationConflation(features).strict(false); | ||
@@ -17,2 +18,3 @@ | ||
t.equal(result.type, 'point'); | ||
t.equal(result.location, val); | ||
t.equal(result.id, '[' + val.toString() + ']'); | ||
@@ -22,13 +24,24 @@ }); | ||
}); | ||
t.test('an invalid [lon,lat] coordinate pair returns false', t => { | ||
t.notOk(loco.validateLocation([])); | ||
t.notOk(loco.validateLocation(['a'])); | ||
t.notOk(loco.validateLocation([0])); | ||
t.notOk(loco.validateLocation([0, 0, 0])); | ||
t.notOk(loco.validateLocation([-181, -90])); | ||
t.notOk(loco.validateLocation([-180, 91])); | ||
t.notOk(loco.validateLocation([181, -90])); | ||
t.notOk(loco.validateLocation([180, 91])); | ||
t.test('(strict mode) an invalid [lon,lat] coordinate pair throws an error', t => { | ||
t.throws(() => loco.validateLocation([])); | ||
t.throws(() => loco.validateLocation(['a'])); | ||
t.throws(() => loco.validateLocation([0])); | ||
t.throws(() => loco.validateLocation([0, 0, 0])); | ||
t.throws(() => loco.validateLocation([-181, -90])); | ||
t.throws(() => loco.validateLocation([-180, 91])); | ||
t.throws(() => loco.validateLocation([181, -90])); | ||
t.throws(() => loco.validateLocation([180, 91])); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid [lon,lat] coordinate pair returns falsy', t => { | ||
t.notOk(locoNS.validateLocation([])); | ||
t.notOk(locoNS.validateLocation(['a'])); | ||
t.notOk(locoNS.validateLocation([0])); | ||
t.notOk(locoNS.validateLocation([0, 0, 0])); | ||
t.notOk(locoNS.validateLocation([-181, -90])); | ||
t.notOk(locoNS.validateLocation([-180, 91])); | ||
t.notOk(locoNS.validateLocation([181, -90])); | ||
t.notOk(locoNS.validateLocation([180, 91])); | ||
t.end(); | ||
}); | ||
t.end(); | ||
@@ -44,2 +57,3 @@ }); | ||
t.equal(result.type, 'geojson'); | ||
t.equal(result.location, val); | ||
t.equal(result.id, val); | ||
@@ -49,7 +63,12 @@ }); | ||
}); | ||
t.test('an invalid `.geojson` identifier returns false', t => { | ||
t.notOk(loco.validateLocation('philly_metro')); // missing .geojson | ||
t.notOk(loco.validateLocation('fake.geojson')); // fake filename | ||
t.test('(strict mode) an invalid `.geojson` identifier throws an error', t => { | ||
t.throws(() => loco.validateLocation('philly_metro')); // missing .geojson | ||
t.throws(() => loco.validateLocation('fake.geojson')); // fake filename | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid `.geojson` identifier returns falsy', t => { | ||
t.notOk(locoNS.validateLocation('philly_metro')); // missing .geojson | ||
t.notOk(locoNS.validateLocation('fake.geojson')); // fake filename | ||
t.end(); | ||
}); | ||
t.test('`.geojson` identifiers compare as lowercase', t => { | ||
@@ -59,2 +78,3 @@ const result = loco.validateLocation('PHiLLy_MeTRo.GeoJSoN'); | ||
t.equal(result.type, 'geojson'); | ||
t.equal(result.location, 'PHiLLy_MeTRo.GeoJSoN'); | ||
t.equal(result.id, 'philly_metro.geojson'); | ||
@@ -73,2 +93,3 @@ t.end(); | ||
t.equal(result.type, 'countrycoder'); | ||
t.equal(result.location, val); | ||
t.equal(result.id, 'Q145'); | ||
@@ -78,8 +99,14 @@ }); | ||
}); | ||
t.test('an invalid country coder identifier returns false', t => { | ||
t.notOk(loco.validateLocation('')); | ||
t.notOk(loco.validateLocation('false')); | ||
t.notOk(loco.validateLocation('null')); | ||
t.test('(strict mode) an invalid country coder identifier throws an error', t => { | ||
t.throws(() => loco.validateLocation('')); | ||
t.throws(() => loco.validateLocation('false')); | ||
t.throws(() => loco.validateLocation('null')); | ||
t.end(); | ||
}); | ||
t.test('(non strict mode) an invalid country coder identifier returns falsy', t => { | ||
t.notOk(locoNS.validateLocation('')); | ||
t.notOk(locoNS.validateLocation('false')); | ||
t.notOk(locoNS.validateLocation('null')); | ||
t.end(); | ||
}); | ||
t.end(); | ||
@@ -86,0 +113,0 @@ }); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
4754878
26
13190
301
0