SegmentStringNodingTest.java

/*
 * Copyright (c) 2016 Vivid Solutions.
 *
 * 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.noding.snapround;

import java.util.ArrayList;
import java.util.List;

import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.noding.NodedSegmentString;
import org.locationtech.jts.noding.SegmentString;

import junit.framework.TestCase;
import junit.textui.TestRunner;


/**
 * Test for correctly created Noded Segment Strings
 * under an extreme usage of SnapRounding.
 * This test reveals a bug in SegmentNodeList.createSplitEdge()
 * which can create 1-point Segment Strings
 * if the input is incorrectly noded due to robustness issues.
 * It also reveals a limitation in SegmentNode sorting which
 * can cause nodes to sort wrongly if their coordinates are very close 
 * and they are relatively far off the line segment containing them.
 * This is actually outside of the operating regime of the SegmentNode comparison,
 * but in there is a simple fix which handles some cases like these.
 * 
 * See https://github.com/locationtech/jts/pull/395
 *
 * @version 1.17
 */
public class SegmentStringNodingTest  extends TestCase {

  WKTReader rdr = new WKTReader();

  public static void main(String args[]) {
    TestRunner.run(SegmentStringNodingTest.class);
  }

  public SegmentStringNodingTest(String name) { super(name); }
  
  public void testThinTriangle() throws Exception {
    String wkt = "LINESTRING ( 55121.54481117887 42694.49730855581, 55121.54481117887 42694.4973085558, 55121.458748617406 42694.419143944244, 55121.54481117887 42694.49730855581 )";
    PrecisionModel pm = new PrecisionModel(1.1131949079327356E11);
    checkNodedStrings(wkt, pm);
}

  public void testSegmentLength1Failure() throws Exception {
    String wkt = "LINESTRING ( -1677607.6366504875 -588231.47100446, -1674050.1010869485 -587435.2186255794, -1670493.6527468169 -586636.7948791061, -1424286.3681743187 -525586.1397894835, -1670493.6527468169 -586636.7948791061, -1674050.1010869485 -587435.2186255795, -1677607.6366504875 -588231.47100446)";
    PrecisionModel pm = new PrecisionModel(1.11E10);
    checkNodedStrings(wkt, pm);
  }
  
  private void checkNodedStrings(String wkt, PrecisionModel pm) throws ParseException {
    Geometry g = new WKTReader().read(wkt);
    List<NodedSegmentString> strings = new ArrayList<>();
    strings.add(new NodedSegmentString(g.getCoordinates(), null));
    new SnapRoundingNoder(pm).computeNodes(strings);
    
    @SuppressWarnings("unchecked")
    List<NodedSegmentString> noded = NodedSegmentString.getNodedSubstrings(strings);
    for (NodedSegmentString s : noded) {
      assertTrue("Found a 1-point segmentstring", s.size() >= 2);
      assertTrue("Found a collapsed edge", ! isCollapsed(s) );
    }
  }

  /**
   * Test if the segmentString is a collapsed edge 
   * of the form ABA.
   * These should not be returned by noding. 
   *
   * @param s a segmentString
   * @return true if the segmentString is collapsed
   */
  private boolean isCollapsed(SegmentString s) {
    if (s.size() != 3) return false;
    boolean isEndsEqual = s.getCoordinate(0).equals2D(s.getCoordinate(2));
    boolean isMiddleDifferent = ! s.getCoordinate(0).equals2D(s.getCoordinate(1));
    boolean isCollapsed = isEndsEqual && isMiddleDifferent;
    return isCollapsed;
  }
  

}