diff --git a/packages/turf-rewind/LICENSE b/packages/turf-rewind/LICENSE new file mode 100644 index 0000000000..96ce51b76f --- /dev/null +++ b/packages/turf-rewind/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2017 TurfJS + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/turf-rewind/README.md b/packages/turf-rewind/README.md new file mode 100644 index 0000000000..03b82fcc28 --- /dev/null +++ b/packages/turf-rewind/README.md @@ -0,0 +1,79 @@ +# @turf/rewind + +# rewind + +Rewind [(Mutli)LineString](http://geojson.org/geojson-spec.html#linestring) or [(Multi)Polygon](http://geojson.org/geojson-spec.html#polygon) outer ring clockwise and inner rings counterclockwise (Uses [Shoelace Formula](http://en.wikipedia.org/wiki/Shoelace_formula)). + +**Parameters** + +- `geojson` **[Feature](http://geojson.org/geojson-spec.html#feature-objects)<([Polygon](http://geojson.org/geojson-spec.html#polygon) \| [MultiPolygon](http://geojson.org/geojson-spec.html#multipolygon) \| [LineString](http://geojson.org/geojson-spec.html#linestring) \| [MultiLineString](http://geojson.org/geojson-spec.html#multilinestring))>** input GeoJSON Polygon +- `reverse` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** enable reverse winding (optional, default `false`) +- `mutate` **\[[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** allows GeoJSON input to be mutated (significant performance increase if true) (optional, default `false`) + +**Examples** + +```javascript +var polygon = { + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [[121, -29], [138, -29], [138, -18], [121, -18], [121, -29]] + ] + } +}; +var rewind = turf.rewind(polygon); + +//addToMap +var addToMap = [rewind]; +``` + +Returns **[Feature](http://geojson.org/geojson-spec.html#feature-objects)<([Polygon](http://geojson.org/geojson-spec.html#polygon) \| [MultiPolygon](http://geojson.org/geojson-spec.html#multipolygon) \| [LineString](http://geojson.org/geojson-spec.html#linestring) \| [MultiLineString](http://geojson.org/geojson-spec.html#multilinestring))>** rewind Polygon + +# rewindLineString + +Rewind LineString - outer ring clockwise + +**Parameters** + +- `coords` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>>** GeoJSON LineString geometry coordinates +- `reverse` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** enable reverse winding (optional, default `false`) + +Returns **void** mutates coordinates + +# rewindPolygon + +Rewind Polygon - outer ring clockwise and inner rings counterclockwise. + +**Parameters** + +- `coords` **[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)<[number](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number)>>>** GeoJSON Polygon geometry coordinates +- `reverse` **\[[Boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean)]** enable reverse winding (optional, default `false`) + +Returns **void** mutates coordinates + + + +--- + +This module is part of the [Turfjs project](http://turfjs.org/), an open source +module collection dedicated to geographic algorithms. It is maintained in the +[Turfjs/turf](https://github.com/Turfjs/turf) repository, where you can create +PRs and issues. + +### Installation + +Install this module individually: + +```sh +$ npm install @turf/rewind +``` + +Or install the Turf module that includes it as a function: + +```sh +$ npm install @turf/turf +``` diff --git a/packages/turf-rewind/bench.js b/packages/turf-rewind/bench.js new file mode 100644 index 0000000000..a5182ff65b --- /dev/null +++ b/packages/turf-rewind/bench.js @@ -0,0 +1,34 @@ +const fs = require('fs'); +const path = require('path'); +const load = require('load-json-file'); +const Benchmark = require('benchmark'); +const rewind = require('./'); + +const directory = path.join(__dirname, 'test', 'in') + path.sep; +let fixtures = fs.readdirSync(directory).map(filename => { + return { + filename, + name: path.parse(filename).name, + geojson: load.sync(directory + filename) + }; +}); +// fixtures = fixtures.filter(fixture => fixture.name === 'polygons'); + +/** + * Benchmark Results + * + * geometry-polygon-counter-clockwise x 10,379,811 ops/sec ±0.84% (89 runs sampled) + * line-clockwise x 7,937,098 ops/sec ±1.13% (90 runs sampled) + * line-counter-clockwise x 6,324,221 ops/sec ±5.40% (74 runs sampled) + * polygon-clockwise x 8,941,337 ops/sec ±2.95% (81 runs sampled) + * polygon-counter-clockwise x 7,625,375 ops/sec ±12.27% (72 runs sampled) + */ +const suite = new Benchmark.Suite('turf-rewind'); +for (const {name, geojson} of fixtures) { + suite.add(name, () => rewind(geojson, false, true)); +} + +suite + .on('cycle', e => console.log(String(e.target))) + .on('complete', () => {}) + .run(); diff --git a/packages/turf-rewind/index.d.ts b/packages/turf-rewind/index.d.ts new file mode 100644 index 0000000000..41b670855a --- /dev/null +++ b/packages/turf-rewind/index.d.ts @@ -0,0 +1,14 @@ +/// + +type Polygon = GeoJSON.Feature | GeoJSON.Polygon; +type LineString = GeoJSON.Feature | GeoJSON.LineString; +type MultiLineString = GeoJSON.Feature | GeoJSON.MultiLineString; +type MultiPolygon = GeoJSON.Feature | GeoJSON.MultiPolygon; +type Rewind = Polygon | LineString | MultiLineString | MultiPolygon; + +/** + * http://turfjs.org/docs/#rewind + */ +declare function rewind(geojson: Input, reversed?: boolean, mutate?: boolean): Input; +declare namespace rewind { } +export = rewind; diff --git a/packages/turf-rewind/index.js b/packages/turf-rewind/index.js new file mode 100644 index 0000000000..0a4fa4c629 --- /dev/null +++ b/packages/turf-rewind/index.js @@ -0,0 +1,99 @@ +var getCoords = require('@turf/invariant').getCoords; +var isClockWise = require('turf-is-clockwise'); + +/** + * Rewind {@link LineString|(Mutli)LineString} or {@link Polygon|(Multi)Polygon} outer ring clockwise and inner rings counterclockwise (Uses {@link http://en.wikipedia.org/wiki/Shoelace_formula|Shoelace Formula}). + * + * @name rewind + * @param {Feature} geojson input GeoJSON Polygon + * @param {Boolean} [reverse=false] enable reverse winding + * @param {boolean} [mutate=false] allows GeoJSON input to be mutated (significant performance increase if true) + * @returns {Feature} rewind Polygon + * @example + * var polygon = { + * "type": "Feature", + * "properties": {}, + * "geometry": { + * "type": "Polygon", + * "coordinates": [ + * [[121, -29], [138, -29], [138, -18], [121, -18], [121, -29]] + * ] + * } + * }; + * var rewind = turf.rewind(polygon); + * + * //addToMap + * var addToMap = [rewind]; + */ +module.exports = function (geojson, reverse, mutate) { + // default params + reverse = (reverse !== undefined) ? reverse : false; + mutate = (mutate !== undefined) ? mutate : false; + + // validation + if (!geojson) throw new Error(' is required'); + if (typeof reverse !== 'boolean') throw new Error(' must be a boolean'); + if (typeof mutate !== 'boolean') throw new Error(' must be a boolean'); + + // prevent input mutation + if (mutate === false || mutate === undefined) geojson = JSON.parse(JSON.stringify(geojson)); + + var coords = getCoords(geojson); + var type = getGeomType(geojson); + + switch (type) { + case 'LineString': + rewindLineString(coords, reverse); + return geojson; + case 'Polygon': + rewindPolygon(coords, reverse); + return geojson; + case 'MultiLineString': + coords.forEach(function (lineCoords) { + rewindLineString(lineCoords, reverse); + }); + return geojson; + case 'MultiPolygon': + coords.forEach(function (lineCoords) { + rewindPolygon(lineCoords, reverse); + }); + return geojson; + default: + throw new Error('geometry ' + type + ' type not supported'); + } +}; + +/** + * Rewind LineString - outer ring clockwise + * + * @param {Array>} coords GeoJSON LineString geometry coordinates + * @param {Boolean} [reverse=false] enable reverse winding + * @returns {void} mutates coordinates + */ +function rewindLineString(coords, reverse) { + if (isClockWise(coords) === reverse) coords.reverse(); +} + +/** + * Rewind Polygon - outer ring clockwise and inner rings counterclockwise. + * + * @param {Array>>} coords GeoJSON Polygon geometry coordinates + * @param {Boolean} [reverse=false] enable reverse winding + * @returns {void} mutates coordinates + */ +function rewindPolygon(coords, reverse) { + // outer ring + if (isClockWise(coords[0]) === reverse) { + coords[0].reverse(); + } + // inner rings + for (var i = 1; i < coords.length; i++) { + if (isClockWise(coords[i]) !== reverse) { + coords[i].reverse(); + } + } +} + +function getGeomType(feature) { + return (feature.geometry) ? feature.geometry.type : feature.type; +} diff --git a/packages/turf-rewind/package.json b/packages/turf-rewind/package.json new file mode 100644 index 0000000000..bfeb5bbadf --- /dev/null +++ b/packages/turf-rewind/package.json @@ -0,0 +1,48 @@ +{ + "name": "@turf/rewind", + "version": "4.0.0", + "description": "turf rewind module", + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts" + ], + "scripts": { + "test": "node test.js", + "bench": "node bench.js" + }, + "repository": { + "type": "git", + "url": "git://github.com/Turfjs/turf.git" + }, + "keywords": [ + "turf", + "geojson", + "polygon", + "rewind" + ], + "author": "Turf Authors", + "contributors": [ + "Abel Vázquez Montoro <@AbelVM>", + "Tom MacWright <@tmcw>", + "Denis Carriere <@DenisCarriere>", + "Morgan Herlocker <@morganherlocker>" + ], + "license": "MIT", + "bugs": { + "url": "https://github.com/Turfjs/turf/issues" + }, + "homepage": "https://github.com/Turfjs/turf", + "devDependencies": { + "@turf/helpers": "^4.2.0", + "benchmark": "^2.1.4", + "load-json-file": "^2.0.0", + "tape": "^4.6.3", + "write-json-file": "^2.0.0" + }, + "dependencies": { + "@turf/invariant": "^4.2.0", + "turf-is-clockwise": "^1.0.0" + } +} diff --git a/packages/turf-rewind/test.js b/packages/turf-rewind/test.js new file mode 100644 index 0000000000..3c477e5574 --- /dev/null +++ b/packages/turf-rewind/test.js @@ -0,0 +1,31 @@ +const fs = require('fs'); +const test = require('tape'); +const path = require('path'); +const load = require('load-json-file'); +const write = require('write-json-file'); +const rewind = require('./'); + +const directories = { + in: path.join(__dirname, 'test', 'in') + path.sep, + out: path.join(__dirname, 'test', 'out') + path.sep +}; + +let fixtures = fs.readdirSync(directories.in).map(filename => { + return { + filename, + name: path.parse(filename).name, + geojson: load.sync(directories.in + filename) + }; +}); +// fixtures = fixtures.filter(fixture => fixture.name === 'line-counter-clockwise'); + +test('turf-rewind', t => { + for (const {filename, name, geojson} of fixtures) { + const {reverse} = geojson.properties || {}; + const results = rewind(geojson, reverse); + + if (process.env.REGEN) write.sync(directories.out + filename, results); + t.deepEqual(results, load.sync(directories.out + filename), name); + } + t.end(); +}); diff --git a/packages/turf-rewind/test/in/geometry-polygon-counter-clockwise.geojson b/packages/turf-rewind/test/in/geometry-polygon-counter-clockwise.geojson new file mode 100644 index 0000000000..0142d8bdf4 --- /dev/null +++ b/packages/turf-rewind/test/in/geometry-polygon-counter-clockwise.geojson @@ -0,0 +1,27 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] +} \ No newline at end of file diff --git a/packages/turf-rewind/test/in/line-clockwise.geojson b/packages/turf-rewind/test/in/line-clockwise.geojson new file mode 100644 index 0000000000..4e44ddb26c --- /dev/null +++ b/packages/turf-rewind/test/in/line-clockwise.geojson @@ -0,0 +1,49 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 122.87109375, + -20.13847031245114 + ], + [ + 126.21093749999999, + -15.792253570362446 + ], + [ + 129.5947265625, + -14.604847155053898 + ], + [ + 134.560546875, + -15.834535741221552 + ], + [ + 138.73535156249997, + -20.014645445341355 + ], + [ + 139.833984375, + -25.799891182088306 + ], + [ + 134.12109375, + -30.183121842195504 + ], + [ + 131.8798828125, + -30.221101852485987 + ], + [ + 128.49609375, + -29.878755346037963 + ], + [ + 124.4091796875, + -27.839076094777802 + ] + ] + } +} \ No newline at end of file diff --git a/packages/turf-rewind/test/in/line-counter-clockwise.geojson b/packages/turf-rewind/test/in/line-counter-clockwise.geojson new file mode 100644 index 0000000000..17dafd3b27 --- /dev/null +++ b/packages/turf-rewind/test/in/line-counter-clockwise.geojson @@ -0,0 +1,49 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 124.4091796875, + -27.839076094777802 + ], + [ + 128.49609375, + -29.878755346037963 + ], + [ + 131.8798828125, + -30.221101852485987 + ], + [ + 134.12109375, + -30.183121842195504 + ], + [ + 139.833984375, + -25.799891182088306 + ], + [ + 138.73535156249997, + -20.014645445341355 + ], + [ + 134.560546875, + -15.834535741221552 + ], + [ + 129.5947265625, + -14.604847155053898 + ], + [ + 126.21093749999999, + -15.792253570362446 + ], + [ + 122.87109375, + -20.13847031245114 + ] + ] + } +} diff --git a/packages/turf-rewind/test/in/polygon-clockwise.geojson b/packages/turf-rewind/test/in/polygon-clockwise.geojson new file mode 100644 index 0000000000..b823d23879 --- /dev/null +++ b/packages/turf-rewind/test/in/polygon-clockwise.geojson @@ -0,0 +1,31 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] + } +} diff --git a/packages/turf-rewind/test/in/polygon-counter-clockwise.geojson b/packages/turf-rewind/test/in/polygon-counter-clockwise.geojson new file mode 100644 index 0000000000..79fec8dac7 --- /dev/null +++ b/packages/turf-rewind/test/in/polygon-counter-clockwise.geojson @@ -0,0 +1,31 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] + } +} \ No newline at end of file diff --git a/packages/turf-rewind/test/out/geometry-polygon-counter-clockwise.geojson b/packages/turf-rewind/test/out/geometry-polygon-counter-clockwise.geojson new file mode 100644 index 0000000000..749cf998ce --- /dev/null +++ b/packages/turf-rewind/test/out/geometry-polygon-counter-clockwise.geojson @@ -0,0 +1,27 @@ +{ + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] +} diff --git a/packages/turf-rewind/test/out/line-clockwise.geojson b/packages/turf-rewind/test/out/line-clockwise.geojson new file mode 100644 index 0000000000..61a7433741 --- /dev/null +++ b/packages/turf-rewind/test/out/line-clockwise.geojson @@ -0,0 +1,49 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 122.87109375, + -20.13847031245114 + ], + [ + 126.21093749999999, + -15.792253570362446 + ], + [ + 129.5947265625, + -14.604847155053898 + ], + [ + 134.560546875, + -15.834535741221552 + ], + [ + 138.73535156249997, + -20.014645445341355 + ], + [ + 139.833984375, + -25.799891182088306 + ], + [ + 134.12109375, + -30.183121842195504 + ], + [ + 131.8798828125, + -30.221101852485987 + ], + [ + 128.49609375, + -29.878755346037963 + ], + [ + 124.4091796875, + -27.839076094777802 + ] + ] + } +} diff --git a/packages/turf-rewind/test/out/line-counter-clockwise.geojson b/packages/turf-rewind/test/out/line-counter-clockwise.geojson new file mode 100644 index 0000000000..61a7433741 --- /dev/null +++ b/packages/turf-rewind/test/out/line-counter-clockwise.geojson @@ -0,0 +1,49 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 122.87109375, + -20.13847031245114 + ], + [ + 126.21093749999999, + -15.792253570362446 + ], + [ + 129.5947265625, + -14.604847155053898 + ], + [ + 134.560546875, + -15.834535741221552 + ], + [ + 138.73535156249997, + -20.014645445341355 + ], + [ + 139.833984375, + -25.799891182088306 + ], + [ + 134.12109375, + -30.183121842195504 + ], + [ + 131.8798828125, + -30.221101852485987 + ], + [ + 128.49609375, + -29.878755346037963 + ], + [ + 124.4091796875, + -27.839076094777802 + ] + ] + } +} diff --git a/packages/turf-rewind/test/out/polygon-clockwise.geojson b/packages/turf-rewind/test/out/polygon-clockwise.geojson new file mode 100644 index 0000000000..b823d23879 --- /dev/null +++ b/packages/turf-rewind/test/out/polygon-clockwise.geojson @@ -0,0 +1,31 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] + } +} diff --git a/packages/turf-rewind/test/out/polygon-counter-clockwise.geojson b/packages/turf-rewind/test/out/polygon-counter-clockwise.geojson new file mode 100644 index 0000000000..b823d23879 --- /dev/null +++ b/packages/turf-rewind/test/out/polygon-counter-clockwise.geojson @@ -0,0 +1,31 @@ +{ + "type": "Feature", + "properties": {}, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 121.9921875, + -29.22889003019423 + ], + [ + 121.9921875, + -18.312810846425442 + ], + [ + 138.515625, + -18.312810846425442 + ], + [ + 138.515625, + -29.22889003019423 + ], + [ + 121.9921875, + -29.22889003019423 + ] + ] + ] + } +} diff --git a/packages/turf-rewind/test/types.ts b/packages/turf-rewind/test/types.ts new file mode 100644 index 0000000000..bb9d6709f0 --- /dev/null +++ b/packages/turf-rewind/test/types.ts @@ -0,0 +1,15 @@ +import {polygon, lineString, multiLineString, multiPolygon} from '@turf/helpers' +import * as rewind from '../' + +const coords = [[121, -29], [138, -29], [138, -18], [121, -18], [121, -29]] +const poly = polygon([coords]) +const line = lineString(coords) +const multiPoly = multiPolygon([[coords], [coords]]) +const multiLine = multiLineString([coords, coords]) + +rewind(line) +rewind(poly) +rewind(multiPoly) +rewind(multiLine) +rewind(poly, true) +rewind(poly, true, true)