CoverageBoundarySegmentFinder.java

/*
 * Copyright (c) 2022 Martin Davis.
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
 * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
 * and the Eclipse Distribution License is available at
 *
 * http://www.eclipse.org/org/documents/edl-v10.php.
 */
package org.locationtech.jts.coverage;

import java.util.HashSet;
import java.util.Set;

import org.locationtech.jts.geom.CoordinateSequence;
import org.locationtech.jts.geom.CoordinateSequenceFilter;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineSegment;

/**
 * Finds coverage segments which occur in only a single coverage element.
 * In a valid coverage, these are exactly the line segments which lie
 * on the boundary of the coverage.
 * <p>
 * In an invalid coverage, segments might occur in 3 or more elements.
 * This situation is not detected.
 * 
 * @author mdavis
 *
 */
class CoverageBoundarySegmentFinder implements CoordinateSequenceFilter {
  
  public static Set<LineSegment> findBoundarySegments(Geometry[] geoms) {
    Set<LineSegment> segs = new HashSet<LineSegment>();
    CoverageBoundarySegmentFinder finder = new CoverageBoundarySegmentFinder(segs);
    for (Geometry geom : geoms) {
      geom.apply(finder);
    }
    return segs;
  }

  public static boolean isBoundarySegment(Set<LineSegment> boundarySegs, CoordinateSequence seq, int i) {
    LineSegment seg = createSegment(seq, i);
    return boundarySegs.contains(seg);
  }
  
  private Set<LineSegment> boundarySegs;

  public CoverageBoundarySegmentFinder(Set<LineSegment> segs) {
    this.boundarySegs = segs;
  }

  @Override
  public void filter(CoordinateSequence seq, int i) {
    //-- final point does not start a segment
    if (i >= seq.size() - 1)
      return;
    LineSegment seg = createSegment(seq, i);
    /**
     * Records segments with an odd number of occurrences.
     * In a valid coverage each segment can occur only 1 or 2 times.
     * This does not detect invalid situations, where a segment might occur 3 or more times.
     */
    if (boundarySegs.contains(seg)) {
      boundarySegs.remove(seg);
    }
    else {
      boundarySegs.add(seg);
    }
  }

  private static LineSegment createSegment(CoordinateSequence seq, int i) {
    LineSegment seg = new LineSegment(seq.getCoordinate(i), seq.getCoordinate(i + 1));
    seg.normalize();
    return seg;
  }

  @Override
  public boolean isDone() {
    return false;
  }

  @Override
  public boolean isGeometryChanged() {
    return false;
  }

}