org.kalypso.jts.JTSUtilities.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.jts.JTSUtilities.java

Source

/*----------------    FILE HEADER KALYPSO ------------------------------------------
 *
 *  This file is part of kalypso.
 *  Copyright (C) 2004 by:
 *
 *  Technical University Hamburg-Harburg (TUHH)
 *  Institute of River and coastal engineering
 *  Denickestrae 22
 *  21073 Hamburg, Germany
 *  http://www.tuhh.de/wb
 *
 *  and
 *
 *  Bjoernsen Consulting Engineers (BCE)
 *  Maria Trost 3
 *  56070 Koblenz, Germany
 *  http://www.bjoernsen.de
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *
 *  E-Mail:
 *  belger@bjoernsen.de
 *  schlienger@bjoernsen.de
 *  v.doemming@tuhh.de
 *
 *  ---------------------------------------------------------------------------*/
package org.kalypso.jts;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.core.runtime.Assert;
import org.kalypso.commons.java.lang.MathUtils;
import org.kalypso.commons.java.lang.Objects;
import org.kalypso.commons.math.LinearEquation;
import org.kalypso.commons.math.LinearEquation.SameXValuesException;
import org.kalypso.commons.math.geom.PolyLine;
import org.kalypsodeegree.model.geometry.GM_Envelope;
import org.kalypsodeegree_impl.model.geometry.JTSAdapter;

import com.infomatiq.jsi.Rectangle;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateList;
import com.vividsolutions.jts.geom.CoordinateSequence;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineSegment;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.MultiLineString;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.util.GeometryEditor;
import com.vividsolutions.jts.linearref.LengthIndexedLine;
import com.vividsolutions.jts.operation.valid.IsValidOp;
import com.vividsolutions.jts.operation.valid.TopologyValidationError;

/**
 * Utility class for some geometry operations.
 * 
 * @author Holger Albert
 */
public final class JTSUtilities {
    /**
     * The allowed tolerance.
     */
    // FIXME: bad, rather use precisions model of JTS
    public static final double TOLERANCE = 10E-04;

    private JTSUtilities() {
        throw new UnsupportedOperationException();
    }

    /**
     * This function delivers the first point from a line in another geometry.
     * 
     * @param line
     *          The points of this line will be checked. The first, which lies in the given geometry is returned.
     * @param geometry_2nd
     *          The points of the line will be checked with this geometry.
     * @return The first point of the line, which lies in the second geometry.
     */
    public static Point linePointInGeometry(final LineString line, final Geometry geometry_2nd) {
        final int numPoints = line.getNumPoints();

        for (int i = 0; i < numPoints; i++) {
            final Point pointN = line.getPointN(i);

            if (geometry_2nd.contains(pointN)) {
                final GeometryFactory factory = new GeometryFactory(pointN.getPrecisionModel(), pointN.getSRID());
                return factory.createPoint(new Coordinate(pointN.getCoordinate()));
            }
        }

        return null;
    }

    /**
     * This function calculates the distance from the start point to a point, lying on the line.
     * 
     * @param line
     *          The line.
     * @param point
     *          One point lying on the line.
     * @return The distance of the point on the line.
     * @deprecated This method is too slow for long chains. Use {@link com.vividsolutions.jts.linearref.LengthIndexedLine} instead. And does not work for last line segment!!!!
     */
    @Deprecated
    public static double pointDistanceOnLine(final LineString line, final Point point) {
        /* Check for intersection. */
        if (point.distance(line) >= TOLERANCE)
            throw new IllegalStateException("The point does not lie on the line...");

        if (line.getEndPoint().equals(point))
            return line.getLength();

        /* The needed factory. */
        final GeometryFactory factory = new GeometryFactory(line.getPrecisionModel(), line.getSRID());

        /* Get all coordinates. */
        final Coordinate[] coordinates = line.getCoordinates();

        /* Only loop until the one before the last one. */
        for (int i = 0; i < coordinates.length - 2; i++) {
            /* Get the coordinates to the current one + 1. */
            final Coordinate[] coords = ArrayUtils.subarray(coordinates, 0, i + 2);

            /* Create a new line with the coordinates. */
            final LineString ls = factory.createLineString(coords);
            if (point.distance(ls) >= TOLERANCE)
                continue;

            /* Point was intersecting the last segment, now take all coordinates but the last one ... */
            final LinkedList<Coordinate> lineCoords = new LinkedList<>();
            for (int j = 0; j < coords.length - 1; j++)
                lineCoords.add(coords[j]);

            /* ... and add the point as last one. */
            lineCoords.add(point.getCoordinate());

            /* Create the new geometry. */
            final LineString tempLine = factory.createLineString(lineCoords.toArray(new Coordinate[] {}));

            return tempLine.getLength();
        }

        throw new IllegalStateException();
    }

    /**
     * This function calculates a point at a specific length of a line.
     * 
     * @param lineJTS
     *          The line string on which the point has to be.
     * @param distance
     *          The distance at which the point should be placed on the line.
     * @return The newly created point on the line or null, if something was wrong.
     */
    public static Point pointOnLine(final LineString lineJTS, final double distance) {
        final Coordinate crd = posOnLine(lineJTS, distance);
        final GeometryFactory factory = new GeometryFactory(lineJTS.getPrecisionModel(), lineJTS.getSRID());
        return factory.createPoint(crd);
    }

    /**
     * This function calculates a specific position on a line.
     * 
     * @param lineJTS
     *          The line string on which the point has to be.
     * @param distanceOnLine
     *          The distance at which the point should be placed on the line.
     * @return The newly created position on the line or null, if something was wrong.
     * @deprecated Use LenghtIndexedLine instead.
     */
    @Deprecated
    public static Coordinate posOnLine(final LineString line, final double distanceOnLine) {
        final double length = line.getLength();
        if (distanceOnLine < 0 || distanceOnLine > length)
            return null;

        final int numPoints = line.getNumPoints();
        if (numPoints == 0)
            return null;

        /* Only loop until the point before the last point. */
        /* The remaining distance will be the remaining distance on the found line segment */
        double remainingDistance = distanceOnLine;
        LineSegment segment = null;
        for (int i = 0; i < numPoints - 1; i++) {
            final Point startPoint = line.getPointN(i);
            final Point endPoint = line.getPointN(i + 1);

            segment = new LineSegment(new Coordinate(startPoint.getCoordinate()),
                    new Coordinate(endPoint.getCoordinate()));
            final double lineLength = segment.getLength();
            if (remainingDistance - lineLength < 0)
                break;

            remainingDistance -= lineLength;
        }

        if (segment == null)
            return null;

        /* Now calculate the rest of the line. */
        final double max = segment.getLength();
        final Coordinate startPoint = segment.p0;
        final Coordinate endPoint = segment.p1;

        /* If the two X koords are equal, take one of them for the new point. */
        final double x = interpolateX(startPoint.x, endPoint.x, 0, max, remainingDistance);
        final double y = interpolateX(startPoint.y, endPoint.y, 0, max, remainingDistance);
        final double z = interpolateX(startPoint.z, endPoint.z, 0, max, remainingDistance);
        return new Coordinate(x, y, z);
    }

    private static double interpolateX(final double x1, final double x2, final int y1, final double y2,
            final double y) {
        if (Double.isNaN(x1) || Double.isNaN(x2))
            return Double.NaN;

        if (Double.compare(x1, x2) == 0)
            return x1;
        else {
            try {
                final LinearEquation computeX = new LinearEquation(x1, y1, x2, y2);
                return computeX.computeX(y);
            } catch (final SameXValuesException e) {
                // should never happen, has we tests this explicitly
                e.printStackTrace();
                return x1;
            }
        }
    }

    public static LineSegment findSegmentInLine(final LineString line, final double distanceOnLine) {
        final double length = line.getLength();
        if (distanceOnLine < 0 || distanceOnLine > length)
            return null;

        final int numPoints = line.getNumPoints();
        if (numPoints == 0)
            return null;

        double currentDistance = 0;
        for (int i = 0; i < numPoints - 1; i++) {
            final Point startPoint = line.getPointN(i);
            final Point endPoint = line.getPointN(i + 1);

            final double distance = endPoint.distance(startPoint);
            currentDistance += distance;

            if (distanceOnLine < currentDistance)
                return new LineSegment(startPoint.getCoordinate(), endPoint.getCoordinate());
        }

        return null;
    }

    /**
     * This function calculates a point at a specific length of a line.
     * 
     * @param lineJTS
     *          The line string on which the point has to be.
     * @param percent
     *          The distance in percent at which the point should be placed on the line as Int(!)
     * @return The newly created point on the line or null, if something was wrong. Returns the start point or the end
     *         point if percentage is 0 or 100.
     */
    public static Point pointOnLinePercent(final LineString lineJTS, final double percent) {
        if (percent < 0 || percent > 100)
            return null;

        if (percent == 0)
            return lineJTS.getPointN(0);

        if (percent == 100)
            return lineJTS.getPointN(lineJTS.getNumPoints() - 1);

        final double length = lineJTS.getLength();
        final double distance = length / 100.0 * percent;

        return pointOnLine(lineJTS, distance);
    }

    /**
     * This function creates a line segment (JTS) of a line from a given start point to an end point, including all points
     * on the given line.<br>
     * The returned line always starts at <code>start</code> and ends at <code>end</code>, regardless of the orientation
     * of the input line.<br/>
     * The start and end point are projected to the given linear geometry, so do not necessarily need to lie on the given
     * line.<br/>
     * This implementation is based on {@link LengthIndexedLine} of JTS and has the same restraints.
     * 
     * @param line
     *          The original line.
     * @param start
     *          The start point of the new line
     * @param end
     *          The end point of the new line
     * @return A linear geometry (i.e. either a {@link LineString} or a {@link MultiLineString} on the original line
     *         starting at with the start point and ending with the end point. If the input is a {@link LineString}, the
     *         result can be safelay cast to {@link LineString} as well.
     */
    public static Geometry extractLineString(final Geometry linearGeomety, final Point startPoint,
            final Point endPoint) {
        final LengthIndexedLine index = new LengthIndexedLine(linearGeomety);

        final double startIndex = index.project(startPoint.getCoordinate());
        final double endIndex = index.project(endPoint.getCoordinate());

        /* make sure orientiation is from start to end */
        final double start = Math.min(startIndex, endIndex);
        final double end = Math.max(startIndex, endIndex);

        return index.extractLine(start, end);
    }

    /**
     * This function creates a line segment (JTS) of a line from a given start point to an end point, including all points
     * on the given line.<br>
     * TODO: The used distance is calculated only by the x- and y-coordinates!! For an 3-dimensaional distance
     * calculation, the start and end point should have z-coordinates.
     * 
     * @param line
     *          The original line.
     * @param start
     *          The start point of the new line (it has to be one point that lies on the original line).
     * @param end
     *          The end point of the new line (it has to be one point that lies on the original line).
     * @return A LineString on the original line starting at with the start point and ending with the end point.
     * @deprecated Use {@link #extractLineString(Geometry, Point, Point)} instead
     */
    @Deprecated
    public static LineString createLineString(final Geometry line, final Point start, final Point end) {
        /* Check if both points are lying on the line (2d!). */
        if (line.distance(start) >= TOLERANCE || line.distance(end) >= TOLERANCE)
            return null;

        if (line instanceof LineString) {
            /* Check the orientation of the line. */
            if (!getLineOrientation((LineString) line, start, end))
                return createLineStringFromLine((LineString) line, end, start);

            return createLineStringFromLine((LineString) line, start, end);
        } else if (line instanceof MultiLineString)
            return createLineStringFromMultiLine((MultiLineString) line, start, end);

        return null;
    }

    /**
     * Evaluates the two given points and returns true, if the direction is equal of that from line (its points).
     * 
     * @param line
     *          The original LineString.
     * @param start
     *          The start point of the new line (it has to be one point that lies on the original LineString).
     * @param end
     *          The end point of the new line (it has to be one point that lies on the original LineString).
     * @return True, if the first found point of the line is nearer to the start point, than to the end point.
     */
    public static boolean getLineOrientation(final LineString line, final Point start, final Point end) {
        /* Check if both points are lying on the line. */
        if (line.distance(start) >= TOLERANCE || line.distance(end) >= TOLERANCE)
            throw new IllegalArgumentException("One of the two points does not lie on the given line ...");

        boolean first = false;

        for (int i = 0; i < line.getNumPoints() - 1; i++) {
            final Point pointN = line.getPointN(i);
            final Point pointN1 = line.getPointN(i + 1);

            /* Build a line with the two points to check the flag. */
            final LineSegment testLine = new LineSegment(new Coordinate(pointN.getCoordinate()),
                    new Coordinate(pointN1.getCoordinate()));

            if (testLine.distance(start.getCoordinate()) < TOLERANCE)
                first = true;

            if (testLine.distance(end.getCoordinate()) < TOLERANCE) {
                /* The direction is inverse. */
                if (!first)
                    return false;

                break;
            }
        }

        return true;
    }

    /**
     * This function creates a LineString (JTS) of a LineString from a given start point to an end point, including all
     * points on the given LineString. However it does not check the orientation of the start and end point. This must be
     * done before calling this method. Use {@link JTSUtilities#getLineOrientation(LineString, Point, Point)} for this
     * operation. Both points should have the same orientation than the line, otherwise the new line has only two points,
     * namly the start and end point.
     * 
     * @param line
     *          The original LineString.
     * @param start
     *          The start point of the new line (it has to be one point that lies on the original LineString).
     * @param end
     *          The end point of the new line (it has to be one point that lies on the original LineString).
     * @return A LineString on the original LineString starting at with the start point and ending with the end point.
     */
    private static LineString createLineStringFromLine(final LineString line, final Point start, final Point end) {
        final List<Point> points = new LinkedList<>();

        boolean add = false;

        points.add(start);

        for (int i = 0; i < line.getNumPoints() - 1; i++) {
            final Point pointN = line.getPointN(i);
            final Point pointN1 = line.getPointN(i + 1);

            if (add)
                points.add(pointN);

            /* Build a line with the two points to check the flag. */
            final LineSegment testLine = new LineSegment(new Coordinate(pointN.getCoordinate()),
                    new Coordinate(pointN1.getCoordinate()));

            if (testLine.distance(start.getCoordinate()) < TOLERANCE)
                add = true;

            if (testLine.distance(end.getCoordinate()) < TOLERANCE) {
                add = false;
                break;
            }
        }

        points.add(end);

        /* Create the coordinates for the new line string. */
        final Coordinate[] coordinates = new Coordinate[points.size()];

        for (int i = 0; i < points.size(); i++)
            coordinates[i] = new Coordinate(points.get(i).getCoordinate());

        final GeometryFactory factory = line.getFactory();
        return factory.createLineString(coordinates);
    }

    /**
     * This function creates a LineString (JTS) of a MultiLineString from a given start point to an end point, including
     * all points on the given MultiLineString. Gaps between the different LineStrings will be connected in the result, if
     * it should contain more than one LineString of the original MultiLineString. However it does not check the
     * orientation of the start and end point.<br>
     * <br>
     * KNOWN ISSUE:<br>
     * It is possible that this function produce errors, if the start or the end point lies in a gap between two
     * LineStrings. The two points of the gap will not be checked for that point. The result than, will possibly be
     * nonsense. <br>
     * <br>
     * ATTENTION:<br>
     * This class is strange, because creating a LineString part of a MultiLineString should be normally done by
     * dissolving the MultiLineString in one LineString-Object and getting the LineString part of it.<br>
     * There can not be quaranteed, that this function works error free!
     * 
     * @param line
     *          The original MultiLineString.
     * @param start
     *          The start point of the new line (it has to be one point on the original MultiLineString).
     * @param end
     *          The end point of the new line (it has to be one point on the original MultiLineString).
     * @return A LineString on the original MultiLineString starting at with the start point and ending with the end
     *         point.
     */
    private static LineString createLineStringFromMultiLine(final MultiLineString line, final Point start,
            final Point end) {
        final List<Point> points = new LinkedList<>();

        boolean add = false;
        boolean endPointFound = false;

        points.add(start);
        for (int i = 0; i < line.getNumGeometries(); i++) {
            final LineString lineN = (LineString) line.getGeometryN(i);

            for (int j = 0; j < lineN.getNumPoints() - 1; j++) {
                final Point pointN = lineN.getPointN(j);
                final Point pointN1 = lineN.getPointN(j + 1);

                if (add)
                    points.add(pointN);

                /* Build a line with the two points to check the flag. */
                final LineSegment testLine = new LineSegment(new Coordinate(pointN.getCoordinate()),
                        new Coordinate(pointN1.getCoordinate()));

                if (testLine.distance(start.getCoordinate()) < TOLERANCE)
                    add = true;

                if (testLine.distance(end.getCoordinate()) < TOLERANCE) {
                    add = false;
                    endPointFound = true;
                    break;
                }
            }

            if (endPointFound)
                break;
        }

        points.add(end);

        /* Create the coordinates for the new line string. */
        final Coordinate[] coordinates = new Coordinate[points.size()];

        for (int i = 0; i < points.size(); i++)
            coordinates[i] = new Coordinate(points.get(i).getCoordinate());

        final GeometryFactory factory = new GeometryFactory(line.getPrecisionModel(), line.getSRID());

        return factory.createLineString(coordinates);
    }

    /**
     * This function creates a line segment with the two given points, calculates the length of the line segment and
     * returns the length.
     * 
     * @param pointOne
     *          This point will be used as start point of the line segment.
     * @param pointTwo
     *          This point will be used as end point of the line segment.
     * @return The length of the line between the two points given.
     */
    public static double getLengthBetweenPoints(final Point pointOne, final Point pointTwo) {
        return getLengthBetweenPoints(pointOne.getCoordinate(), pointTwo.getCoordinate());
    }

    /**
     * This function creates a line segment with the two given coordinates, calculates the length of the line segment and
     * returns the length.
     * 
     * @param coordinateOne
     *          This coordinate will be used as start point of the line segment.
     * @param coordinateTwo
     *          This coordinate will be used as end point of the line segment.
     * @return The length of the line between the two coordinates given.
     */
    public static double getLengthBetweenPoints(final Coordinate coordinateOne, final Coordinate coordinateTwo) {
        final LineSegment segment = new LineSegment(coordinateOne, coordinateTwo);
        return segment.getLength();
    }

    /** Creates a jts-polygon from a deegree-envelope */
    public static Polygon convertGMEnvelopeToPolygon(final GM_Envelope envelope, final GeometryFactory gf) {
        final Coordinate minCoord = JTSAdapter.export(envelope.getMin());
        final Coordinate maxCoord = JTSAdapter.export(envelope.getMax());
        final Coordinate tmp1Coord = new Coordinate(minCoord.x, maxCoord.y);
        final Coordinate tmp2Coord = new Coordinate(maxCoord.x, minCoord.y);

        final Coordinate[] coordinates = new Coordinate[] { minCoord, tmp1Coord, maxCoord, tmp2Coord, minCoord };
        final LinearRing linearRing = gf.createLinearRing(coordinates);
        return gf.createPolygon(linearRing, null);
    }

    /** Converts an envelope to a polygon that covers the envelope. */
    public static Polygon convertEnvelopeToPolygon(final Envelope envelope, final GeometryFactory gf) {
        final Coordinate minCoord = new Coordinate(envelope.getMinX(), envelope.getMinY());
        final Coordinate maxCoord = new Coordinate(envelope.getMaxX(), envelope.getMaxY());
        final Coordinate tmp1Coord = new Coordinate(minCoord.x, maxCoord.y);
        final Coordinate tmp2Coord = new Coordinate(maxCoord.x, minCoord.y);

        final Coordinate[] coordinates = new Coordinate[] { minCoord, tmp1Coord, maxCoord, tmp2Coord, minCoord };
        final LinearRing linearRing = gf.createLinearRing(coordinates);
        return gf.createPolygon(linearRing, null);
    }

    /**
     * @param ring
     *          array of ordered coordinates, last must equal first one
     * @return signed area, area >= 0 means points are counter clockwise defined (mathematic positive)
     */
    public static double calcSignedAreaOfRing(final Coordinate[] ring) {
        if (ring.length < 4) // 3 points and 4. is repetition of first point
            throw new UnsupportedOperationException("Can not calculate area of < 3 points ...");

        final Coordinate a = ring[0]; // base
        double area = 0;
        for (int i = 1; i < ring.length - 2; i++) {
            final Coordinate b = ring[i];
            final Coordinate c = ring[i + 1];

            area += (b.y - a.y) * (a.x - c.x) // bounding rectangle

                    - ((a.x - b.x) * (b.y - a.y)//
                            + (b.x - c.x) * (b.y - c.y)//
                            + (a.x - c.x) * (c.y - a.y)//
                    ) / 2d;
        }

        return area;
    }

    /**
     * This function will check all line segments and return the one, in which the given point lies. If no segment is
     * found it will return null.
     * 
     * @param curve
     *          The curve to check.
     * @param point
     *          The point, which marks the segment (e.g. an intersection point of another geometry).
     * @return The line segment or null.
     */
    public static LineSegment findLineSegment(final LineString curve, final Point point) {
        for (int i = 0; i < curve.getNumPoints() - 1; i++) {
            final Point pointN = curve.getPointN(i);
            final Point pointN1 = curve.getPointN(i + 1);

            /* Build a line with the two points to check the intersection. */
            final LineSegment segment = new LineSegment(pointN.getCoordinate(), pointN1.getCoordinate());

            /* If found, return it. */
            if (segment.distance(point.getCoordinate()) < TOLERANCE)
                return segment;
        }

        return null;
    }

    /**
     * This function adds points to the line.<br>
     * It will not change the topology of the line in that way, that it changes direction.<br>
     * <br>
     * REMARK:<br>
     * It can be very slow, in dependance of the amount of points to be added.
     * 
     * @param line
     *          The line, to which the points are added to.
     * @param originalPoints
     *          The points, which should be added. The points has to lie on the line.
     * @return The new line as copy of the old line, including the given points. The result may be null.
     */
    public static LineString addPointsToLine(final LineString line, final List<Point> originalPoints) {
        return addPointsToLine(line, originalPoints.toArray(new Point[] {}));
    }

    public static LineString addPointsToLine(final LineString line, final Point... points) {
        final Coordinate[] crds = new Coordinate[points.length];
        for (int i = 0; i < crds.length; i++)
            crds[i] = new Coordinate(points[i].getCoordinate());

        return addPointsToLine(line, crds);
    }

    public static LineString addPointsToLine(final LineString line, final Coordinate... locations) {
        /* The geometry factory. */
        final GeometryFactory factory = new GeometryFactory(line.getPrecisionModel(), line.getSRID());

        /* Memory for the new coordinates. */
        final CoordinateList newCoordinates = new CoordinateList();

        /* Get all coordinates. */
        final Coordinate[] lineCoordinates = line.getCoordinates();

        /* Always add the first coordinate. */
        newCoordinates.add(lineCoordinates[0], false);

        final Collection<Coordinate> toIgnore = new HashSet<>();

        /* Only loop until the one before the last one. */
        for (int i = 0; i < lineCoordinates.length - 1; i++) {
            /* Get the coordinates. */
            final Coordinate startCoord = lineCoordinates[i];
            final Coordinate endCoord = lineCoordinates[i + 1];

            /* Create a new line with the coordinates. */
            final LineSegment ls = new LineSegment(startCoord, endCoord);

            /* If no one is intersecting, the current end coordinate has to be added. */
            final List<Coordinate> toAdd = new ArrayList<>();

            for (final Coordinate location : locations) {
                if (toIgnore.contains(location))
                    continue;

                if (ls.distance(location) < TOLERANCE) {
                    /* The point intersects, and has to be added. */
                    toAdd.add(location);

                    /* The points should be removed from the old points list for performance reasons. */
                    toIgnore.add(location);
                    continue;
                }
            }

            /* Add all points. */
            final List<CoordinatePair> coordinatePairs = getCoordinatePairs(startCoord, toAdd);
            for (final CoordinatePair coordinatePair : coordinatePairs)
                newCoordinates.add(coordinatePair.getSecondCoordinate(), false);

            /* Add the end coordinate. */
            newCoordinates.add(endCoord, false);
        }

        return factory.createLineString(newCoordinates.toCoordinateArray());
    }

    /**
     * Inverts a given geometry.
     * 
     * @param geometry
     *          The geometry, which should be inverted.
     */
    public static Geometry invert(final Geometry geometry) {
        final GeometryFactory factory = new GeometryFactory(geometry.getPrecisionModel(), geometry.getSRID());

        if (geometry instanceof LineString) {
            final LineString lineString = (LineString) geometry;
            final Coordinate[] coordinates = lineString.getCoordinates();

            final Set<Coordinate> myCoordinates = new LinkedHashSet<>();
            for (int i = coordinates.length - 1; i >= 0; i--)
                myCoordinates.add(coordinates[i]);

            return factory.createLineString(myCoordinates.toArray(new Coordinate[] {}));
        }

        throw new UnsupportedOperationException();
    }

    /**
     * This function adds a z-coordinate to each point of a line string. It interpolates the z-coordinate, using the
     * length of the line segment between the start point (parameter start) and the current point. The last point will get
     * the maximum as the z-coordinate (parameter end).
     * 
     * @param lineString
     *          To each point on this line string the z-coordinate will be added.
     * @param start
     *          A start value (a start time, for example).
     * @param end
     *          A end value (an end time, for example).
     * @return A new line string with z-coordinate.
     */
    public static LineString addInterpolatedZCoordinates(final LineString lineString, final double start,
            final double end) throws SameXValuesException {
        /* Interpolate the times for each points, if time is given. */

        /* List with the coordinates of the new line string. */
        final List<Coordinate> coordinates = new ArrayList<>();

        /* The x-axis represents the distance on the line string. */
        final double x1 = 0.0;
        final double x2 = lineString.getLength();

        /* The y-axis represents the values given by the parameters (a time, for example). */
        final double y1 = start;
        final double y2 = end;

        /* Create the linear equation. */
        final LinearEquation equation = new LinearEquation(x1, y1, x2, y2);

        for (int i = 0; i < lineString.getNumPoints(); i++) {
            /* Get the points. */
            final Point point = lineString.getPointN(i);

            /* The distance from start to this point. */
            /* If this point is the start, it should be 0. */
            /* If this point is the end, it should be lineString.getLength(). */
            final double distance = pointDistanceOnLine(lineString, point);

            /* Compute the x to this point (needed time to this point, for example). */
            final double x = equation.computeY(distance);

            /* Create the coordinate. */
            final Coordinate coordinate = new Coordinate(point.getX(), point.getY(), x);
            coordinates.add(coordinate);
        }

        /* Create the new line string. */
        final GeometryFactory factory = new GeometryFactory(lineString.getPrecisionModel(), lineString.getSRID());

        return factory.createLineString(coordinates.toArray(new Coordinate[] {}));
    }

    /**
     * This function calculates the center coordinate between two coordinates.
     * 
     * @param coordinate_one
     *          The first coordinate.
     * @param coordinate_two
     *          The second coordinate.
     * @return The center coordinate.
     */
    public static Coordinate getCenterCoordinate(final Coordinate coordinate_one, final Coordinate coordinate_two) {
        final double x = (coordinate_one.x + coordinate_two.x) / 2;
        final double y = (coordinate_one.y + coordinate_two.y) / 2;

        return new Coordinate(x, y);
    }

    /**
     * This function collects polygons from polygons (which will return itself in the list) or multi polygons.
     * 
     * @param geometry
     *          The geometry to collect from. If it is no polygon, an empty list will be returned.
     * @return The list of contained polygons or an empty list.
     */
    public static List<Polygon> collectPolygons(final Geometry geometry) {
        /* Memory for the results. */
        final List<Polygon> polygons = new ArrayList<>();

        if (!(geometry instanceof Polygon) && !(geometry instanceof MultiPolygon))
            return polygons;

        if (geometry instanceof Polygon) {
            polygons.add((Polygon) geometry);
            return polygons;
        }

        if (geometry instanceof MultiPolygon) {
            final MultiPolygon multi = (MultiPolygon) geometry;
            final int num = multi.getNumGeometries();

            for (int i = 0; i < num; i++) {
                final Geometry geometryN = multi.getGeometryN(i);
                polygons.add((Polygon) geometryN);
            }
        }

        return polygons;
    }

    /**
     * This function inspects each coordinate of the given array and removes the z-coordinate from it (sets Double.NaN).
     * 
     * @param coordinates
     *          The array of coordinates.
     * @return A new array of new coordinates without the z-coordinate.
     */
    public static Coordinate[] removeZCoordinates(final Coordinate[] coordinates) {
        /* Memory for the results. */
        final List<Coordinate> results = new ArrayList<>();

        for (final Coordinate coordinate : coordinates)
            results.add(new Coordinate(coordinate.x, coordinate.y));

        return results.toArray(new Coordinate[] {});
    }

    /**
     * Buffers a geometry.<br>
     * The size of the buffer is determined as a ratio of the size of its envelope.<br>
     * More exact, it is <code>ratio * Math.max(envelope.getWidht(), envelope.getHeight())</code>.
     */
    public static Geometry bufferWithRatio(final Geometry geometry, final double ratio) {
        final Envelope envelope = geometry.getEnvelopeInternal();
        final double width = envelope.getWidth();
        final double height = envelope.getHeight();
        final double max = Math.max(width, height);

        return geometry.buffer(max * ratio);
    }

    /**
     * Calculates the fractions some polygons are covering one base geometry (should be a geometry with an area).
     * 
     * @see #fractionAreaOf(Geometry, Polygon)
     */
    public static double[] fractionAreasOf(final Geometry baseGeometry, final Polygon[] coverPolygons) {
        final double[] factors = new double[coverPolygons.length];
        for (int i = 0; i < coverPolygons.length; i++)
            factors[i] = JTSUtilities.fractionAreaOf(baseGeometry, coverPolygons[i]);

        return factors;
    }

    /**
     * Calculates the part (as fraction) of one polygon covering another.
     * 
     * @param baseGeometry
     *          The geometry (should be a geometry with an area), that is covered (by the calculated fraction) by the <code>coverPolygon</code>. May NOT be <code>null</code>.
     * @param coverPolygon
     *          The polygon covering (or not) the basePolygon. My be <code>null</code> (in that case, <code>0.0</code> is
     *          returned).
     */
    public static double fractionAreaOf(final Geometry baseGeometry, final Polygon coverPolygon) {
        Assert.isNotNull(baseGeometry);

        if (coverPolygon == null)
            return 0.0;

        /* The intersect function is much faster, than the intersection function. */
        /* So you should use it to check for intersection, even if you need the polygon of the overlapping area. */
        if (!baseGeometry.intersects(coverPolygon))
            return 0.0;

        final Geometry intersection = baseGeometry.intersection(coverPolygon);
        if (intersection == null)
            return 0.0;

        final double totalArea = baseGeometry.getArea();
        final double intersectionArea = intersection.getArea();

        return intersectionArea / totalArea;
    }

    public static double getAreaFraction(final Geometry baseGeometry, final Polygon coverPolygon) {
        Assert.isNotNull(baseGeometry);

        if (coverPolygon == null)
            return 0.0;

        /* The intersect function is much faster, than the intersection function. */
        /* So you should use it to check for intersection, even if you need the polygon of the overlapping area. */
        if (!baseGeometry.intersects(coverPolygon))
            return 0.0;

        final Geometry intersection = baseGeometry.intersection(coverPolygon);
        if (intersection == null)
            return 0.0;

        final double coverArea = coverPolygon.getArea();
        final double intersectionArea = intersection.getArea();

        return intersectionArea / coverArea;
    }

    public static Polygon cleanPolygonInteriorRings(Polygon poly) {
        final GeometryFactory factory = new GeometryFactory();

        final List<LinearRing> myInnerRings = new ArrayList<>();
        final int rings = poly.getNumInteriorRing();

        for (int i = 0; i < rings; i++) {
            final LineString lineString = poly.getInteriorRingN(i);
            final Coordinate[] coordinates = lineString.getCoordinates();
            final LinearRing ring = factory.createLinearRing(coordinates);

            final Polygon innerPolygon = factory.createPolygon(ring, null);
            final double area = innerPolygon.getArea();
            if (area > 0.1)
                myInnerRings.add(ring);
        }

        if (myInnerRings.size() != rings) {
            final LineString outer = poly.getExteriorRing();
            final Coordinate[] outerCoordinates = outer.getCoordinates();
            final LinearRing outerRing = factory.createLinearRing(outerCoordinates);

            poly = factory.createPolygon(outerRing, myInnerRings.toArray(new LinearRing[] {}));
        }

        return poly;
    }

    public static Geometry[] findGeometriesInRange(final Geometry[] geometries, final Geometry base,
            final double radius) {
        final Set<Geometry> myGeometries = new HashSet<>();

        for (final Geometry geometry : geometries) {
            final double distance = base.distance(geometry);
            if (distance <= radius)
                myGeometries.add(geometry);
        }

        return myGeometries.toArray(new Geometry[] {});
    }

    /**
     * @return coordinates in range sorted by distance
     */
    public static Coordinate[] findCoordinatesInRange(final Coordinate[] coordinates, final Coordinate base,
            final double radius) {
        final Comparator<Coordinate> comparator = new Comparator<Coordinate>() {
            @Override
            public int compare(final Coordinate c1, final Coordinate c2) {

                final Double d1 = Double.valueOf(c1.distance(base));
                final Double d2 = Double.valueOf(c2.distance(base));

                return d1.compareTo(d2);
            }
        };

        final Set<Coordinate> myCoordinates = new TreeSet<>(comparator);

        for (final Coordinate c : coordinates) {
            final double distance = base.distance(c);
            if (distance <= radius)
                myCoordinates.add(c);
        }

        return myCoordinates.toArray(new Coordinate[] {});
    }

    /**
     * This function returns the minimal x-value of a sequence of coordinates.
     * 
     * @param seq
     *          The coordinate sequence.
     * @return The minimal x-value of a sequence of coordinates. {@link Double#POSITIVE_INFINITY} If the sequence is
     *         empty.
     */
    public static double getMinX(final CoordinateSequence seq) {
        double min = Double.POSITIVE_INFINITY;
        for (int i = 0; i < seq.size(); i++)
            min = Math.min(min, seq.getX(i));

        return min;
    }

    /**
     * This function returns the maximal x-value of a sequence of coordinates.
     * 
     * @param seq
     *          The coordinate sequence.
     * @return The maximal x-value of a sequence of coordinates. {@link Double#NEGATIVE_INFINITY} If the sequence is
     *         empty.
     */
    public static double getMaxX(final CoordinateSequence seq) {
        double max = Double.NEGATIVE_INFINITY;
        for (int i = 0; i < seq.size(); i++)
            max = Math.max(max, seq.getX(i));

        return max;
    }

    /**
     * This function returns all x-values of the given sequence as an array.
     * 
     * @param seq
     *          The coordinate sequence.
     * @return All x-values of the given sequence as an array.
     */
    public static double[] getXValues(final CoordinateSequence seq) {
        final double[] x = new double[seq.size()];
        for (int i = 0; i < seq.size(); i++)
            x[i] = seq.getX(i);

        return x;
    }

    /**
     * This function returns all y-values of the given sequence as an array.
     * 
     * @param seq
     *          The coordinate sequence.
     * @return All y-values of the given sequence as an array.
     */
    public static double[] getYValues(final CoordinateSequence seq) {
        final double[] y = new double[seq.size()];
        for (int i = 0; i < seq.size(); i++)
            y[i] = seq.getY(i);

        return y;
    }

    /**
     * This function validates geometries.
     * 
     * @param msg
     *          Basic error message.
     * @param g
     *          Geometries to check.
     * @return If an error exists an error message will be returned.
     */
    public static String validateGeometries(String msg, final Geometry... g) {
        boolean error = false;
        for (final Geometry geometry : g) {
            final IsValidOp isValidOp = new IsValidOp(geometry);
            final TopologyValidationError validationError = isValidOp.getValidationError();
            if (validationError != null) {
                msg += String.format(" Error: %s", validationError.getMessage());
                error = true;
            }
        }

        if (error)
            return msg;

        return null;
    }

    /**
     * This function adds z coordinates to the given geometry, using the inverse distance weighting on a list of points
     * with z coordinates.
     * 
     * @param geometry
     *          The geometry for which the z coordinates should be added.
     * @param points
     *          The points with z coordinates, which will be used for the inverse distance weighting.
     * @param numberOfPoints
     *          The number of the nearest points of the list, that will be used in the distance weighting. If this
     *          parameter is <= 0, all points will be used.
     * @return A new geometry with x, y and z coordinates.
     */
    public static Geometry addZCoordinates(final Geometry geometry, final List<Coordinate> points,
            final int numberOfPoints) {
        /* Check the prerequisites. */
        if (geometry == null)
            throw new IllegalArgumentException("No geometry given ...");

        /* Check the prerequisites. */
        if (points == null || points.size() == 0)
            throw new IllegalArgumentException("No points with z coordinates given ...");

        /* Create the geometry factory. */
        final GeometryFactory factory = new GeometryFactory();

        /* Clone the geometry. */
        final Geometry newGeometry = factory.createGeometry(geometry);

        /* Modify the coordinates of the new geometry. */
        final Coordinate[] coordinates = newGeometry.getCoordinates();
        for (final Coordinate coordinate : coordinates) {
            /* Now add a z coordinate to the coordinate. */
            addZCoordinate(coordinate, points, numberOfPoints);
        }

        /* Tell the new geometry, that it has changed. */
        newGeometry.geometryChanged();

        return newGeometry;
    }

    /**
     * This function adds a z coordinate to the given point, using the inverse distance weighting on a list of points with
     * z coordinates.
     * 
     * @param coordinate
     *          The coordinate for which the z coordinates should be added.
     * @param points
     *          The points with z coordinates, which will be used for the inverse distance weighting.
     * @param numberOfPoints
     *          The number of the nearest points of the list, that will be used in the distance weighting. If this
     *          parameter is <= 0, all points will be used.
     */
    private static void addZCoordinate(final Coordinate coordinate, final List<Coordinate> points,
            final int numberOfPoints) {
        /* Check the prerequisites. */
        if (coordinate == null)
            throw new IllegalArgumentException("No geometry given ...");

        /* Check the prerequisites. */
        if (points == null || points.size() == 0)
            throw new IllegalArgumentException("No points with z coordinates given ...");

        /* Get the coordinate pairs. */
        final List<CoordinatePair> coordinatePairs = getCoordinatePairs(coordinate, points);

        /* The sum of all distances. */
        double sumDistances = 0.0;

        /* Calculate the distances. */
        final List<Double> distances = new ArrayList<>();
        for (int i = 0; i < coordinatePairs.size(); i++) {
            if (numberOfPoints > 0 && i >= numberOfPoints)
                break;

            /* Get the coordinate pair. */
            final CoordinatePair coordinatePair = coordinatePairs.get(i);

            final double d = coordinatePair.getDistance();
            if (0.0 == d) {
                final Coordinate second = coordinatePair.getSecondCoordinate();
                coordinate.z = second.z;

                return;
            }

            /* Get the distance of the coordinate to the coordinate of the point. */
            final double distance = 1 / d;

            /* First add it to the sum of distances. */
            sumDistances = sumDistances + distance;

            /* Then add it to the list of distances. */
            distances.add(new Double(distance));
        }

        /* Calculate the factors. */
        final List<Double> factors = new ArrayList<>();
        for (int i = 0; i < distances.size(); i++) {
            /* Get the distance. */
            final Double distance = distances.get(i);

            /* Calculate the factor. */
            final double factor = distance.doubleValue() / sumDistances;

            /* Add it to the list of factors. */
            factors.add(new Double(factor));
        }

        /* Now calculate the new z coordinate. */
        double newZ = 0.0;
        for (int i = 0; i < coordinatePairs.size(); i++) {
            if (numberOfPoints > 0 && i >= numberOfPoints)
                break;

            /* Get the coordinate pair. */
            final CoordinatePair coordinatePair = coordinatePairs.get(i);

            /* Get the factor. */
            final Double factor = factors.get(i);

            /* Calculate the new z coordinate. */
            newZ = newZ + coordinatePair.getSecondCoordinate().z * factor.doubleValue();
        }

        /* Set the new z coordinate. */
        coordinate.z = newZ;
    }

    /**
     * This function returns a list of coordinates pairs. The first coordinate of a pair will be always the parameter
     * coordinate and the second coordinate of a pair will be a coordinate of one point of the list. The list will be
     * sorted by the distance, each pair has.
     * 
     * @param coordinate
     *          The coordinate.
     * @param points
     *          The list of points.
     * @return A list of coordinate pairs. The first coordinate of a pair will be always the parameter coordinate, and the
     *         second coordinate of a pair will be a coordinate of one point of the list. The list will be sorted by the
     *         distance, each pair has.
     */
    public static List<CoordinatePair> getCoordinatePairs(final Coordinate coordinate,
            final List<Coordinate> points) {
        /* Memory for the results. */
        final List<CoordinatePair> results = new ArrayList<>();

        for (int i = 0; i < points.size(); i++) {
            /* Get the point. */
            final Coordinate point = points.get(i);

            /* Create the coordinate pair. */
            final CoordinatePair coordinatePair = new CoordinatePair(coordinate, point);

            /* Add to the results. */
            results.add(coordinatePair);
        }

        /* Sort the results. */
        Collections.sort(results, null);

        return results;
    }

    /**
     * This function returns the nearest point on the line within a distance of the provided point.
     * 
     * @param line
     *          The points on this line will be evaluated.
     * @param point
     *          This is the reference point.
     * @param distance
     *          The nearest point of line segment, the point is in must lie within the given distance. If you provide a
     *          invalid distance such as {@link Double#NaN}, {@link Double#NEGATIVE_INFINITY}, {@link Double#POSITIVE_INFINITY} or a negative number, a default distance ({@link #TOLERANCE} =
     *          {@value #TOLERANCE}) will be used.
     * @return The point, if one could be found or null.
     */
    public static Point findPointInLine(final LineString line, final Point point, double distance) {
        /* Check for intersection. */
        if (point.distance(line) >= TOLERANCE)
            throw new IllegalStateException("The point does not lie on the line ...");

        /* Check the distance. */
        if (Double.isNaN(distance) || Double.isInfinite(distance))
            distance = TOLERANCE;

        /* Find the line segment, this point is in. */
        final LineSegment segment = findLineSegment(line, point);
        if (segment == null)
            return null;

        /* Check the distance to the reference point. */
        final Coordinate referenceCoordinate = point.getCoordinate();
        final double distance0 = segment.getCoordinate(0).distance(referenceCoordinate);
        final double distance1 = segment.getCoordinate(1).distance(referenceCoordinate);
        Coordinate closestCoordinate = null;
        if (distance0 <= distance1)
            closestCoordinate = segment.getCoordinate(0);
        else
            closestCoordinate = segment.getCoordinate(1);

        if (closestCoordinate.distance(referenceCoordinate) <= distance) {
            final GeometryFactory factory = new GeometryFactory(line.getPrecisionModel(), line.getSRID());
            return factory.createPoint(closestCoordinate);
        }

        return null;
    }

    /**
     * This function finds the points via the NEAREST rule. Method was copied from InformDSS class AbstractGeoMeasure
     * 
     * @return The list of affected points. Always with size = 2.
     */
    public static Point findNearestProjectionPoints(final Polygon polygone, final Point point) {
        final Coordinate base = point.getCoordinate();

        double distance = Double.MAX_VALUE;
        Coordinate ptr = null;

        /* Get the exterior ring. */
        final LineString ring = polygone.getExteriorRing();
        final Coordinate[] coordinates = ring.getCoordinates();

        for (final Coordinate coordinate : coordinates) {
            final double d = coordinate.distance(base);
            if (d < distance) {
                ptr = coordinate;
                distance = d;
            }
        }

        if (Objects.isNull(ptr))
            return null;

        final GeometryFactory factory = new GeometryFactory(point.getPrecisionModel(), point.getSRID());
        return factory.createPoint(ptr);
    }

    public static Coordinate[] replace(final Coordinate[] coordinates, final Coordinate old, final Coordinate set) {
        final List<Coordinate> replaced = new ArrayList<>();
        for (final Coordinate coordinate : coordinates) {
            if (coordinate.equals(old))
                replaced.add(set);
            else
                replaced.add(coordinate);
        }

        return replaced.toArray(new Coordinate[] {});
    }

    public static double distanceZ(final Coordinate c1, final Coordinate c2) {
        if (Double.isNaN(c1.z) || Double.isNaN(c2.z))
            throw new IllegalStateException();

        final double dx = c1.x - c2.x;
        final double dy = c1.y - c2.y;
        final double dz = c1.z - c2.z;

        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }

    public static Polygon interpolateMissingZ(final Polygon input) {
        final GeometryFactory geomFactory = input.getFactory();
        final LineString exteriorRing = input.getExteriorRing();
        final LinearRing exteriorZ = geomFactory
                .createLinearRing(interpolateMissingZ(exteriorRing).getCoordinates());
        final LinearRing[] interiorZ = new LinearRing[input.getNumInteriorRing()];
        for (int i = 0; i < input.getNumInteriorRing(); i++) {
            final LineString interiorRing = input.getInteriorRingN(i);
            interiorZ[i] = geomFactory.createLinearRing(interpolateMissingZ(interiorRing).getCoordinates());
        }
        return geomFactory.createPolygon(exteriorZ, interiorZ);
    }

    /**
     * Interpolate missing z values for all vertices from vertices that already have a z value.<br/>
     * For each vertex, the z value is interpolated from the two nearest adjacent vertices with z values. If only one
     * neighbour has a z value, it is directly copied.
     */
    public static LineString interpolateMissingZ(final LineString input) {
        final LengthIndexedLine lengthIndex = new LengthIndexedLine(input);

        final Coordinate[] coordinates = input.getCoordinates();

        /* a create mapping distance - z for later interpolation */
        final Collection<Double> distancesWithZsList = new ArrayList<>(coordinates.length);

        final Collection<Double> zsList = new ArrayList<>(coordinates.length);

        for (final Coordinate vertex : coordinates) {
            if (!Double.isNaN(vertex.z)) {
                final double distance = lengthIndex.indexOf(vertex);
                distancesWithZsList.add(distance);
                zsList.add(vertex.z);
            }
        }

        // nothing to do if there are no z values anywhere
        if (zsList.isEmpty())
            return input;

        final Double[] distances = distancesWithZsList.toArray(new Double[distancesWithZsList.size()]);
        final Double[] zs = zsList.toArray(new Double[zsList.size()]);

        /* create a polyline for interpolation of missing z's */
        final PolyLine interpolator = new PolyLine(distances, zs, TOLERANCE);

        /* last step: interpolate missing z's via interpolator */
        final Coordinate[] coordinatesWithZ = new Coordinate[coordinates.length];

        for (int i = 0; i < coordinatesWithZ.length; i++) {
            final Coordinate vertex = coordinates[i];

            if (Double.isNaN(vertex.z)) {
                final double distance = lengthIndex.indexOf(vertex);
                final double interpolatedZ = interpolator.getYFor(distance, false);
                coordinatesWithZ[i] = new Coordinate(vertex.x, vertex.y, interpolatedZ);
            } else
                coordinatesWithZ[i] = new Coordinate(vertex);
        }

        /* create and return new line string */
        final LineString output = input.getFactory().createLineString(coordinatesWithZ);
        output.setSRID(input.getSRID());
        return output;
    }

    /**
     * Removes points closer to
     */
    public static Polygon removeCoincidentPoints(final Polygon polygon, final double nearDistance,
            final double distanceTolerance) {
        final LinearRing exteriorRing = (LinearRing) polygon.getExteriorRing();
        final LinearRing newExteriorRing = removeCoincidentPoints(exteriorRing, nearDistance, distanceTolerance);
        final int ringCount = polygon.getNumInteriorRing();
        final LinearRing[] newInteriorRings = new LinearRing[ringCount];
        for (int i = 0; i < ringCount; i++)
            newInteriorRings[i] = removeCoincidentPoints((LinearRing) polygon.getInteriorRingN(i), nearDistance,
                    distanceTolerance);
        return polygon.getFactory().createPolygon(newExteriorRing, newInteriorRings);
    }

    /**
     * Removes interior points of this line that are within the given distance from another point
     */
    @SuppressWarnings("unchecked")
    public static <T extends LineString> T removeCoincidentPoints(final T linestring, final double nearDistance,
            final double distanceTolerance) {
        final GeometryEditor editor = new GeometryEditor();
        final Geometry result = editor.edit(linestring, new GeometryEditor.CoordinateOperation() {

            @Override
            public Coordinate[] edit(final Coordinate[] coordinates, final Geometry geometry) {
                final CoordinateList coordList = new CoordinateList();
                coordList.add(coordinates[0]);
                for (int i = 1; i < coordinates.length - 1; i++) {
                    final Coordinate p0 = coordList.getCoordinate(coordList.size() - 1);
                    final Coordinate p1 = coordinates[i];
                    final Coordinate p2 = coordinates[i + 1];
                    if (p1.distance(p0) <= nearDistance || p1.distance(p2) <= nearDistance) {
                        final boolean nearlyColinear = new LineSegment(p0, p2).distance(p1) < distanceTolerance;
                        if (nearlyColinear)
                            // gobble
                            continue;
                    }
                    coordList.add(p1);
                }
                coordList.add(coordinates[coordinates.length - 1]);
                return coordList.toCoordinateArray();
            }
        });
        return (T) result;
    }

    public static com.infomatiq.jsi.Rectangle toRectangle(final Envelope envelope) {
        final float x1 = MathUtils.floorFloat(envelope.getMinX());
        final float y1 = MathUtils.floorFloat(envelope.getMinY());

        final float x2 = MathUtils.ceilFloat(envelope.getMaxX());
        final float y2 = MathUtils.ceilFloat(envelope.getMaxY());

        return new Rectangle(x1, y1, x2, y2);
    }

    public static Rectangle toRectangle(final double x, final double y) {
        final float x1 = MathUtils.floorFloat(x);
        final float y1 = MathUtils.floorFloat(y);
        final float x2 = MathUtils.ceilFloat(x);
        final float y2 = MathUtils.ceilFloat(y);

        return new Rectangle(x1, y1, x2, y2);
    }
}