From 31024c50741a33d85fb846699a373817224cb7c3 Mon Sep 17 00:00:00 2001 From: pietervdvn Date: Fri, 11 Feb 2022 15:27:20 +0100 Subject: [PATCH] Properly fix detection of 'point in polygon' with multipolygons, fixes GRB display issue --- Logic/GeoOperations.ts | 99 ++++++++++++++++++++++-------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/Logic/GeoOperations.ts b/Logic/GeoOperations.ts index a6f17c773..6b0945e1b 100644 --- a/Logic/GeoOperations.ts +++ b/Logic/GeoOperations.ts @@ -124,6 +124,20 @@ export class GeoOperations { 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 { // ray-casting algorithm based on // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html @@ -136,62 +150,31 @@ export class GeoOperations { 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 y: number = pointCoordinate[1]; - for (let i = 0; i < feature.geometry.coordinates.length; i++) { - let poly = feature.geometry.coordinates[i]; - let inside = false; - for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) { - const coori = poly[i]; - const coorj = poly[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; + if (feature.geometry.type === "MultiPolygon") { + const coordinatess = feature.geometry.coordinates; + for (const coordinates of coordinatess) { + const inThisPolygon = GeoOperations.pointInPolygonCoordinates(x, y, coordinates) + if (inThisPolygon) { + return true; } + } - if (inside) { - return true; - } + return false; } - 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) { 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. * Returns the length if intersecting a linestring and a (multi)polygon (in meters), returns a surface area (in m²) if intersecting two (multi)polygons