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} coordinates 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 (Multi)Point(s) as input search
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The typescript version of this would be Feature<Point|MultiPoint> | FeatureCollection<Point|Multipoint> but maybe this is fine for JSDoc?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, as a TS user myself, your suggestion is more correct but looking at the docs for different functions I sensed there was an established convention already. I don't mind changing them, I would just change the params too to match. Since each type becomes a link in the docs, maybe it was done for simplicity.

image

* @param {FeatureCollection|Geometry|Feature<Polygon|MultiPolygon>} polygons (Multi)Polygon(s) to check if points are within
* @returns {FeatureCollection<Point>} (Multi)Points that land within at least one polygon
* @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
96 changes: 93 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 -- single point", (t) => {
t.plan(4);

// test with a single point
Expand Down Expand Up @@ -59,7 +59,97 @@ 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 -- single multipoint", (t) => {
t.plan(12);

const poly1 = polygon([
[
[0, 0],
[0, 100],
[100, 100],
[100, 0],
[0, 0],
],
]);

const mpt1 = multiPoint([[50, 50]]); // inside poly1
const mpt2 = multiPoint([[150, 150]]); // outside poly1
const mpt3 = multiPoint([
[50, 50],
[150, 150],
]); // inside and outside poly1
const mpt1FC = featureCollection([mpt1]);
const polyFC = featureCollection([poly1]);

// multipoint within
const 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
const 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
const 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
const mpPartWithin = pointsWithinPolygon(mpt3, polyFC);
t.ok(mpPartWithin, "returns a featurecollection");
const 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

const 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 -- support extra point geometry", (t) => {
const pts = points([
[-46.6318, -23.5523],
[-46.6246, -23.5325],
Expand Down
19 changes: 18 additions & 1 deletion packages/turf-points-within-polygon/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import pointsWithinPolygon from "./";
import { points, polygon } from "@turf/helpers";
import { points, polygon, multiPoint, featureCollection } from "@turf/helpers";

const pts = points([
[-46.6318, -23.5523],
Expand All @@ -8,6 +8,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 +36,4 @@ const searchWithin = polygon([
],
]);
const ptsWithin = pointsWithinPolygon(pts, searchWithin);
const mptsWithin = pointsWithinPolygon(mpts, searchWithin);