-
Notifications
You must be signed in to change notification settings - Fork 943
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Converted turf-mask to Typescript and added support for geometry para…
…meters (#2644) * Converted turf-mask to Typescript. Fixed an apparently unreported bug where the typings suggested it was ok to pass a Polygon or MultiPolygon Geometry (rather than a complete Feature), but the code would bomb out.
- Loading branch information
1 parent
a8038d0
commit 2786f69
Showing
8 changed files
with
225 additions
and
111 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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) | ||
); | ||
} | ||
|
||
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; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.