Search in sources :

Example 1 with IsValidOp

use of org.locationtech.jts.operation.valid.IsValidOp in project presto by prestodb.

the class GeometryUtils method getGeometryInvalidReason.

public static Optional<String> getGeometryInvalidReason(org.locationtech.jts.geom.Geometry geometry) {
    IsValidOp validOp = new IsValidOp(geometry);
    IsSimpleOp simpleOp = new IsSimpleOp(geometry);
    try {
        TopologyValidationError err = validOp.getValidationError();
        if (err != null) {
            return Optional.of(err.getMessage());
        }
    } catch (UnsupportedOperationException e) {
        // It should not happen in practice.
        throw new PrestoException(INVALID_FUNCTION_ARGUMENT, "Geometry type not valid", e);
    }
    if (!simpleOp.isSimple()) {
        String errorDescription;
        String geometryType = geometry.getGeometryType();
        switch(GeometryType.getForJtsGeometryType(geometryType)) {
            case POINT:
                errorDescription = "Invalid point";
                break;
            case MULTI_POINT:
                errorDescription = "Repeated point";
                break;
            case LINE_STRING:
            case MULTI_LINE_STRING:
                errorDescription = "Self-intersection at or near";
                break;
            case POLYGON:
            case MULTI_POLYGON:
            case GEOMETRY_COLLECTION:
                // In OGC (which JTS follows): Polygons, MultiPolygons, Geometry Collections are simple.
                // This shouldn't happen, but in case it does, return a reasonable generic message.
                errorDescription = "Topology exception at or near";
                break;
            default:
                throw new PrestoException(INVALID_FUNCTION_ARGUMENT, format("Unknown geometry type: %s", geometryType));
        }
        org.locationtech.jts.geom.Coordinate nonSimpleLocation = simpleOp.getNonSimpleLocation();
        return Optional.of(format("[%s] %s: (%s %s)", geometryType, errorDescription, nonSimpleLocation.getX(), nonSimpleLocation.getY()));
    }
    return Optional.empty();
}
Also used : IsSimpleOp(org.locationtech.jts.operation.IsSimpleOp) IsValidOp(org.locationtech.jts.operation.valid.IsValidOp) Coordinate(org.locationtech.jts.geom.Coordinate) TopologyValidationError(org.locationtech.jts.operation.valid.TopologyValidationError) PrestoException(com.facebook.presto.spi.PrestoException)

Example 2 with IsValidOp

use of org.locationtech.jts.operation.valid.IsValidOp in project vertexium by visallo.

the class GeoUtils method toJtsPolygon.

public static Geometry toJtsPolygon(List<GeoPoint> outerBoundary, List<List<GeoPoint>> holeBoundaries, boolean lenient) {
    LinearRing shell = toJtsLinearRing(outerBoundary, true, lenient);
    LinearRing[] holes = holeBoundaries == null ? new LinearRing[0] : holeBoundaries.stream().map(holeBoundary -> toJtsLinearRing(holeBoundary, false, lenient)).toArray(LinearRing[]::new);
    Geometry polygon = GEOMETRY_FACTORY.createPolygon(shell, holes);
    TopologyValidationError validationError = new IsValidOp(polygon).getValidationError();
    if (validationError != null) {
        if (lenient) {
            LOGGER.info("Attempting to repair and normalize an invalid polygon.");
            // NOTE: there seems to be a bug in JTS where normalizing puts the paths backwards. Reverse as a hack for now.
            polygon = polygon.buffer(0).norm().reverse();
        } else {
            throw new VertexiumInvalidShapeException(validationError.toString());
        }
    }
    return polygon;
}
Also used : IsValidOp(org.locationtech.jts.operation.valid.IsValidOp) TopologyValidationError(org.locationtech.jts.operation.valid.TopologyValidationError)

Example 3 with IsValidOp

use of org.locationtech.jts.operation.valid.IsValidOp in project qupath by qupath.

the class TestContourTracing method testImage.

static void testImage(BufferedImage img) throws Exception {
    assertEquals(BufferedImage.TYPE_BYTE_GRAY, img.getType());
    // Create a histogram - entries will be used for object areas
    var pixels = img.getRaster().getSamples(0, 0, img.getWidth(), img.getHeight(), 0, (int[]) null);
    int max = Arrays.stream(pixels).max().orElse(0);
    int[] hist = new int[max + 1];
    for (int p : pixels) hist[p]++;
    // Check for a simple wrapped image server
    for (int i = 0; i < max; i++) {
        var geom = ContourTracing.createTracedGeometry(img.getRaster(), i, i, 0, null);
        assertEquals(hist[i], geom.getArea(), 0.000001);
        if (alwaysCheckValidity || geom.getNumPoints() < MAX_POINTS_FOR_VALIDITY) {
            var error = new IsValidOp(geom).getValidationError();
            if (error != null)
                logger.warn("{}", error);
            assertNull(error);
        } else
            logger.debug("Validity check skipped ({} points)", geom.getNumPoints());
    }
}
Also used : IsValidOp(org.locationtech.jts.operation.valid.IsValidOp)

Example 4 with IsValidOp

use of org.locationtech.jts.operation.valid.IsValidOp in project qupath by qupath.

the class TestPixelClassifierTools method checkCreateObjects.

private void checkCreateObjects(ImageServer<BufferedImage> server, int[] hist, Map<PathClass, Integer> classificationLabelsReverse) throws IOException {
    var hierarchy = new PathObjectHierarchy();
    boolean success = PixelClassifierTools.createObjectsFromPredictions(server, hierarchy, Collections.singleton(hierarchy.getRootObject()), r -> PathObjects.createAnnotationObject(r), 0, 0);
    assertTrue(success);
    // Recall that we have an object for zero as well
    var annotations = new ArrayList<>(hierarchy.getAnnotationObjects());
    assertEquals(hist.length, annotations.size());
    // Check areas for all our annotations
    Collections.sort(annotations, Comparator.comparingInt(a -> classificationLabelsReverse.get(a.getPathClass())));
    for (var annotation : annotations) {
        int label = classificationLabelsReverse.get(annotation.getPathClass());
        var roi = annotation.getROI();
        double area = roi.getArea();
        if (printAreas)
            logger.debug(hist[label] + ": \t" + area);
        assertEquals(hist[label], area);
        var geom = roi.getGeometry();
        if (alwaysCheckValidity || geom.getNumPoints() < MAX_POINTS_FOR_VALIDITY) {
            var error = new IsValidOp(geom).getValidationError();
            if (error != null)
                logger.warn("{}", error);
            assertNull(error);
        }
        assertEquals(hist[label], geom.getArea());
    }
}
Also used : CreateObjectOptions(qupath.opencv.ml.pixel.PixelClassifierTools.CreateObjectOptions) RoiLabeling(qupath.imagej.processing.RoiLabeling) Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) ByteProcessor(ij.process.ByteProcessor) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) PathClassFactory(qupath.lib.objects.classes.PathClassFactory) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) DataBufferByte(java.awt.image.DataBufferByte) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) ImageStatistics(ij.process.ImageStatistics) ChannelType(qupath.lib.images.servers.ImageServerMetadata.ChannelType) Map(java.util.Map) ImageIO(javax.imageio.ImageIO) ImageServers(qupath.lib.images.servers.ImageServers) Path(java.nio.file.Path) MethodSource(org.junit.jupiter.params.provider.MethodSource) IsValidOp(org.locationtech.jts.operation.valid.IsValidOp) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) PathObjects(qupath.lib.objects.PathObjects) PathClass(qupath.lib.objects.classes.PathClass) IOException(java.io.IOException) UUID(java.util.UUID) ParameterizedTest(org.junit.jupiter.params.ParameterizedTest) Assertions(org.junit.jupiter.api.Assertions) WrappedBufferedImageServer(qupath.lib.images.servers.WrappedBufferedImageServer) Comparator(java.util.Comparator) Collections(java.util.Collections) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) ArrayList(java.util.ArrayList) IsValidOp(org.locationtech.jts.operation.valid.IsValidOp)

Example 5 with IsValidOp

use of org.locationtech.jts.operation.valid.IsValidOp in project qupath by qupath.

the class GeometryTools method tryToFixPolygon.

/**
 * Test a polygon for validity, attempting to fix TopologyValidationErrors if possible.
 * This attempts a range of tricks (starting with Geometry.buffer(0)), although none
 * are guaranteed to work. The first that largely preserves the polygon's area is returned.
 * <p>
 * The result is guaranteed to be valid, but not necessarily to be a close match to the
 * original polygon; in particular, if everything failed the result will be empty.
 * <p>
 * Code that calls this method can test if the output is equal to the input to determine
 * if any changes were made.
 *
 * @param polygon input (possibly-invalid) polygon
 * @return the input polygon (if valid), an adjusted polygon (if attempted fixes helped),
 *         or an empty polygon if the situation could not be resolved
 */
public static Geometry tryToFixPolygon(Polygon polygon) {
    TopologyValidationError error = new IsValidOp(polygon).getValidationError();
    if (error == null)
        return polygon;
    logger.debug("Invalid polygon detected! Attempting to correct {}", error.toString());
    // Area calculations seem to be reliable... even if the topology is invalid
    double areaBefore = polygon.getArea();
    double tol = 0.0001;
    // Try fast buffer trick to make valid (but sometimes this can 'break', e.g. with bow-tie shapes)
    Geometry geomBuffered = polygon.buffer(0);
    double areaBuffered = geomBuffered.getArea();
    if (geomBuffered.isValid() && GeneralTools.almostTheSame(areaBefore, areaBuffered, tol))
        return geomBuffered;
    // If the buffer trick gave us an exceedingly small area, try removing this and see if that resolves things
    if (!geomBuffered.isEmpty() && areaBuffered < areaBefore * 0.001) {
        try {
            Geometry geomDifference = polygon.difference(geomBuffered);
            if (geomDifference.isValid())
                return geomDifference;
        } catch (Exception e) {
            logger.debug("Attempting to fix by difference failed: " + e.getLocalizedMessage(), e);
        }
    }
    // Resort to the slow method of fixing polygons if we have to
    logger.debug("Unable to fix Geometry with buffer(0) - will try snapToSelf instead");
    double distance = GeometrySnapper.computeOverlaySnapTolerance(polygon);
    Geometry geomSnapped = GeometrySnapper.snapToSelf(polygon, distance, true);
    if (geomSnapped.isValid())
        return geomSnapped;
    // If everything failed, return an empty polygon (which will at least be valid...)
    return polygon.getFactory().createPolygon();
}
Also used : Geometry(org.locationtech.jts.geom.Geometry) IsValidOp(org.locationtech.jts.operation.valid.IsValidOp) TopologyValidationError(org.locationtech.jts.operation.valid.TopologyValidationError) ParseException(java.text.ParseException) TopologyException(org.locationtech.jts.geom.TopologyException)

Aggregations

IsValidOp (org.locationtech.jts.operation.valid.IsValidOp)6 TopologyValidationError (org.locationtech.jts.operation.valid.TopologyValidationError)4 PrestoException (com.facebook.presto.spi.PrestoException)2 Coordinate (org.locationtech.jts.geom.Coordinate)2 IsSimpleOp (org.locationtech.jts.operation.IsSimpleOp)2 ByteProcessor (ij.process.ByteProcessor)1 ImageStatistics (ij.process.ImageStatistics)1 BufferedImage (java.awt.image.BufferedImage)1 DataBufferByte (java.awt.image.DataBufferByte)1 IOException (java.io.IOException)1 Path (java.nio.file.Path)1 ParseException (java.text.ParseException)1 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 Collections (java.util.Collections)1 Comparator (java.util.Comparator)1 HashMap (java.util.HashMap)1 LinkedHashMap (java.util.LinkedHashMap)1 Map (java.util.Map)1 UUID (java.util.UUID)1