Search in sources :

Example 1 with TopologyValidationError

use of org.locationtech.jts.operation.valid.TopologyValidationError 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 TopologyValidationError

use of org.locationtech.jts.operation.valid.TopologyValidationError 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 TopologyValidationError

use of org.locationtech.jts.operation.valid.TopologyValidationError 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)

Example 4 with TopologyValidationError

use of org.locationtech.jts.operation.valid.TopologyValidationError in project urban-eureka by errir503.

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)

Aggregations

IsValidOp (org.locationtech.jts.operation.valid.IsValidOp)4 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 ParseException (java.text.ParseException)1 Geometry (org.locationtech.jts.geom.Geometry)1 TopologyException (org.locationtech.jts.geom.TopologyException)1