Package org.locationtech.udig.tools.geometry.internal.util

Source Code of org.locationtech.udig.tools.geometry.internal.util.GeometryUtil

/* uDig - User Friendly Desktop Internet GIS client
* http://udig.refractions.net
* (C) 2012, Refractions Research Inc.
* (C) 2006, Axios Engineering S.L. (Axios)
* (C) 2006, County Council of Gipuzkoa, Department of Environment and Planning
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* (http://www.eclipse.org/legal/epl-v10.html), and the Axios BSD
* License v1.0 (http://udig.refractions.net/files/asd3-v10.html).
*/
package org.locationtech.udig.tools.geometry.internal.util;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;

import com.vividsolutions.jts.algorithm.CentralEndpointIntersector;
import com.vividsolutions.jts.algorithm.HCoordinate;
import com.vividsolutions.jts.algorithm.NotRepresentableException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
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.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.polygonize.Polygonizer;

import org.locationtech.udig.tools.geometry.merge.MergeStrategy;
import org.locationtech.udig.tools.internal.i18n.Messages;

/**
* Geometry utility methods
* <p>
* Collection of method which gets feature or feature collection to applay
* geometry operations
* </p>
*
* @author Mauricio Pazos (www.axios.es)
* @author Aritz Davila (www.axios.es)
* @since 1.1.0
*/
public class GeometryUtil {

  /**
   * unused
   */
  private GeometryUtil() {

    // util class
  }

  /**
   * Returns a geometry which is the union of all the non null default
   * geometries from the features in <code>featureCollection</code>
   *
   * @param featureCollection
   * @param expectedGeometryClass
   *
   * @return Geometry Union
   */
  public static Geometry geometryUnion(final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection) {

    Geometry resultGeom = null;
    FeatureIterator<SimpleFeature>  iterator = featureCollection.features();
    try {
      SimpleFeature currFeature;
      while (iterator.hasNext()) {

        currFeature = iterator.next();
        Geometry featureGeom = (Geometry) currFeature.getDefaultGeometry();
        assert featureGeom != null : "the feature must have almost one geometry"; //$NON-NLS-1$

        featureGeom.normalize();
        if (resultGeom == null) {
          resultGeom = featureGeom;
        } else {
          resultGeom = MergeStrategy.mergeOp(resultGeom, featureGeom);
        }
      }

    } finally {
      iterator.close();
    }
    return resultGeom;
  }

  /**
   * Extracts the geometries and makes a geometry array.
   * <p>
   * Note the resulting array size might be lower than the featureCollection
   * size, as it will not contain null geometries.
   * </p>
   *
   * @param featureCollection
   * @return Geometry[] geometries present in features
   */
  public static Geometry[] extractGeometries(final FeatureCollection<SimpleFeatureType, SimpleFeature> featureCollection) {

    FeatureIterator<SimpleFeature> iter = featureCollection.features();
    try {
      SimpleFeature feature;
      ArrayList<Geometry> geometries = new ArrayList<Geometry>(featureCollection.size());
      int finalSize = 0;
      while (iter.hasNext()) {

        feature = iter.next();
        Geometry geometry = (Geometry) feature.getDefaultGeometry();
        if (geometry == null) {
          continue;
        } else if (geometry instanceof GeometryCollection) {

          GeometryCollection geomSet = (GeometryCollection) geometry;
          final int size = geomSet.getNumGeometries();
          for (int j = 0; j < size; j++) {
            geometries.add(geomSet.getGeometryN(j));
          }
          finalSize += size;

        } else {
          geometries.add(geometry);
          finalSize++;
        }
      }

      return geometries.toArray(new Geometry[finalSize]);

    } finally {
      iter.close();
    }

  }

  /**
   * Adapts a Geometry <code>geom</code> to another type of geometry given the
   * desired geometry class.
   * <p>
   * Currently implemented adaptations:
   * <ul>
   * <li>Point -> MultiPoint. Wraps the Point on a single part MultiPoint.
   * <li>Polygon -> MultiPolygon. Wraps the Polygon on a single part
   * MultiPolygon.
   * <li>LineString -> MultiLineString. Wraps the LineString on a single part
   * MultiLineString.
   * <li>MultiLineString -> LineString. Succeeds if merging the parts result
   * in a single LineString, fails otherwise.
   * <li>MultiPolygon -> Polygon. Succeeds if merging the parts result in a
   * single Polygon, fails otherwise.
   * <li>* -> GeometryCollection
   * </ul>
   *
   * If the given type of geometry is the same as the desired one, returns
   * that type of geometry.
   * </p>
   * TODO: add more adaptations on an as needed basis
   *
   * @param inputGeom
   * @param adaptTo
   * @return a new Geometry adapted
   * @throws IllegalArgumentException
   *             if <code>geom</code> cannot be adapted as <code>adapTo</code>
   */
  public static Geometry adapt(final Geometry inputGeom, final Class<? extends Geometry> adaptTo) {

    assert inputGeom != null : "inputGeom can't be null"; //$NON-NLS-1$
    assert adaptTo != null : "adaptTo can't be null"; //$NON-NLS-1$

    if (Geometry.class.equals(adaptTo)) {
      return inputGeom;
    }
    final Class<?> geomClass = inputGeom.getClass();

    if (geomClass.equals(adaptTo)) {
      return inputGeom;
    }

    final GeometryFactory gf = inputGeom.getFactory();

    if (MultiPoint.class.equals(adaptTo) && Point.class.equals(geomClass)) {
      return gf.createMultiPoint(new Point[] { (Point) inputGeom });
    }

    if (Polygon.class.equals(adaptTo)) {
      if (adaptTo.equals(geomClass)) {
        return inputGeom;
      }
      Polygonizer polygonnizer = new Polygonizer();
      polygonnizer.add(inputGeom);
      Collection<? extends Polygon> polys = polygonnizer.getPolygons();
      Polygon[] polygons = new ArrayList<Polygon>(polys).toArray(new Polygon[polys.size()]);

      if (polygons.length == 1) {
        return polygons[0];
      }
    }

    if (MultiPolygon.class.equals(adaptTo)) {
      if (adaptTo.equals(geomClass)) {
        return inputGeom;
      }
      if (Polygon.class.equals(geomClass)) {
        return gf.createMultiPolygon(new Polygon[] { (Polygon) inputGeom });
      }
      /*
       * Polygonizer polygonnizer = new Polygonizer();
       * polygonnizer.add(inputGeom); Collection polys =
       * polygonnizer.getPolygons(); Polygon[] polygons = new
       * ArrayList<Polygon>(polys).toArray(new Polygon[polys.size()]); if
       * (MultiPolygon.class.equals(adaptTo)) { return
       * gf.createMultiPolygon(polygons); } if (polygons.length == 1) {
       * return polygons[0]; }
       */
    }

    if (GeometryCollection.class.equals(adaptTo)) {
      return gf.createGeometryCollection(new Geometry[] { inputGeom });
    }

    if (MultiLineString.class.equals(adaptTo) || LineString.class.equals(adaptTo)) {
      LineMerger merger = new LineMerger();
      merger.add(inputGeom);
      Collection<? extends LineString> mergedLineStrings = merger.getMergedLineStrings();
      ArrayList<LineString> lineList = new ArrayList<LineString>(mergedLineStrings);
      LineString[] lineStrings = lineList.toArray(new LineString[mergedLineStrings.size()]);

      assert lineStrings.length >= 1;

      if (MultiLineString.class.equals(adaptTo)) {
        MultiLineString line = gf.createMultiLineString(lineStrings);
        return line;
      } else { // adapts to LineString class
        if (lineStrings.length == 1) {
          Geometry mergedResult = (Geometry) lineStrings[0];
          return mergedResult;

        } else { // it has more than one geometry
          final String msg = MessageFormat.format(Messages.GeometryUtil_DonotKnowHowAdapt,
                geomClass.getSimpleName(), adaptTo.getSimpleName());
          throw new IllegalArgumentException(msg);
        }
      }
    }
    if (Polygon.class.equals(adaptTo) && (MultiPolygon.class.equals(geomClass))) {
      // adapts multipolygon to polygon

      assert inputGeom.getNumGeometries() == 1 : "the collection must have 1 element to adapt to Polygon"; //$NON-NLS-1$
      return inputGeom.getGeometryN(1);

    } else if (LineString.class.equals(adaptTo) && (MultiLineString.class.equals(geomClass))) {
      // adapts MultiLinestring to Linestring

      assert inputGeom.getNumGeometries() == 1 : "the collection must have 1 element to adapt to Polygon"; //$NON-NLS-1$
      return inputGeom.getGeometryN(1);
    }
    // Adapt a geometry collection that contains points, to Point or
    // MultiPoint.
    if (GeometryCollection.class.equals(geomClass)
          && (MultiPoint.class.equals(adaptTo) || Point.class.equals(adaptTo))) {
      List<Geometry> geomList = new ArrayList<Geometry>();
      for (int i = 0; i < inputGeom.getNumGeometries(); i++) {
        Geometry geom = inputGeom.getGeometryN(i);
        Class<?> clazz = geom.getClass();
        if (MultiPoint.class.equals(clazz) || Point.class.equals(clazz)) {
          geomList.add(geom);
        }
      }
      return gf.buildGeometry(geomList);
    }

    // Adapt a geometry collection that contains lines, to LineString or
    // MultiLineString.
    if (GeometryCollection.class.equals(geomClass)
          && (MultiLineString.class.equals(adaptTo) || LineString.class.equals(adaptTo))) {
      List<Geometry> geomList = new ArrayList<Geometry>();
      for (int i = 0; i < inputGeom.getNumGeometries(); i++) {
        Geometry geom = inputGeom.getGeometryN(i);
        Class<?> clazz = geom.getClass();
        if (MultiLineString.class.equals(clazz) || LineString.class.equals(clazz)) {
          geomList.add(geom);
        }
      }
      return gf.buildGeometry(geomList);
    }

    // Adapt a geometry collection that contains polygons, to Polyong or
    // MultiPolygon.
    if (GeometryCollection.class.equals(geomClass)
          && (MultiPolygon.class.equals(adaptTo) || Polygon.class.equals(adaptTo))) {
      List<Geometry> geomList = new ArrayList<Geometry>();
      for (int i = 0; i < inputGeom.getNumGeometries(); i++) {
        Geometry geom = inputGeom.getGeometryN(i);
        Class<?> clazz = geom.getClass();
        if (MultiPolygon.class.equals(clazz) || Polygon.class.equals(clazz)) {
          geomList.add(geom);
        }
      }
      return gf.buildGeometry(geomList);
    }

    final String msg = MessageFormat.format(Messages.GeometryUtil_DonotKnowHowAdapt, geomClass.getSimpleName(),
          adaptTo.getSimpleName());

    throw new IllegalArgumentException(msg);
  }

  /**
   * Creates a geometry collection using the simple geometries contained in
   * the gem List.
   *
   * @param simpleGeometries
   *            homogeneous list of zero or more geometries of the class:
   *            Points, LineStrings or Polygons
   * @param expectedClass
   *            expected result class
   *
   * @return a new instance of GeometryCollection with the simple geometries
   */
  public static GeometryCollection adaptToGeomCollectionArrayList<Geometry> simpleGeometries,
                              Class<? extends GeometryCollection> expectedClass) {

    if (simpleGeometries.size() == 0) {

      return GeometryUtil.getNullGeometryCollection();
    }

    final GeometryFactory geomFactory = simpleGeometries.get(0).getFactory();
    GeometryCollection geomResult = null;
    if (MultiPolygon.class.equals(expectedClass)) {
      geomResult = geomFactory.createMultiPolygon(simpleGeometries.toArray(new Polygon[simpleGeometries.size()]));
    } else if (MultiLineString.class.equals(expectedClass)) {
      geomResult = geomFactory.createMultiLineString(simpleGeometries.toArray(new LineString[simpleGeometries
            .size()]));
    } else if (MultiPoint.class.equals(expectedClass)) {
      geomResult = geomFactory.createMultiPoint(simpleGeometries.toArray(new Point[simpleGeometries.size()]));

    } else {

      assert false : "illegal argument: the expectedClass parameter must be a subclass of GeometryCollection"; //$NON-NLS-1$
    }

    return geomResult;
  }

  /**
   *
   * @return an empty geometry collection
   */
  private static GeometryCollection getNullGeometryCollection() {

    GeometryFactory gf = new GeometryFactory();
    return gf.createGeometryCollection(new Geometry[] {});
  }

  /**
   * creates the source geometry array to the class required
   *
   * @param sourceGeomArray
   *            one or more geometries
   * @param requiredGeom
   *
   * @return new geometry of the required class
   */
  public static Geometry adapt(final ArrayList<Geometry> sourceGeomArray, final Class<? extends Geometry> requiredGeom) {

    assert sourceGeomArray.size() >= 1 : "one or more geometries are required in the source geometry collection"; //$NON-NLS-1$

    final GeometryFactory geomFactory = sourceGeomArray.get(0).getFactory();

    Geometry result;
    if (GeometryCollection.class.equals(requiredGeom.getSuperclass())) {

      Class<? extends GeometryCollection> geomCollectionClass = (Class<? extends GeometryCollection>) requiredGeom;
      result = adaptToGeomCollection(sourceGeomArray, geomCollectionClass);

    } else {// simple geometry is required

      result = geomFactory.buildGeometry(sourceGeomArray);
    }

    return result;
  }

  /**
   * @param geomClass
   * @return the geometry class's dimension
   */
  public static int getDimension(final Class<?> geomClass) {

    if ((Point.class.equals(geomClass)) || (MultiPoint.class.equals(geomClass))) {
      return 0;
    } else if ((LineString.class.equals(geomClass)) || (MultiLineString.class.equals(geomClass))) {
      return 1;
    } else if ((Polygon.class.equals(geomClass)) || (MultiPolygon.class.equals(geomClass))) {
      return 2;
    } else {
      final String msg = MessageFormat.format(Messages.GeometryUtil_CannotGetDimension, geomClass.getName());
      throw new IllegalArgumentException(msg);
    }
  }

  /**
   * @param simpleGeometry
   *            Point, LineString or Polygon class
   * @return a compatible Geometry collection for the simple geometry
   */
  public static Class<? extends GeometryCollection> getCompatibleCollection(final Class<? extends Geometry> simpleGeometry) {

    if (Point.class.equals(simpleGeometry)) {
      return MultiPoint.class;
    } else if (LineString.class.equals(simpleGeometry)) {
      return MultiLineString.class;
    } else if (Polygon.class.equals(simpleGeometry)) {
      return MultiPolygon.class;
    } else {
      throw new IllegalArgumentException(Messages.GeometryUtil_ExpectedSimpleGeometry);
    }
  }

  /**
   * <p>
   *
   * <pre>
   * Converts the feature LineString into Polygon. If the LineString geometry
   * can't be converted, (i.e, the line has 2 coordinates, so its impossible
   * to create a linearRing) throws {@link IllegalArgumentException}.
   *
   * The line must has at least 3 coordinates.
   *
   * - Get the line geometry.
   * - For each geometry, create a LinerRing.
   * - Combine all the LinearRing into one Geometry.
   *
   * Return the combined geometry.
   *
   * </pre>
   *
   * </p>
   *
   * @param feature
   * @return
   */
  public static Geometry convertLineStringIntoPolygonGeometry(SimpleFeature feature) {

    GeometryFactory factory = new GeometryFactory();
    Geometry featureGeom = (Geometry) feature.getDefaultGeometry();

    List<Geometry> ringsList = new ArrayList<Geometry>();

    for (int i = 0; i < featureGeom.getNumGeometries(); i++) {

      Geometry line = featureGeom.getGeometryN(i);

      Coordinate[] lineCoor = line.getCoordinates();
      if (lineCoor.length < 3) {
        // not a valid geometry for convert to polygon, so continue.
        continue;
      }
      // for creating a linearRing, the geometry must form a closed
      // geometry.
      // close the geometry.
      Coordinate[] closedLine = closeGeometry(lineCoor);
      // try to create a linearRing.
      LinearRing ring;
      try {
        ring = factory.createLinearRing(closedLine);
        // Create the polygon geometry and add to the list.
        Geometry polygonGeometry = factory.createPolygon(ring, null);
        ringsList.add(polygonGeometry);
      } catch (Exception e) {
        // Couldn't create a ring from this geometry,so continue.
        continue;
      }
    }

    Geometry combined = factory.buildGeometry(ringsList);
    // combined.union();

    return combined;
  }

  /**
   * This coordinates belong to a LineString geometry, it's coordinates aren't
   * closed, close the coordinate. The first coordinate must be equal to the
   * last coordinate.
   *
   * @param lineCoor
   * @return
   */
  public static Coordinate[] closeGeometry(Coordinate[] lineCoor) {

    if (lineCoor[0].equals(lineCoor[lineCoor.length - 1])) {
      // it's closed so return it.
      return lineCoor;
    }

    Coordinate[] closedCoor = new Coordinate[lineCoor.length + 1];
    int i;
    for (i = 0; i < lineCoor.length; i++) {
      closedCoor[i] = lineCoor[i];
    }
    // closed the geometry by setting the last coordinate equal to the first
    // coordinate
    closedCoor[i] = lineCoor[0];

    assert closedCoor[0].equals(closedCoor[closedCoor.length - 1]);

    return closedCoor;

  }

  private static Coordinate[][]  inputLines  = new Coordinate[2][2];

  /**
   * This method computes the actual value of the intersection point. To
   * obtain the maximum precision from the intersection calculation, the
   * coordinates are normalized by subtracting the minimum ordinate values (in
   * absolute value). This has the effect of removing common significant
   * digits from the calculation to maintain more bits of precision.
   *
   * @param precisionModel
   */
  public static Coordinate intersection(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2) {
    inputLines[0][0] = p1;
    inputLines[0][1] = p2;
    inputLines[1][0] = q1;
    inputLines[1][1] = q2;
    Coordinate intPt = intersectionWithNormalization(p1, p2, q1, q2);

    return intPt;
  }

  private static Coordinate intersectionWithNormalization(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2) {
    Coordinate n1 = new Coordinate(p1);
    Coordinate n2 = new Coordinate(p2);
    Coordinate n3 = new Coordinate(q1);
    Coordinate n4 = new Coordinate(q2);
    Coordinate normPt = new Coordinate();
    normalizeToEnvCentre(n1, n2, n3, n4, normPt);

    Coordinate intPt = safeHCoordinateIntersection(n1, n2, n3, n4);

    intPt.x += normPt.x;
    intPt.y += normPt.y;

    return intPt;
  }

  /**
   * Normalize the supplied coordinates to so that the midpoint of their
   * intersection envelope lies at the origin.
   *
   * @param n00
   * @param n01
   * @param n10
   * @param n11
   * @param normPt
   */
  private static void normalizeToEnvCentreCoordinate n00,
                        Coordinate n01,
                        Coordinate n10,
                        Coordinate n11,
                        Coordinate normPt) {
    double minX0 = n00.x < n01.x ? n00.x : n01.x;
    double minY0 = n00.y < n01.y ? n00.y : n01.y;
    double maxX0 = n00.x > n01.x ? n00.x : n01.x;
    double maxY0 = n00.y > n01.y ? n00.y : n01.y;

    double minX1 = n10.x < n11.x ? n10.x : n11.x;
    double minY1 = n10.y < n11.y ? n10.y : n11.y;
    double maxX1 = n10.x > n11.x ? n10.x : n11.x;
    double maxY1 = n10.y > n11.y ? n10.y : n11.y;

    double intMinX = minX0 > minX1 ? minX0 : minX1;
    double intMaxX = maxX0 < maxX1 ? maxX0 : maxX1;
    double intMinY = minY0 > minY1 ? minY0 : minY1;
    double intMaxY = maxY0 < maxY1 ? maxY0 : maxY1;

    double intMidX = (intMinX + intMaxX) / 2.0;
    double intMidY = (intMinY + intMaxY) / 2.0;
    normPt.x = intMidX;
    normPt.y = intMidY;

    /*
     * // equilavalent code using more modular but slower method Envelope
     * env0 = new Envelope(n00, n01); Envelope env1 = new Envelope(n10,
     * n11); Envelope intEnv = env0.intersection(env1); Coordinate intMidPt
     * = intEnv.centre();
     *
     * normPt.x = intMidPt.x; normPt.y = intMidPt.y;
     */

    n00.x -= normPt.x;
    n00.y -= normPt.y;
    n01.x -= normPt.x;
    n01.y -= normPt.y;
    n10.x -= normPt.x;
    n10.y -= normPt.y;
    n11.x -= normPt.x;
    n11.y -= normPt.y;
  }

  /**
   * Computes a segment intersection using homogeneous coordinates. Round-off
   * error can cause the raw computation to fail, (usually due to the segments
   * being approximately parallel). If this happens, a reasonable
   * approximation is computed instead.
   *
   * @param p1
   *            a segment endpoint
   * @param p2
   *            a segment endpoint
   * @param q1
   *            a segment endpoint
   * @param q2
   *            a segment endpoint
   * @return the computed intersection point
   */
  private static Coordinate safeHCoordinateIntersection(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2) {

    Coordinate intPt = null;
    try {
      intPt = HCoordinate.intersection(p1, p2, q1, q2);
    } catch (NotRepresentableException e) {
      // compute an approximate result
      intPt = CentralEndpointIntersector.getIntersection(p1, p2, q1, q2);
    }
    return intPt;
  }

  /**
   * Rotate the given lineString with the provided angle.
   *
   * @param input
   *            The lineString to rotate.
   * @param angle
   *            The angle in radiant.
   * @return The rotated lineString.
   */
  public static LineString rotation(LineString input, double angle) {

    Coordinate center = input.getCentroid().getCoordinate();
    return rotation(input, angle, center);
  }

  /**
   * Rotate the given lineString with the provided angle and a center point as
   * a reference point required for rotate.
   *
   * @param input
   *            The input lineString to rotate.
   * @param angle
   *            The angle in radiant.
   * @param center
   *            The center point as reference to rotate.
   * @return The rotated lineString.
   */
  public static LineString rotation(LineString input, double angle, Coordinate center) {

    GeometryFactory gf = input.getFactory();

    Coordinate[] lineCoords = input.getCoordinates();
    List<Coordinate> rotationCoords = new LinkedList<Coordinate>();

    for (int i = 0; i < lineCoords.length; i++) {

      Coordinate actualCoord = lineCoords[i];
      rotationCoords.add(rotate(actualCoord, center, angle));
    }

    Coordinate[] newCoords = rotationCoords.toArray(new Coordinate[rotationCoords.size()]);

    return gf.createLineString(newCoords);
  }

  /**
   * Rotate the given polygon.
   *
   * @param input
   *            The polygon to rotate.
   * @param angle
   *            Angle in radiant.
   * @return The rotated polygon.
   */
  public static Polygon rotation(Polygon input, double angle) {

    Coordinate center = input.getCentroid().getCoordinate();
    return rotation(input, angle, center);
  }

  /**
   * Rotates the polygon.
   *
   * @param input
   *            The polygon to rotate
   * @param angle
   *            Angle in radiant.
   * @param pivot
   *            x,y coordinate reference needed to rotate.
   * @return a new polygon
   */
  public static Polygon rotation(Polygon input, double angle, Coordinate pivot) {

    GeometryFactory gf = input.getFactory();

    Coordinate[] shellCoords = input.getExteriorRing().getCoordinates();
    List<Coordinate> rotationShell = new LinkedList<Coordinate>();

    for (int i = 0; i < shellCoords.length; i++) {

      Coordinate actualCoord = shellCoords[i];
      rotationShell.add(rotate(actualCoord, pivot, angle));
    }

    List<LinearRing> holes = new LinkedList<LinearRing>();

    for (int j = 0; j < input.getNumInteriorRing(); j++) {

      List<Coordinate> rotationHole = new LinkedList<Coordinate>();
      Coordinate[] holeCoords = input.getInteriorRingN(j).getCoordinates();
      for (int i = 0; i < holeCoords.length; i++) {

        Coordinate actualCoord = holeCoords[i];
        rotationHole.add(rotate(actualCoord, pivot, angle));
      }
      Coordinate[] rotatedHole = rotationHole.toArray(new Coordinate[rotationHole.size()]);
      holes.add(gf.createLinearRing(rotatedHole));
    }

    Coordinate[] newCoords = rotationShell.toArray(new Coordinate[rotationShell.size()]);
    LinearRing shell = gf.createLinearRing(newCoords);

    LinearRing[] rings = holes.toArray(new LinearRing[holes.size()]);

    return gf.createPolygon(shell, rings);
  }

  /**
   * Rotate the given coordinate.
   *
   * @param actualCoord
   *            Coordinate to rotate.
   * @param center
   *            Reference coordinate needed to rotate.
   * @param angle
   *            Angle in radiant.
   * @return The rotated coordinate.
   */
  private static Coordinate rotate(Coordinate actualCoord, Coordinate center, double angle) {

    Coordinate rotateCoord = new Coordinate();
    rotateCoord.x = (Math.cos(angle) * (actualCoord.x - center.x)) - (Math.sin(angle) * (actualCoord.y - center.y))
          + center.x;
    rotateCoord.y = (Math.sin(angle) * (actualCoord.x - center.x)) + (Math.cos(angle) * (actualCoord.y - center.y))
          + center.y;

    assert rotateCoord != null;

    return rotateCoord;
  }


}
TOP

Related Classes of org.locationtech.udig.tools.geometry.internal.util.GeometryUtil

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.