diff --git a/packages/turf-points-within-polygon/index.d.ts b/packages/turf-points-within-polygon/index.d.ts index 794819b9d7..d3a31d3ad9 100644 --- a/packages/turf-points-within-polygon/index.d.ts +++ b/packages/turf-points-within-polygon/index.d.ts @@ -3,6 +3,7 @@ import { FeatureCollection, Polygon, MultiPolygon, + MultiPoint, Point, Properties, } from "@turf/helpers"; @@ -11,9 +12,10 @@ import { * http://turfjs.org/docs/#pointswithinpolygon */ export default function pointsWithinPolygon< + F extends Point | MultiPoint, G extends Polygon | MultiPolygon, P = Properties >( - points: Feature | FeatureCollection, + points: Feature | FeatureCollection, polygons: Feature | FeatureCollection | G -): FeatureCollection; +): FeatureCollection; diff --git a/packages/turf-points-within-polygon/index.js b/packages/turf-points-within-polygon/index.js index 69b2a9bf0f..75cbde298c 100644 --- a/packages/turf-points-within-polygon/index.js +++ b/packages/turf-points-within-polygon/index.js @@ -1,14 +1,14 @@ import pointInPolygon from "@turf/boolean-point-in-polygon"; -import { featureCollection } from "@turf/helpers"; -import { geomEach, featureEach } from "@turf/meta"; +import { featureCollection, multiPoint } from "@turf/helpers"; +import { geomEach, featureEach, coordEach } from "@turf/meta"; /** - * Finds {@link Points} that fall within {@link (Multi)Polygon(s)}. + * Finds {@link Points} or {@link MultiPoint} coordinate positions that fall within {@link (Multi)Polygon(s)}. * * @name pointsWithinPolygon - * @param {Feature|FeatureCollection} points Points as input search - * @param {FeatureCollection|Geometry|Feature} polygons Points must be within these (Multi)Polygon(s) - * @returns {FeatureCollection} points that land within at least one polygon + * @param {Feature|FeatureCollection} points Point(s) or MultiPoint(s) as input search + * @param {FeatureCollection|Geometry|Feature} polygons (Multi)Polygon(s) to check if points are within + * @returns {FeatureCollection} Point(s) or MultiPoint(s) with positions that land within at least one polygon. The geometry type will match what was passsed in * @example * var points = turf.points([ * [-46.6318, -23.5523], @@ -41,11 +41,28 @@ function pointsWithinPolygon(points, polygons) { var results = []; featureEach(points, function (point) { var contained = false; - geomEach(polygons, function (polygon) { - if (pointInPolygon(point, polygon)) contained = true; - }); - if (contained) { - results.push(point); + if (point.geometry.type === "Point") { + geomEach(polygons, function (polygon) { + if (pointInPolygon(point, polygon)) contained = true; + }); + if (contained) { + results.push(point); + } + } else if (point.geometry.type === "MultiPoint") { + var pointsWithin = []; + geomEach(polygons, function (polygon) { + coordEach(point, function (pointCoord) { + if (pointInPolygon(pointCoord, polygon)) { + contained = true; + pointsWithin.push(pointCoord); + } + }); + }); + if (contained) { + results.push(multiPoint(pointsWithin)); + } + } else { + throw new Error("Input geometry must be a Point or MultiPoint"); } }); return featureCollection(results); diff --git a/packages/turf-points-within-polygon/test.js b/packages/turf-points-within-polygon/test.js index 0f967a9fdb..62826e7940 100644 --- a/packages/turf-points-within-polygon/test.js +++ b/packages/turf-points-within-polygon/test.js @@ -1,10 +1,10 @@ import test from "tape"; -import { point, points } from "@turf/helpers"; +import { multiPoint, point, points } from "@turf/helpers"; import { polygon } from "@turf/helpers"; import { featureCollection } from "@turf/helpers"; import pointsWithinPolygon from "./index"; -test("turf-points-within-polygon", (t) => { +test("turf-points-within-polygon -- point", (t) => { t.plan(4); // test with a single point @@ -59,7 +59,124 @@ test("turf-points-within-polygon", (t) => { t.equal(counted.features.length, 5, "multiple points in multiple polygons"); }); -test("turf-points-within-polygon -- support extra geometry", (t) => { +test("turf-points-within-polygon -- multipoint", (t) => { + t.plan(12); + + var poly1 = polygon([ + [ + [0, 0], + [0, 100], + [100, 100], + [100, 0], + [0, 0], + ], + ]); + + var mpt1 = multiPoint([[50, 50]]); // inside poly1 + var mpt2 = multiPoint([[150, 150]]); // outside poly1 + var mpt3 = multiPoint([ + [50, 50], + [150, 150], + ]); // inside and outside poly1 + var mpt1FC = featureCollection([mpt1]); + var polyFC = featureCollection([poly1]); + + // multipoint within + var mpWithin = pointsWithinPolygon(mpt1, polyFC); + t.ok( + mpWithin && mpWithin.type === "FeatureCollection", + "returns a featurecollection" + ); + t.equal(mpWithin.features.length, 1, "1 multipoint in 1 polygon"); + t.equal( + mpWithin.features[0].geometry.type, + "MultiPoint", + "1 multipoint with correct type" + ); + + // multipoint fc within + var fcWithin = pointsWithinPolygon(mpt1FC, polyFC); + t.ok( + fcWithin && fcWithin.type === "FeatureCollection", + "returns a featurecollection" + ); + t.equal(fcWithin.features.length, 1, "1 multipoint in 1 polygon"); + + // multipoint not within + var mpNotWithin = pointsWithinPolygon(mpt2, polyFC); + t.ok( + mpNotWithin && mpNotWithin.type === "FeatureCollection", + "returns an empty featurecollection" + ); + t.equal(mpNotWithin.features.length, 0, "0 multipoint in 1 polygon"); + + // multipoint with point coords both within and not within + var mpPartWithin = pointsWithinPolygon(mpt3, polyFC); + t.ok(mpPartWithin, "returns a featurecollection"); + var partCoords = mpPartWithin.features[0].geometry.coordinates; + t.equal( + partCoords.length, + 1, + "multipoint result should have 1 remaining coord that was within polygon" + ); + t.equal( + partCoords[0][0] === mpt3.geometry.coordinates[0][0] && + partCoords[0][1] === mpt3.geometry.coordinates[0][1], + true, + "remaining coord should have expected values" + ); + + // multiple multipoints and multiple polygons + + var poly2 = polygon([ + [ + [10, 0], + [20, 10], + [20, 20], + [20, 0], + [10, 0], + ], + ]); + var mptFC = featureCollection([mpt1, mpt2, mpt3]); + var poly2FC = featureCollection([poly1, poly2]); + + var fcMultiWithin = pointsWithinPolygon(mptFC, poly2FC); + t.ok(fcMultiWithin, "returns a featurecollection"); + t.equal( + fcMultiWithin.features.length, + 2, + "multiple points in multiple polygons" + ); +}); + +test("turf-points-within-polygon -- point and multipoint", (t) => { + t.plan(4); + + var poly = polygon([ + [ + [0, 0], + [0, 100], + [100, 100], + [100, 0], + [0, 0], + ], + ]); + var polyFC = featureCollection([poly]); + + var pt = point([50, 50]); + var mpt1 = multiPoint([[50, 50]]); // inside poly1 + var mpt2 = multiPoint([[150, 150]]); // outside poly1 + var mixedFC = featureCollection([pt, mpt1, mpt2]); + + var counted = pointsWithinPolygon(mixedFC, polyFC); + + t.ok(counted, "returns a featurecollection"); + t.equal(counted.features.length, 2, "1 point and 1 multipoint in 1 polygon"); + t.equal(counted.features[0].geometry.type, "Point"); + t.equal(counted.features[1].geometry.type, "MultiPoint"); +}); + +test("turf-points-within-polygon -- support extra point geometry", (t) => { const pts = points([ [-46.6318, -23.5523], [-46.6246, -23.5325], diff --git a/packages/turf-points-within-polygon/types.ts b/packages/turf-points-within-polygon/types.ts index 7253a2f1e6..2085c244b8 100644 --- a/packages/turf-points-within-polygon/types.ts +++ b/packages/turf-points-within-polygon/types.ts @@ -1,5 +1,12 @@ import pointsWithinPolygon from "./"; -import { points, polygon } from "@turf/helpers"; +import { + points, + polygon, + multiPoint, + featureCollection, + Point, + MultiPoint, +} from "@turf/helpers"; const pts = points([ [-46.6318, -23.5523], @@ -8,6 +15,22 @@ const pts = points([ [-46.663, -23.554], [-46.643, -23.557], ]); +const mpt1 = multiPoint( + [ + [50, 50], + [100, 100], + ], + {} +); +const mpt2 = multiPoint( + [ + [75, 75], + [150, 150], + ], + {} +); +const mpts = featureCollection([mpt1, mpt2]); + const searchWithin = polygon([ [ [-46.653, -23.543], @@ -20,3 +43,8 @@ const searchWithin = polygon([ ], ]); const ptsWithin = pointsWithinPolygon(pts, searchWithin); +const mptsWithin = pointsWithinPolygon(mpts, searchWithin); + +// Accepts a mixture of Point and MultiPoint +const mix = featureCollection([...pts.features, mpt1]); +const mixWithin = pointsWithinPolygon(mix, searchWithin);