All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jgrasstools.gears.utils.geometry.GeometryUtilities Maven / Gradle / Ivy

There is a newer version: 0.8.1
Show newest version
/*
 * JGrass - Free Open Source Java GIS http://www.jgrass.org 
 * (C) HydroloGIS - www.hydrologis.com 
 * 
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Library General Public License as published by the Free
 * Software Foundation; either version 2 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 Library General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Library General Public License
 * along with this library; if not, write to the Free Foundation, Inc., 59
 * Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */
package org.jgrasstools.gears.utils.geometry;

import static java.lang.Math.PI;
import static java.lang.Math.abs;
import static java.lang.Math.acos;
import static java.lang.Math.atan;
import static java.lang.Math.toDegrees;

import org.geotools.referencing.GeodeticCalculator;
import org.jgrasstools.gears.libs.monitor.DummyProgressMonitor;
import org.jgrasstools.gears.utils.math.NumericsUtilities;
import org.jgrasstools.gears.utils.sorting.QuickSortAlgorithm;
import org.opengis.feature.type.GeometryType;

import com.vividsolutions.jts.geom.Coordinate;
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.MultiPoint;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.PrecisionModel;

/**
 * Utilities related to {@link Geometry}.
 * 
 * @author Andrea Antonello (www.hydrologis.com)
 */
public class GeometryUtilities {

    /**
     * Geometry types used by the utility.
     */
    public static enum GEOMETRYTYPE {
        POINT, MULTIPOINT, LINE, MULTILINE, POLYGON, MULTIPOLYGON, UNKNOWN
    }

    private static GeometryFactory geomFactory;
    private static PrecisionModel precModel;

    public static PrecisionModel basicPrecisionModel() {
        return (pm());
    }

    public static GeometryFactory gf() {
        if (geomFactory == null) {
            geomFactory = new GeometryFactory();
        }
        return (geomFactory);
    }

    public static PrecisionModel pm() {
        if (precModel == null) {
            precModel = new PrecisionModel();
        }
        return (precModel);
    }

    /**
     * Create a simple polygon (no holes).
     * 
     * @param coords the coords of the polygon.
     * @return the {@link Polygon}.
     */
    public static Polygon createSimplePolygon( Coordinate[] coords ) {
        LinearRing linearRing = gf().createLinearRing(coords);
        return gf().createPolygon(linearRing, null);
    }

    /**
     * Creates a polygon that may help out as placeholder.
     * 
     * @return a dummy {@link Polygon}.
     */
    public static Polygon createDummyPolygon() {
        Coordinate[] c = new Coordinate[]{new Coordinate(0.0, 0.0), new Coordinate(1.0, 1.0), new Coordinate(1.0, 0.0),
                new Coordinate(0.0, 0.0)};
        LinearRing linearRing = gf().createLinearRing(c);
        return gf().createPolygon(linearRing, null);
    }

    public static Polygon createPolygonFromEnvelope( Envelope env ) {
        Coordinate[] c = new Coordinate[]{new Coordinate(env.getMinX(), env.getMinY()),
                new Coordinate(env.getMinX(), env.getMaxY()), new Coordinate(env.getMaxX(), env.getMaxY()),
                new Coordinate(env.getMaxX(), env.getMinY()), new Coordinate(env.getMinX(), env.getMinY())};
        LinearRing linearRing = gf().createLinearRing(c);
        return gf().createPolygon(linearRing, null);
    }

    /**
     * Calculates the angle between two {@link LineSegment}s.
     * 
     * @param l1 the first segment.
     * @param l2 the second segment.
     * @return the angle between the two segments, starting from the first segment 
     *                  moving clockwise.
     */
    public static double angleBetween( LineSegment l1, LineSegment l2 ) {
        double tol = 0.00001;
        // analyze slopes
        double s1 = (l1.p1.y - l1.p0.y) / (l1.p1.x - l1.p0.x);
        double s2 = (l2.p1.y - l2.p0.y) / (l2.p1.x - l2.p0.x);

        if (abs(s1 - s2) < tol)
            return (0);
        if (abs(s1 + s2) < tol)
            return (PI);

        // not of equal slope, transform lines so that they are tail to tip and
        // use the cosine law to calculate angle between

        // transform line segments tail to tail, originating at (0,0)
        LineSegment tls1 = new LineSegment(new Coordinate(0, 0), new Coordinate(l1.p1.x - l1.p0.x, l1.p1.y - l1.p0.y));
        LineSegment tls2 = new LineSegment(new Coordinate(0, 0), new Coordinate(l2.p1.x - l2.p0.x, l2.p1.y - l2.p0.y));

        // line segment for third side of triangle
        LineSegment ls3 = new LineSegment(tls1.p1, tls2.p1);

        double c = ls3.getLength();
        double a = tls1.getLength();
        double b = tls2.getLength();

        return toDegrees(acos((a * a + b * b - c * c) / (2 * a * b)));
    }

    /**
     * Calculates the azimuth in degrees given two {@link Coordinate} composing a line.
     * 
     * Note that the coords order is important and will differ of 180.
     * 
     * @param c1 first coordinate (used as origin).
     * @param c2 second coordinate.
     * @return the azimuth angle.
     */
    public static double azimuth( Coordinate c1, Coordinate c2 ) {
        // vertical
        if (c1.x == c2.x) {
            if (c1.y == c2.y) {
                // same point
                return Double.NaN;
            } else if (c1.y < c2.y) {
                return 0.0;
            } else if (c1.y > c2.y) {
                return 180.0;
            }
        }
        // horiz
        if (c1.y == c2.y) {
            if (c1.x < c2.x) {
                return 90.0;
            } else if (c1.x > c2.x) {
                return 270.0;
            }
        }
        // -> /
        if (c1.x < c2.x && c1.y < c2.y) {
            double tanA = (c2.x - c1.x) / (c2.y - c1.y);
            double atan = atan(tanA);
            return toDegrees(atan);
        }
        // -> \
        if (c1.x < c2.x && c1.y > c2.y) {
            double tanA = (c1.y - c2.y) / (c2.x - c1.x);
            double atan = atan(tanA);
            return toDegrees(atan) + 90.0;
        }
        // <- /
        if (c1.x > c2.x && c1.y > c2.y) {
            double tanA = (c1.x - c2.x) / (c1.y - c2.y);
            double atan = atan(tanA);
            return toDegrees(atan) + 180;
        }
        // <- \
        if (c1.x > c2.x && c1.y < c2.y) {
            double tanA = (c2.y - c1.y) / (c1.x - c2.x);
            double atan = atan(tanA);
            return toDegrees(atan) + 270;
        }

        return Double.NaN;
    }

    /**
     * Returns the {@link GEOMETRYTYPE} for a given {@link Geometry}. 
     * 
     * @param geometry the geometry to check.
     * @return the type.
     */
    public static GEOMETRYTYPE getGeometryType( Geometry geometry ) {
        if (geometry instanceof LineString) {
            return GEOMETRYTYPE.LINE;
        } else if (geometry instanceof MultiLineString) {
            return GEOMETRYTYPE.MULTILINE;
        } else if (geometry instanceof Point) {
            return GEOMETRYTYPE.POINT;
        } else if (geometry instanceof MultiPoint) {
            return GEOMETRYTYPE.MULTIPOINT;
        } else if (geometry instanceof Polygon) {
            return GEOMETRYTYPE.POLYGON;
        } else if (geometry instanceof MultiPolygon) {
            return GEOMETRYTYPE.MULTIPOLYGON;
        } else {
            return null;
        }
    }

    /**
     * Returns the {@link GEOMETRYTYPE} for a given {@link GeometryType}. 
     * 
     * @param geometryType the geometry type to check.
     * @return the type.
     */
    public static GEOMETRYTYPE getGeometryType( GeometryType geometryType ) {
        Class< ? > binding = geometryType.getBinding();

        if (binding == LineString.class) {
            return GEOMETRYTYPE.LINE;
        } else if (binding == MultiLineString.class) {
            return GEOMETRYTYPE.MULTILINE;
        } else if (binding == Point.class) {
            return GEOMETRYTYPE.POINT;
        } else if (binding == MultiPoint.class) {
            return GEOMETRYTYPE.MULTIPOINT;
        } else if (binding == Polygon.class) {
            return GEOMETRYTYPE.POLYGON;
        } else if (binding == MultiPolygon.class) {
            return GEOMETRYTYPE.MULTIPOLYGON;
        } else {
            return null;
        }
    }

    /**
     * Checks if the given geometry is a {@link LineString} (or {@link MultiLineString}) geometry.
     * 
     * @param geometry the geometry to check.
     * @return true if there are lines in there.
     */
    public static boolean isLine( Geometry geometry ) {
        if (geometry instanceof LineString || geometry instanceof MultiLineString) {
            return true;
        }
        return false;
    }

    /**
     * Checks if the given geometry is a {@link Polygon} (or {@link MultiPolygon}) geometry.
     * 
     * @param geometry the geometry to check.
     * @return true if there are polygons in there.
     */
    public static boolean isPolygon( Geometry geometry ) {
        if (geometry instanceof Polygon || geometry instanceof MultiPolygon) {
            return true;
        }
        return false;
    }

    /**
     * Checks if the given geometry is a {@link Point} (or {@link MultiPoint}) geometry.
     * 
     * @param geometry the geometry to check.
     * @return true if there are points in there.
     */
    public static boolean isPoint( Geometry geometry ) {
        if (geometry instanceof Point || geometry instanceof MultiPoint) {
            return true;
        }
        return false;
    }

    /**
     * Calculates the area of a polygon from its vertices.
     * 
     * @param x the array of x coordinates.
     * @param y the array of y coordinates.
     * @param N the number of sides of the polygon.
     * @return the area of the polygon.
     */
    public static double getPolygonArea( int[] x, int[] y, int N ) {
        int i, j;
        double area = 0;

        for( i = 0; i < N; i++ ) {
            j = (i + 1) % N;
            area += x[i] * y[j];
            area -= y[i] * x[j];
        }

        area /= 2;
        return (area < 0 ? -area : area);
    }

    /**
     * Calculates the 3d distance between two coordinates that have an elevation information.
     * 
     * 

Note that the {@link Coordinate#distance(Coordinate)} method does only 2d. * * @param c1 coordinate 1. * @param c2 coordinate 2. * @param geodeticCalculator If supplied it will be used for planar distance calculation. * @return the distance considering also elevation. */ public static double distance3d( Coordinate c1, Coordinate c2, GeodeticCalculator geodeticCalculator ) { if (Double.isNaN(c1.z) || Double.isNaN(c2.z)) { throw new IllegalArgumentException("Missing elevation information in the supplied coordinates."); } double deltaElev = Math.abs(c1.z - c2.z); double projectedDistance; if (geodeticCalculator != null) { geodeticCalculator.setStartingGeographicPoint(c1.x, c1.y); geodeticCalculator.setDestinationGeographicPoint(c2.x, c2.y); projectedDistance = geodeticCalculator.getOrthodromicDistance(); } else { projectedDistance = c1.distance(c2); } double distance = NumericsUtilities.pythagoras(projectedDistance, deltaElev); return distance; } public static void sortHorizontal( Coordinate[] coordinates ) { QuickSortAlgorithm sorter = new QuickSortAlgorithm(new DummyProgressMonitor()); double[] x = new double[coordinates.length]; double[] y = new double[coordinates.length]; for( int i = 0; i < x.length; i++ ) { x[i] = coordinates[i].x; y[i] = coordinates[i].y; } sorter.sort(x, y); for( int i = 0; i < x.length; i++ ) { coordinates[i].x = x[i]; coordinates[i].y = y[i]; } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy