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

pointsWithinPolygon: add MultiPoint support #2137

Merged
merged 13 commits into from
Jul 6, 2021
6 changes: 4 additions & 2 deletions packages/turf-points-within-polygon/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
FeatureCollection,
Polygon,
MultiPolygon,
MultiPoint,
Point,
Properties,
} from "@turf/helpers";
Expand All @@ -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<Point, P> | FeatureCollection<Point, P>,
points: Feature<F, P> | FeatureCollection<F, P>,
polygons: Feature<G> | FeatureCollection<G> | G
): FeatureCollection<Point, P>;
): FeatureCollection<F, P>;
twelch marked this conversation as resolved.
Show resolved Hide resolved
39 changes: 28 additions & 11 deletions packages/turf-points-within-polygon/index.js
Original file line number Diff line number Diff line change
@@ -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<Point>} points Points as input search
* @param {FeatureCollection|Geometry|Feature<Polygon|MultiPolygon>} polygons Points must be within these (Multi)Polygon(s)
* @returns {FeatureCollection<Point>} points that land within at least one polygon
* @param {Feature|FeatureCollection<Point|MultiPoint>} points Point(s) or MultiPoint(s) as input search
* @param {FeatureCollection|Geometry|Feature<Polygon|MultiPolygon>} polygons (Multi)Polygon(s) to check if points are within
* @returns {FeatureCollection<Point|MultiPoint>} 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],
Expand Down Expand Up @@ -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 = [];
twelch marked this conversation as resolved.
Show resolved Hide resolved
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);
Expand Down
123 changes: 120 additions & 3 deletions packages/turf-points-within-polygon/test.js
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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],
Expand Down
30 changes: 29 additions & 1 deletion packages/turf-points-within-polygon/types.ts
Original file line number Diff line number Diff line change
@@ -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],
Expand All @@ -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],
Expand All @@ -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<Point | MultiPoint>([...pts.features, mpt1]);
const mixWithin = pointsWithinPolygon(mix, searchWithin);