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

Add checks for (non-)simple polygons #634

Merged
merged 40 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
0d05256
Add check for invalid shapes
lehecht Aug 3, 2023
a821c82
Show message for invalid shapes
lehecht Aug 3, 2023
92ff004
Rename method for more clarity
lehecht Aug 3, 2023
f65c261
Remove case that is not reachable
lehecht Aug 9, 2023
b5078d1
Add event listener only once
lehecht Aug 9, 2023
af7dfc3
Simplify event listener method
lehecht Aug 9, 2023
2fdf82a
Concatenate x,y coordinates before size check
lehecht Aug 9, 2023
50c15ae
Remove duplicated coordinates
lehecht Aug 10, 2023
e9ac4f6
Remove superfluous checks for rectangles, ellipses and lines
lehecht Sep 22, 2023
66f4040
Move magic wand methods to MagicWandInteraction.js
lehecht Oct 2, 2023
0940d4c
Add jsts library
lehecht Oct 2, 2023
b164600
Disallow self intersecting polygons
lehecht Oct 11, 2023
0c7a70d
Remove unused imports
lehecht Oct 12, 2023
1c2ba0b
Move polygon validation methods to own class
lehecht Oct 13, 2023
1894844
Rename event for more clarity
lehecht Oct 13, 2023
ca61495
Add polygon validation for video annotations
lehecht Oct 13, 2023
1457ae2
Fix bug with nested polygon parts
lehecht Oct 16, 2023
0316882
Format code
lehecht Oct 16, 2023
7781541
Use value instead of variable
lehecht Oct 17, 2023
43f564d
Remove superfluous magicwand polygon validation
lehecht Oct 17, 2023
0c8b012
Move polygon validation call to drawInteractions.vue
lehecht Oct 17, 2023
669e94c
Transform self-intersecting polygons to simple ones
lehecht Oct 27, 2023
34f3884
Fix missing/unused imports
lehecht Oct 27, 2023
a8ddfd1
Remove unused method
lehecht Oct 27, 2023
3f41193
Remove unused methods
lehecht Oct 27, 2023
66f2fe0
Make polygonValiator a static class
lehecht Oct 30, 2023
398a5ae
Remove redundant return statement
lehecht Nov 2, 2023
4abce21
Replace class export by methods export
lehecht Nov 2, 2023
4996b71
Apply changes from code review
mzur Nov 7, 2023
27a7055
Convert JSTS Monkey import to side-effect import
mzur Nov 7, 2023
86dfe03
Remove unnecessary if-else condition
lehecht Nov 8, 2023
99758f2
Fix bug with intersecting polygons after modification
lehecht Nov 10, 2023
bcefe6d
Rename method to shorten name
lehecht Nov 14, 2023
bdeb64c
Add comments
lehecht Nov 14, 2023
f1e7730
Add comments
lehecht Nov 16, 2023
86b5cea
Make code more readable
lehecht Nov 16, 2023
7a340c6
Add Polygon check after modifying video polygons
lehecht Nov 16, 2023
4fc7573
Edit comments
lehecht Nov 16, 2023
9db8277
Implement alternative handling of polygons with holes
mzur Nov 17, 2023
fabfa21
Merge pull request #700 from biigle/non-simple-polygon-bug-patch-1
mzur Nov 21, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,7 @@ export default {
return this.featureRevisionMap[feature.getId()] !== feature.getRevision();
})
.map((feature) => {
PolygonValidator.simplifyPolygon(feature);
return {
id: feature.getId(),
image_id: feature.get('annotation').image_id,
Expand Down Expand Up @@ -526,7 +527,7 @@ export default {
return;
}

PolygonValidator.makePolygonSimple(e.feature);
PolygonValidator.simplifyPolygon(e.feature);
}

e.feature.set('color', this.selectedLabel.color);
Expand Down
76 changes: 60 additions & 16 deletions resources/assets/js/annotations/ol/PolygonValidator.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'jsts/org/locationtech/jts/monkey'; // This monkey patches jsts prototypes.
import JstsLinearRing from 'jsts/org/locationtech/jts/geom/LinearRing';
import JstsPolygon from 'jsts/org/locationtech/jts/geom/Polygon';
import LinearRing from '@biigle/ol/geom/LinearRing';
import LineString from '@biigle/ol/geom/LineString';
import MultiLineString from '@biigle/ol/geom/MultiLineString';
Expand Down Expand Up @@ -29,7 +28,7 @@ export function isInvalidPolygon(feature) {
*
* @param feature feature containing the (non-simple) polygon
*/
export function makePolygonSimple(feature) {
export function simplifyPolygon(feature) {
if (feature.getGeometry().getType() !== 'Polygon') {
throw new Error("Only polygon geometries are supported.");
}
Expand All @@ -53,9 +52,9 @@ export function makePolygonSimple(feature) {
return feature;
}

// Divide non-simple polygon at cross points into several smaller polygons
// and translate back to ol geometry
let polygons = jstsValidate(jstsPolygon)['array'].map(p => parser.write(p));
// Divide non-simple polygon at cross points into several smaller polygons,
// translate back to ol geometry and polish them
let polygons = polishPolygons(jstsValidate(jstsPolygon).array.map(p => parser.write(p)));
mzur marked this conversation as resolved.
Show resolved Hide resolved

if (polygons.length > 1) {
// Select biggest part
Expand All @@ -78,18 +77,14 @@ export function makePolygonSimple(feature) {
* @return a geometry
*/
function jstsValidate(geom) {
if (geom instanceof JstsPolygon) {
if (geom.isValid()) {
geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
return geom; // If the polygon is valid just return it
}
var polygonizer = new Polygonizer();
jstsAddPolygon(geom, polygonizer);

return polygonizer.getPolygons();
} else {
return geom;
if (geom.isValid()) {
geom.normalize(); // validate does not pick up rings in the wrong order - this will fix that
return geom; // If the polygon is valid just return it
}
var polygonizer = new Polygonizer();
jstsAddPolygon(geom, polygonizer);

return polygonizer.getPolygons();
}

/**
Expand All @@ -106,6 +101,7 @@ function jstsAddPolygon(polygon, polygonizer) {
jstsAddLineString(polygon.getExteriorRing(), polygonizer);

for (var n = polygon.getNumInteriorRing(); n > 0; n--) {
n = polygon.getNumInteriorRing() === 1 ? 0 : n;
mzur marked this conversation as resolved.
Show resolved Hide resolved
jstsAddLineString(polygon.getInteriorRingN(n), polygonizer);
}
}
Expand Down Expand Up @@ -134,6 +130,54 @@ function jstsAddLineString(lineString, polygonizer) {
polygonizer.add(toAdd);
}

/**
* Removes duplicated subpolygon's coordinates which can be still included in some polygons.
*
* @param polygons List of (multi)polygons
* @returns List of separated polygons
*
* **/
function polishPolygons(polygons) {
mzur marked this conversation as resolved.
Show resolved Hide resolved
// Filter all multipolygons that contain two or more coordinate lists
let multiPolygons = polygons.filter(p => p.getCoordinates().length > 1);

if (multiPolygons.length === 0) {
return polygons;
}

multiPolygons.forEach(p => {
let pCoords = p.getCoordinates();
for (let i = 0; i < polygons.length; i++) {
let otherP = polygons[i]
if (p !== otherP) {
// subCoords can contain only one coordinate list
let subCoords = pCoords.filter(coords => areEqual(coords, otherP.getCoordinates()[0]));
// If subpolygon's coordinates are duplicates, remove them
if (subCoords.length > 0) {
pCoords.splice(pCoords.indexOf(subCoords[0]), 1);
p.setCoordinates(pCoords);
}
}
}
});
return polygons;
}

/**
* Check if two arrays have equal content
*
* @param a1 first array
* @param a2 second array
*
* @returns True if arrays have equal content otherwise false
*
* **/
function areEqual(a1, a2) {
let a1Strings = a1.map(xy => JSON.stringify(xy));
let a2Strings = a2.map(xy => JSON.stringify(xy));
return a1Strings.every(xy => a2Strings.includes(xy));
}

/**
* Determine polygon with largest area
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ export default {
}

// If polygon is self-intersecting, create simple polygon
PolygonValidator.makePolygonSimple(e.feature);
PolygonValidator.simplifyPolygon(e.feature);
}

let lastFrame = this.pendingAnnotation.frames[this.pendingAnnotation.frames.length - 1];
Expand Down