-
Notifications
You must be signed in to change notification settings - Fork 944
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
Distance to Polygon / MultiPolygon from Point #1743
Comments
Thanks for reopening @lostpebble |
Hi @lostpebble , |
Hi @slachtar , I did manage to come up with a solution: import {
booleanPointInPolygon,
MultiPolygon,
Point,
pointToLineDistance,
polygon as makePolygon,
Polygon,
polygonToLineString,
} from "@turf/turf";
interface IODistanceToPolygonInput {
point: Point;
polygon: Polygon | MultiPolygon;
}
// Returns distance in meters (negative values for points inside) from a point to the edges of a polygon
export function distanceToPolygon_direct({ point, polygon }: IODistanceToPolygonInput): number {
let distance: number;
if (polygon.type === "MultiPolygon") {
distance = polygon.coordinates
.map(coords => distanceToPolygon_direct({ point, polygon: makePolygon(coords).geometry! }))
.reduce((smallest, current) => (current < smallest ? current : smallest));
} else {
if (polygon.coordinates.length > 1) {
// Has holes
const [exteriorDistance, ...interiorDistances] = polygon.coordinates.map(coords =>
distanceToPolygon_direct({ point, polygon: makePolygon([coords]).geometry! })
);
if (exteriorDistance < 0) {
// point is inside the exterior polygon shape
const smallestInteriorDistance = interiorDistances.reduce(
(smallest, current) => (current < smallest ? current : smallest)
);
if (smallestInteriorDistance < 0) {
// point is inside one of the holes (therefore not actually inside this shape)
distance = smallestInteriorDistance * -1;
} else {
// find which is closer, the distance to the hole or the distance
// to the edge of the exterior, and set that as the inner distance.
distance =
smallestInteriorDistance < exteriorDistance * -1
? smallestInteriorDistance * -1
: exteriorDistance;
}
} else {
distance = exteriorDistance;
}
} else {
// The actual distance operation - on a normal, hole-less polygon (converted to meters)
distance = pointToLineDistance(point, polygonToLineString(polygon)) * 1000;
if (booleanPointInPolygon(point, polygon)) {
distance = distance * -1;
}
}
}
return distance;
} Basically, for multi-polygons we need to iterate over their inner polygons and run the algorithm on each one - and then we pull out the smallest distance (which could also be negative, showing the deepest distance this point is inside the polygon). Its been a while since I've reviewed this part of my code, but I remember it working without any issues. Hope that helps! |
@lostpebble thank you, nice work, will try it out for my use case. |
Seems to work nicely. Thank you @lostpebble ! In case someone sees this and just wants to use it in place without having to recompile turf, this version works with turf ~5.1.6:
|
@pachacamac nicely done, very helpful, thank you |
Thanks for sharing your code @lostpebble and @pachacamac 🙌 I made a typescript version of the function if anyone would want to copy-paste it. |
This is great! I wish it would get merged into turf. |
Here's another solution which is a bit more verbose, but also easier to understand:
|
any plans for merging this into turf into a new module |
@pachacamac or @lostpebble maybe you can do some PRs? |
@kachkaev I don't domain TS and turf modules are written in TS Would you be so kind to open a PR with this new very useful function ? Check for example You would only need to adapt this into a new folder The TURF community thank you so very much ;) |
Hi @jfoclpf! Glad that you are open to accept this change! I don’t have a lot of extra capacity in the coming weeks, so happy for you copy-paste my solution into a PR (hope that it does not need much tweaking). If anyone else reading this would like to volunteer, feel free to step up! 🙌 |
Just for the clarification, I am not the maintainer of the package, just an end user :) |
Once #2717 (which includes fixes to pointToLineDistance) is merged will look into bringing one of the implementations above into Turf. |
@GeekyMonkey I think in your solution both
|
@kachkaev @lostpebble @pachacamac @jampy @GeekyMonkey Would one of you like to take the lead on a PR for this? |
@smallsaucepan I can probably do that. What would be the preferred way? I'm thinking new package similar to https://github.com/Turfjs/turf/tree/master/packages/turf-point-to-line-distance but point-to-polygon-distance? |
That sounds great @pachacamac. Really appreciate you taking a look! We could go a couple of ways:
What are your thoughts? Pros and cons that you can see? |
@smallsaucepan for easier testability, readability (not a fan of big if/case blocks) and maybe tree-shakeablity I think I would much prefer the first option. |
Happy to go with option 1. Only wanted to go through the thought process first to make sure we aren't over splitting the namespace. Looking through old issues I couldn't find anything requesting nearestLineToWhatever or nearestPolygonToWhatever anyway, so three nearestPointToWhatever functions isn't so bad. Looking at the original use case I've realised it's really asking for nearestPointToPolygon, rather than only distanceToPolygon. Would you be ok to add the pair - pointToPolygonDistance and nearestPointToPolygon? When you get under way, I'm not sure how up to date scripts/create-new-module is. You may be best off copying an existing package sideways. |
@smallsaucepan please have a look #2735 This should be the basic stuff, with tests (and some additional fixes for bugs discovered by the tests). Also thanks to everyone else who contributed! Feel free to add yourselves to the contributors. @kachkaev @lostpebble @jampy @GeekyMonkey |
Added turf-point-to-polygon-distance package to complement point-to-line functionality. Resolves #1743 --------- Co-authored-by: James Beard <[email protected]>
Opened new issue as requested by @rowanwins from #252 .
Currently there is no native way to achieve distance to Polygon (and Multi-Polygon) from a point using Turf.
There was a suggestion in the previous issue to use the vertexes and measure the distance to those, but we end up with the following issue:
In this image, B is clearly the closest point to the polygon, but using the above algorithm we'd end up with point A.
I've come up with the following solution for now:
Using this over an array of Polygons allows me to achieve what I want. (I want to compare the distances of an array of Polygons to a Point and pick out the one which is the closest).
Still need to add support for
MultiPolygon
s (aspolygonToLineString
doesn't support them) - but that's as easy as splitting the geometry's coordinate array into multiple regularPolygons
and finding the shortest distance within that group and choosing the shortest.Since there is no direct function for Polygons, it makes use of
polygonToLineString()
to convert them to lines first and run the distance checks on that.If the point is inside the Polygon, I return the negative value of the distance (how far inside the polygon the point is).
The text was updated successfully, but these errors were encountered: