Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Converted turf-mask to Typescript and added support for geometry parameters #2644

Merged
merged 3 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 11 additions & 12 deletions packages/turf-mask/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,35 @@

## mask

Takes any type of [polygon][1] and an optional mask and returns a [polygon][1] exterior ring with holes.
Takes polygons or multipolygons and an optional mask, and returns an exterior
ring polygon with holes.

### Parameters

* `polygon` **([FeatureCollection][2] | [Feature][3]<([Polygon][4] | [MultiPolygon][5])>)** GeoJSON Polygon used as interior rings or holes.
* `mask` **[Feature][3]<[Polygon][4]>?** GeoJSON Polygon used as the exterior ring (if undefined, the world extent is used)
* `polygon` **([Polygon][1] | [MultiPolygon][2] | [Feature][3]<([Polygon][1] | [MultiPolygon][2])> | [FeatureCollection][4]<([Polygon][1] | [MultiPolygon][2])>)** GeoJSON polygon used as interior rings or holes
* `mask` **([Polygon][1] | [Feature][3]<[Polygon][1]>)?** GeoJSON polygon used as the exterior ring (if undefined, the world extent is used)

### Examples

```javascript
var polygon = turf.polygon([[[112, -21], [116, -36], [146, -39], [153, -24], [133, -10], [112, -21]]]);
var mask = turf.polygon([[[90, -55], [170, -55], [170, 10], [90, 10], [90, -55]]]);
const polygon = turf.polygon([[[112, -21], [116, -36], [146, -39], [153, -24], [133, -10], [112, -21]]]);
const mask = turf.polygon([[[90, -55], [170, -55], [170, 10], [90, 10], [90, -55]]]);

var masked = turf.mask(polygon, mask);
const masked = turf.mask(polygon, mask);

//addToMap
var addToMap = [masked]
const addToMap = [masked]
```

Returns **[Feature][3]<[Polygon][4]>** Masked Polygon (exterior ring with holes).
Returns **[Feature][3]<[Polygon][1]>** Masked Polygon (exterior ring with holes).

[1]: https://tools.ietf.org/html/rfc7946#section-3.1.6

[2]: https://tools.ietf.org/html/rfc7946#section-3.3
[2]: https://tools.ietf.org/html/rfc7946#section-3.1.7

[3]: https://tools.ietf.org/html/rfc7946#section-3.2

[4]: https://tools.ietf.org/html/rfc7946#section-3.1.6

[5]: https://tools.ietf.org/html/rfc7946#section-3.1.7
[4]: https://tools.ietf.org/html/rfc7946#section-3.3

<!-- This file is automatically generated. Please don't edit it directly. If you find an error, edit the source file of the module in question (likely index.js or index.ts), and re-run "yarn docs" from the root of the turf project. -->

Expand Down
11 changes: 7 additions & 4 deletions packages/turf-mask/bench.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Feature, FeatureCollection, Polygon, MultiPolygon } from "geojson";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import { loadJsonFileSync } from "load-json-file";
import Benchmark from "benchmark";
import Benchmark, { Event } from "benchmark";
import { mask as turfMask } from "./index.js";

const __dirname = path.dirname(fileURLToPath(import.meta.url));
Expand All @@ -17,21 +18,23 @@ const directories = {
let fixtures = fs.readdirSync(directories.in).map((filename) => {
return {
name: path.parse(filename).name,
geojson: loadJsonFileSync(path.join(directories.in, filename)),
geojson: loadJsonFileSync(
path.join(directories.in, filename)
) as FeatureCollection<Polygon | MultiPolygon>,
};
});

for (const { name, geojson } of fixtures) {
const [polygon, masking] = geojson.features;
suite.add(name, () => turfMask(polygon, masking));
suite.add(name, () => turfMask(polygon, masking as Feature<Polygon>));
}

// basic x 4,627 ops/sec ±25.23% (21 runs sampled)
// mask-outside x 3,892 ops/sec ±34.80% (15 runs sampled)
// multi-polygon x 5,837 ops/sec ±3.03% (91 runs sampled)
// overlapping x 22,326 ops/sec ±1.34% (90 runs sampled)
suite
.on("cycle", (event) => {
.on("cycle", (event: Event) => {
console.log(String(event.target));
})
.run();
12 changes: 0 additions & 12 deletions packages/turf-mask/index.d.ts

This file was deleted.

80 changes: 0 additions & 80 deletions packages/turf-mask/index.js

This file was deleted.

118 changes: 118 additions & 0 deletions packages/turf-mask/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import {
Feature,
FeatureCollection,
Polygon,
Position,
MultiPolygon,
} from "geojson";
import { polygon as createPolygon, multiPolygon } from "@turf/helpers";
import polygonClipping, { Geom } from "polygon-clipping";

/**
* Takes polygons or multipolygons and an optional mask, and returns an exterior
* ring polygon with holes.
*
* @name mask
* @param {Polygon|MultiPolygon|Feature<Polygon|MultiPolygon>|FeatureCollection<Polygon|MultiPolygon>} polygon GeoJSON polygon used as interior rings or holes
* @param {Polygon|Feature<Polygon>} [mask] GeoJSON polygon used as the exterior ring (if undefined, the world extent is used)
* @returns {Feature<Polygon>} Masked Polygon (exterior ring with holes).
* @example
* const polygon = turf.polygon([[[112, -21], [116, -36], [146, -39], [153, -24], [133, -10], [112, -21]]]);
* const mask = turf.polygon([[[90, -55], [170, -55], [170, 10], [90, 10], [90, -55]]]);
*
* const masked = turf.mask(polygon, mask);
*
* //addToMap
* const addToMap = [masked]
*/
function mask<T extends Polygon | MultiPolygon>(
polygon: T | Feature<T> | FeatureCollection<T>,
mask?: Polygon | Feature<Polygon>
): Feature<Polygon> {
// Define mask
const maskPolygon = createMask(mask);

let polygonOuters = null;
if (polygon.type === "FeatureCollection") {
polygonOuters = unionFc(polygon);
} else if (polygon.type === "Feature") {
// Need to cast below as Position[][] isn't quite as strict as Geom, even
// though they should be equivalent.
polygonOuters = createGeomFromPolygonClippingOutput(
polygonClipping.union(polygon.geometry.coordinates as Geom)
);
} else {
// Geometry
// Need to cast below as Position[][] isn't quite as strict as Geom, even
// though they should be equivalent.
polygonOuters = createGeomFromPolygonClippingOutput(
polygonClipping.union(polygon.coordinates as Geom)
smallsaucepan marked this conversation as resolved.
Show resolved Hide resolved
);
}

polygonOuters.geometry.coordinates.forEach(function (contour) {
maskPolygon.geometry.coordinates.push(contour[0]);
});

return maskPolygon;
}

function unionFc(fc: FeatureCollection<Polygon | MultiPolygon>) {
// Need to cast below as Position[][] isn't quite as strict as Geom, even
// though they should be equivalent.

// Stick with apply() below as spread operator degrades performance. Have
// to disable prefer-spread lint rule though.
/* eslint-disable prefer-spread */
const unioned =
fc.features.length === 2
? polygonClipping.union(
fc.features[0].geometry.coordinates as Geom,
fc.features[1].geometry.coordinates as Geom
)
: polygonClipping.union.apply(
polygonClipping,
fc.features.map(function (f) {
return f.geometry.coordinates;
}) as [Geom, ...Geom[]]
);
/* eslint-enable */
return createGeomFromPolygonClippingOutput(unioned);
}

function createGeomFromPolygonClippingOutput(unioned: Position[][][]) {
return multiPolygon(unioned);
}

/**
* Create Mask Coordinates
*
* @private
* @param {Feature<Polygon>} [mask] default to world if undefined
* @returns {Feature<Polygon>} mask as a polygon
*/
function createMask(mask: Feature<Polygon> | Polygon | undefined) {
const world = [
[
[180, 90],
[-180, 90],
[-180, -90],
[180, -90],
[180, 90],
],
];
let coordinates = world;
if (mask) {
if (mask.type === "Feature") {
// polygon feature
coordinates = mask.geometry.coordinates;
} else {
// polygon geometry
coordinates = mask.coordinates;
}
}
return createPolygon(coordinates);
}

export { mask };
export default mask;
5 changes: 4 additions & 1 deletion packages/turf-mask/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,13 @@
"tape": "^5.7.2",
"tsup": "^8.0.1",
"tsx": "^4.6.2",
"typescript": "^5.2.2",
"write-json-file": "^5.0.0"
},
"dependencies": {
"@turf/helpers": "workspace:^",
"polygon-clipping": "^0.15.3"
"@types/geojson": "7946.0.8",
"polygon-clipping": "^0.15.3",
"tslib": "^2.6.2"
}
}
Loading
Loading