/**
* H2GIS is a library that brings spatial support to the H2 Database Engine
* <http://www.h2database.com>. H2GIS is developed by CNRS
* <http://www.cnrs.fr/>.
*
* This code is part of the H2GIS project. H2GIS is free software;
* you can redistribute it and/or modify it under the terms of the GNU
* Lesser General Public License as published by the Free Software Foundation;
* version 3.0 of the License.
*
* H2GIS 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 Lesser General Public License
* for more details <http://www.gnu.org/licenses/>.
*
*
* For more information, please consult: <http://www.h2gis.org/>
* or contact directly: info_at_h2gis.org
*/
package org.h2gis.functions.spatial.split;
import com.vividsolutions.jts.algorithm.RobustLineIntersector;
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.Polygon;
import com.vividsolutions.jts.noding.IntersectionAdder;
import com.vividsolutions.jts.noding.MCIndexNoder;
import com.vividsolutions.jts.noding.NodedSegmentString;
import com.vividsolutions.jts.noding.SegmentString;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import org.h2gis.api.DeterministicScalarFunction;
/**
* LineIntersector is used to split an input geometry (LineString or MultiLineString) by
* a set of geometries.
* @author Erwan Bocher
*/
public class ST_LineIntersector extends DeterministicScalarFunction{
private static final RobustLineIntersector ROBUST_INTERSECTOR = new RobustLineIntersector();
public ST_LineIntersector() {
addProperty(PROP_REMARKS, "Split an input geometry by another geometry. \n"
+ "This function uses a more robust intersection algorithm than the ST_Split function.\n"
+ "It computes the intersections between the line segments of the input geometries."
+ "A collection of LineString is returned.");
}
@Override
public String getJavaStaticMethod() {
return "lineIntersector";
}
/**
* Split a lineal geometry by a another geometry
* @param inputLines
* @param clipper
* @return
*/
public static Geometry lineIntersector(Geometry inputLines, Geometry clipper) throws IllegalArgumentException {
if(inputLines == null||clipper == null){
return null;
}
if(inputLines.getDimension()==1){
MCIndexNoder mCIndexNoder = new MCIndexNoder();
mCIndexNoder.setSegmentIntersector(new IntersectionAdder(ROBUST_INTERSECTOR));
mCIndexNoder.computeNodes(getSegments(inputLines, clipper));
Collection nodedSubstring = mCIndexNoder.getNodedSubstrings();
GeometryFactory gf = inputLines.getFactory();
ArrayList<LineString> linestrings = new ArrayList<LineString>(nodedSubstring.size());
for (Iterator it = nodedSubstring.iterator(); it.hasNext();) {
SegmentString segment = (SegmentString) it.next();
//We keep only the segments of the input geometry
if((Integer)segment.getData()==0){
Coordinate[] cc = segment.getCoordinates();
cc = CoordinateArrays.atLeastNCoordinatesOrNothing(2, cc);
if (cc.length > 1) {
linestrings.add(gf.createLineString(cc));
}
}
}
if (linestrings.isEmpty()) {
return inputLines;
} else {
return gf.createMultiLineString(linestrings.toArray(new LineString[linestrings.size()]));
}}
throw new IllegalArgumentException("Split a " + inputLines.getGeometryType() + " by a " + clipper.getGeometryType() + " is not supported.");
}
/***
* Convert the input geometries as a list of segments and mark them with a flag
* to identify input and output geometries.
* @param inputLines
* @param clipper
* @return
*/
public static ArrayList<SegmentString> getSegments(Geometry inputLines, Geometry clipper) {
ArrayList<SegmentString> segments = new ArrayList<SegmentString>();
addGeometryToSegments(inputLines, 0, segments);
addGeometryToSegments(clipper, 1, segments);
return segments;
}
/**
* Convert the a geometry as a list of segments and mark it with a flag
* @param geometry
* @param flag
* @param segments
*/
public static void addGeometryToSegments(Geometry geometry, int flag, ArrayList<SegmentString> segments) {
for (int i = 0; i < geometry.getNumGeometries(); i++) {
Geometry component = geometry.getGeometryN(i);
if (component instanceof Polygon) {
add((Polygon) component, flag, segments);
} else if (component instanceof LineString) {
add((LineString) component, flag, segments);
}
}
}
/**
* Convert a polygon as a list of segments and mark it with a flag
* @param poly
* @param flag
* @param segments
*/
private static void add(Polygon poly, int flag, ArrayList<SegmentString> segments) {
add(poly.getExteriorRing(), flag, segments);
for (int j = 0; j < poly.getNumInteriorRing(); j++) {
add(poly.getInteriorRingN(j), flag, segments);
}
}
/**
* Convert a linestring as a list of segments and mark it with a flag
* @param line
* @param flag
* @param segments
*/
private static void add(LineString line, int flag, ArrayList<SegmentString> segments) {
SegmentString ss = new NodedSegmentString(line.getCoordinates(),
flag);
segments.add(ss);
}
}