Properly fix detection of 'point in polygon' with multipolygons, fixes GRB display issue

This commit is contained in:
pietervdvn 2022-02-11 15:27:20 +01:00
parent 08efcbdea0
commit 31024c5074

View file

@ -124,6 +124,20 @@ export class GeoOperations {
return result; return result;
} }
public static pointInPolygonCoordinates(x: number, y: number, coordinates: [number, number][][]) {
const inside = GeoOperations.pointWithinRing(x, y, /*This is the outer ring of the polygon */coordinates[0])
if (!inside) {
return false;
}
for (let i = 1; i < coordinates.length; i++) {
const inHole = GeoOperations.pointWithinRing(x, y, coordinates[i] /* These are inner rings, aka holes*/)
if (inHole) {
return false;
}
}
return true;
}
public static inside(pointCoordinate, feature): boolean { public static inside(pointCoordinate, feature): boolean {
// ray-casting algorithm based on // ray-casting algorithm based on
// http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
@ -136,62 +150,31 @@ export class GeoOperations {
pointCoordinate = pointCoordinate.geometry.coordinates pointCoordinate = pointCoordinate.geometry.coordinates
} }
if (feature.geometry.type === "MultiPolygon") {
const coordinates = feature.geometry.coordinates[0];
const outerPolygon = coordinates[0];
const inside = GeoOperations.inside(pointCoordinate, {
geometry: {
type: 'Polygon',
coordinates: [outerPolygon]
}
})
if (!inside) {
return false;
}
for (let i = 1; i < coordinates.length; i++) {
const inHole = GeoOperations.inside(pointCoordinate, {
geometry: {
type: 'Polygon',
coordinates: [coordinates[i]]
}
})
if (inHole) {
return false;
}
}
return true;
}
const x: number = pointCoordinate[0]; const x: number = pointCoordinate[0];
const y: number = pointCoordinate[1]; const y: number = pointCoordinate[1];
for (let i = 0; i < feature.geometry.coordinates.length; i++) {
let poly = feature.geometry.coordinates[i];
let inside = false; if (feature.geometry.type === "MultiPolygon") {
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { const coordinatess = feature.geometry.coordinates;
const coori = poly[i]; for (const coordinates of coordinatess) {
const coorj = poly[j]; const inThisPolygon = GeoOperations.pointInPolygonCoordinates(x, y, coordinates)
if (inThisPolygon) {
const xi = coori[0]; return true;
const yi = coori[1];
const xj = coorj[0];
const yj = coorj[1];
const intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) {
inside = !inside;
} }
} }
if (inside) { return false;
return true;
}
} }
return false;
}; if (feature.geometry.type === "Polygon") {
return GeoOperations.pointInPolygonCoordinates(x, y, feature.geometry.coordinates)
}
throw "GeoOperations.inside: unsupported geometry type"
}
static lengthInMeters(feature: any) { static lengthInMeters(feature: any) {
return turf.length(feature) * 1000 return turf.length(feature) * 1000
@ -587,6 +570,26 @@ export class GeoOperations {
} }
private static pointWithinRing(x: number, y: number, ring: [number, number][]) {
let inside = false;
for (let i = 0, j = ring.length - 1; i < ring.length; j = i++) {
const coori = ring[i];
const coorj = ring[j];
const xi = coori[0];
const yi = coori[1];
const xj = coorj[0];
const yj = coorj[1];
const intersect = ((yi > y) != (yj > y))
&& (x < (xj - xi) * (y - yi) / (yj - yi) + xi);
if (intersect) {
inside = !inside;
}
}
return inside;
}
/** /**
* Calculates the intersection between two features. * Calculates the intersection between two features.
* Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons * Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons