Package org.locationtech.udig.tools.geometry.split

Source Code of org.locationtech.udig.tools.geometry.split.RingExtractor

/* uDig - User Friendly Desktop Internet GIS
* http://udig.refractions.net
* (c) 2010, Vienna City
*
* 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.split;

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

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateArrays;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.LinearRing;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.operation.polygonize.Polygonizer;

import org.locationtech.udig.tools.geometry.merge.MergeStrategy;

/**
*
* This class is responsible of extracting the rings and the "remaining" line from a
* line that has at least one self-ring.
*
* @author Mauricio Pazos (www.axios.es)
* @author Aritz Davila (www.axios.es)
* @since 1.3.0
*/
public final class RingExtractor {

  /** The geometry factory used to build geometries. */
  private GeometryFactory  gf    = null;

  /** The original line */
  private Geometry    line  = null;

  /**
   * Default constructor.
   *
   * @param line
   *            The line to be analyzed.
   */
  public RingExtractor(Geometry line) {

    // union will create "rings" but they are multilines.
    // so, remove repeated coordinates (the union between each multiline
    // geometry) and then create the line again.

    Geometry linegeom = line.union();
    Coordinate[] coordinates = linegeom.getCoordinates();
    coordinates = CoordinateArrays.removeRepeatedPoints(coordinates);

    this.line = linegeom.getFactory().createLineString(coordinates);
    this.gf = line.getFactory();
  }

  /**
   * Will execute the extraction operation. First it get the rings, and then
   * it "extracts" those rings from the original line getting by this way the
   * remaining line. (it could be a line or a multiline geometry)
   *
   * @return {@link ResultRingExtractor}. It contains 2 object, one is a list
   *         with the rings and the other is the remaining geometry.
   */
  public ResultRingExtractor processExtraction() {

    assert this.line != null : "the line can't be null"; //$NON-NLS-1$
    assert this.gf != null : "the geometry factory can't be null"; //$NON-NLS-1$

    // use the Polygonizer to get the rings.
    List<Geometry> ringList = new ArrayList<Geometry>();
    Geometry multiLines =this.line.union();
    Polygonizer polygonizer = new Polygonizer();
    polygonizer.add(multiLines);
    Collection<Polygon> polyCollection = polygonizer.getPolygons();

    // add the rings to the ringList.
    for (Polygon pol : polyCollection) {

      Coordinate[] polCoord = pol.getExteriorRing().getCoordinates();

      LinearRing polygonRing = this.gf.createLinearRing(polCoord);
      ringList.add(polygonRing);
    }

    // get the remaining line.
    Geometry result = getRemainingLine(line.getCoordinates(), ringList);

    // create the output data.
    ResultRingExtractor outputData = new ResultRingExtractor(result, ringList);
    return outputData;
  }

  /**
   * Get the remaining line by doing the difference operation, and then seek
   * for overlapped lines on the line with its holes. If it founds, add those
   * overlapped lines to the remaining line.
   *
   * @param originalCoord
   *            Coordinates from the original line.
   * @param ringList
   *            The extracted rings.
   * @return The remaining line.
   */
  private Geometry getRemainingLine(Coordinate[] originalCoord, List<Geometry> ringList) {

    List<Geometry> segmentsToAdd = new LinkedList<Geometry>();
    // apply the difference operation.
    Geometry result = this.line;
    for (Geometry ring : ringList) {

      result = result.difference(ring);
    }
    // union for nodded multilineStrings.
    result = SplitUtil.buildLineUnion(result);
    // seek for overlapped segments.
    for (int i = 0; i < originalCoord.length - 1; i++) {

      // mount a segment and see if its intersection with any other
      // segment from the original line return a lineString as result,
      // this would mean that it is overlapped.
      Coordinate[] segCoords = new Coordinate[2];
      segCoords[0] = originalCoord[i];
      segCoords[1] = originalCoord[i + 1];
      LineString segmentToTest = this.gf.createLineString(segCoords);
      for (int j = i + 1; j < originalCoord.length - 1; j++) {

        Coordinate[] testCoord = new Coordinate[2];
        testCoord[0] = originalCoord[j];
        testCoord[1] = originalCoord[j + 1];
        LineString eachSegment = this.gf.createLineString(testCoord);

        if (segmentToTest.intersects(eachSegment)
              && segmentToTest.intersection(eachSegment) instanceof LineString) {

          // first store those segments
          segmentsToAdd.add(segmentToTest.intersection(eachSegment));
        }
      }
    }
    if (segmentsToAdd.size() != 0) {
      result = nodSegments(segmentsToAdd, ringList, result);
    }
    return result;
  }

  /**
   * Node contiguous segments.
   *
   * @param segmentsToAdd
   *            Segments to be nodded with the actualResultLine.
   * @param ringList
   *            The list of rings.
   * @param actualResultLine
   *            The actual lineString.
   * @return The segments nodded to the actualResultLine if they can be
   *         nodded.
   */
  private Geometry nodSegments(List<Geometry> segmentsToAdd, List<Geometry> ringList, Geometry actualResultLine) {

    Geometry result = actualResultLine;
    // check if that lineString exist or is contained in any of
    // the rings.
    for (Geometry segment : segmentsToAdd) {

      assert segment.getCoordinates().length == 2 : "the segment should have 2 coordinates."; //$NON-NLS-1$
      // check with each ring.
      for (Geometry ring : ringList) {
        // contained
        if (ring.contains(segment) || ring.intersects(segment)) {

          result = MergeStrategy.mergeOp(result, segment);
        }
      }
    }

    return result;
  }

  /**
   * Data class that stores the ring extractor data. It'll be the remaining
   * line and the rings.
   */
  public class ResultRingExtractor {

    /* Stores the remaining line. */
    private Geometry    remainingLine;
    /* Stores the extracted rings. */
    private List<Geometry>  rings;

    /**
     * Constructor used to store the data.
     *
     * @param remainingLine
     *            The remaining line.
     * @param rings
     *            Rings obtained from the extraction process.
     */
    public ResultRingExtractor(Geometry remainingLine, List<Geometry> rings) {

      this.remainingLine = remainingLine;
      this.rings = rings;
    }

    /**
     * Get the remaining line.
     *
     * @return A lineString or multiLineString.
     */
    public Geometry getRemainingLine() {

      return this.remainingLine;
    }

    /**
     * Get the extracted rings.
     *
     * @return A list of linearRings.
     */
    public List<Geometry> getRings() {

      return this.rings;
    }

  }

}
TOP

Related Classes of org.locationtech.udig.tools.geometry.split.RingExtractor

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.