Skip to content

Commit

Permalink
pointsWithinPolygon: add MultiPoint support (#2137)
Browse files Browse the repository at this point in the history
* pointsWithinPolygon: add multipoint support

* add multipoint coord filtering

* update jsdoc

* doc consolidate

* tighten up comparison

* update types

* fix return type

* change let to var for linter

* add point geom generic

* doc fixes

* add mixed example

* update jsdoc

* add mixed test
  • Loading branch information
twelch authored Jul 6, 2021
1 parent 630db1e commit 779ac05
Show file tree
Hide file tree
Showing 4 changed files with 181 additions and 17 deletions.
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>;
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 = [];
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);

0 comments on commit 779ac05

Please sign in to comment.