/** * H2GIS is a library that brings spatial support to the H2 Database Engine * <http://www.h2database.com>. H2GIS is developed by CNRS * <http://www.cnrs.fr/>. * * This code is part of the H2GIS project. H2GIS 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; * version 3.0 of the License. * * H2GIS 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 <http://www.gnu.org/licenses/>. * * * For more information, please consult: <http://www.h2gis.org/> * or contact directly: info_at_h2gis.org */ package org.h2gis.utilities.jts_utils; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateArrays; import com.vividsolutions.jts.geom.LineSegment; import com.vividsolutions.jts.math.Vector3D; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.LinkedHashSet; /** * Useful methods for JTS {@link Coordinate}s. * * @author Erwan Bocher */ public final class CoordinateUtils { /** * Determine the min and max "z" values in an array of Coordinates. * * @param cs The array to search. * @return An array of size 2, index 0 is min, index 1 is max. */ public static double[] zMinMax(final Coordinate[] cs) { double zmin; double zmax; boolean validZFound = false; double[] result = new double[2]; zmin = Double.NaN; zmax = Double.NaN; double z; for (int t = cs.length - 1; t >= 0; t--) { z = cs[t].z; if (!(Double.isNaN(z))) { if (validZFound) { if (z < zmin) { zmin = z; } if (z > zmax) { zmax = z; } } else { validZFound = true; zmin = z; zmax = z; } } } result[0] = (zmin); result[1] = (zmax); return result; } /** * Interpolates a z value (linearly) between the two coordinates. * * @param firstCoordinate * @param lastCoordinate * @param toBeInterpolated * @return */ public static double interpolate(Coordinate firstCoordinate, Coordinate lastCoordinate, Coordinate toBeInterpolated) { if (Double.isNaN(firstCoordinate.z)) { return Double.NaN; } if (Double.isNaN(lastCoordinate.z)) { return Double.NaN; } return firstCoordinate.z + (lastCoordinate.z - firstCoordinate.z) * firstCoordinate.distance(toBeInterpolated) / (firstCoordinate.distance(toBeInterpolated) + toBeInterpolated.distance(lastCoordinate)); } /** * Checks if a coordinate array contains a specific coordinate. * * The equality is done only in 2D (z values are not checked). * * @param coords * @param coord * @return */ public static boolean contains2D(Coordinate[] coords, Coordinate coord) { for (Coordinate coordinate : coords) { if (coordinate.equals2D(coord)) { return true; } } return false; } /** * Compute intersection point of two vectors * @param p1 Origin point * @param v1 Direction from p1 * @param p2 Origin point 2 * @param v2 Direction of p2 * @return Null if vectors are collinear or if intersection is done behind one of origin point */ public static Coordinate vectorIntersection(Coordinate p1, Vector3D v1, Coordinate p2, Vector3D v2) { double delta; Coordinate i = null; // Cramer's rule for compute intersection of two planes delta = v1.getX() * (-v2.getY()) - (-v1.getY()) * v2.getX(); if (delta != 0) { double k = ((p2.x - p1.x) * (-v2.getY()) - (p2.y - p1.y) * (-v2.getX())) / delta; // Fix precision problem with big decimal i = new Coordinate(p1.x + k * v1.getX(), p1.y + k * v1.getY(), p1.z + k * v1.getZ()); if(new LineSegment(p1, new Coordinate(p1.x + v1.getX(), p1.y + v1.getY())).projectionFactor(i) < 0 || new LineSegment(p2, new Coordinate(p2.x + v2.getX(), p2.y + v2.getY())).projectionFactor(i) < 0) { return null; } } return i; } /** * Remove dupliacted coordinates * Note : This method doesn't preserve the topology of geometry * * @param coords the input coordinates * @param closeRing is true the first coordinate is added at the end to close the array * @return */ public static Coordinate[] removeDuplicatedCoordinates(Coordinate[] coords, boolean closeRing) { LinkedHashSet<Coordinate> finalCoords = new LinkedHashSet<Coordinate>(); Coordinate prevCoord = coords[0]; finalCoords.add(prevCoord); Coordinate firstCoord = prevCoord; int nbCoords = coords.length; for (int i = 1; i < nbCoords; i++) { Coordinate currentCoord = coords[i]; if (currentCoord.equals2D(prevCoord)) { continue; } finalCoords.add(currentCoord); prevCoord = currentCoord; } if (closeRing) { Coordinate[] coordsFinal = finalCoords.toArray(new Coordinate[finalCoords.size()]); Coordinate[] closedCoords = Arrays.copyOf(coordsFinal, coordsFinal.length + 1); closedCoords[closedCoords.length-1] = firstCoord; return closedCoords; } return finalCoords.toArray(new Coordinate[finalCoords.size()]); } /** * Remove repeated coordinates according a given tolerance * * @param coords the input coordinates * @param tolerance to delete the coordinates * @param duplicateFirstLast false to delete the last coordinate * if there are equals * @return */ public static Coordinate[] removeRepeatedCoordinates(Coordinate[] coords, double tolerance, boolean duplicateFirstLast) { ArrayList<Coordinate> finalCoords = new ArrayList<Coordinate>(); Coordinate prevCoord = coords[0]; finalCoords.add(prevCoord); Coordinate firstCoord =prevCoord; int nbCoords = coords.length; for (int i = 1; i < nbCoords; i++) { Coordinate currentCoord = coords[i]; if (currentCoord.distance(prevCoord) <= tolerance) { continue; } finalCoords.add(currentCoord); prevCoord = currentCoord; } if (!duplicateFirstLast) { if (firstCoord.distance(prevCoord) <= tolerance) { finalCoords.remove(finalCoords.size()-1); } } else{ finalCoords.add(firstCoord); } return finalCoords.toArray(new Coordinate[finalCoords.size()]); } /** * Private constructor for utility class. */ private CoordinateUtils() { } }