com.atolcd.pentaho.di.trans.steps.gisgeoprocessing.GisGeoprocessing.java Source code

Java tutorial

Introduction

Here is the source code for com.atolcd.pentaho.di.trans.steps.gisgeoprocessing.GisGeoprocessing.java

Source

package com.atolcd.pentaho.di.trans.steps.gisgeoprocessing;

/*
 * #%L
 * Pentaho Data Integrator GIS Plugin
 * %%
 * Copyright (C) 2015 Atol CD
 * %%
 * This program 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, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

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

import org.apache.commons.lang.ArrayUtils;
import org.opensphere.geometry.algorithm.ConcaveHull;
import org.pentaho.di.core.Const;
import org.pentaho.di.core.exception.KettleException;
import org.pentaho.di.core.row.RowDataUtil;
import org.pentaho.di.core.row.RowMetaInterface;
import org.pentaho.di.trans.Trans;
import org.pentaho.di.trans.TransMeta;
import org.pentaho.di.trans.step.BaseStep;
import org.pentaho.di.trans.step.StepDataInterface;
import org.pentaho.di.trans.step.StepInterface;
import org.pentaho.di.trans.step.StepMeta;
import org.pentaho.di.trans.step.StepMetaInterface;

import com.atolcd.pentaho.di.core.row.value.ValueMetaGeometry;
import com.atolcd.pentaho.di.gis.utils.GeometryUtils;
import com.vividsolutions.jts.densify.Densifier;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateArrays;
import com.vividsolutions.jts.geom.CoordinateList;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Lineal;
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.geom.Polygonal;
import com.vividsolutions.jts.geom.Puntal;
import com.vividsolutions.jts.geom.util.LinearComponentExtracter;
import com.vividsolutions.jts.linearref.LengthIndexedLine;
import com.vividsolutions.jts.operation.buffer.BufferOp;
import com.vividsolutions.jts.operation.buffer.BufferParameters;
import com.vividsolutions.jts.operation.linemerge.LineMerger;
import com.vividsolutions.jts.operation.overlay.snap.GeometrySnapper;
import com.vividsolutions.jts.operation.polygonize.Polygonizer;
import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier;

public class GisGeoprocessing extends BaseStep implements StepInterface {

    private GisGeoprocessingData data;
    private GisGeoprocessingMeta meta;

    private static GeometryFactory geometryFactory = new GeometryFactory();

    private String operator;

    private Integer firstGeometryFieldIndex;
    private Integer secondGeometryFieldIndex;

    private Double distanceValue;
    private Integer distanceFieldIndex;

    private Integer bufferSegmentsCount;
    private String bufferCapStyle;
    private String bufferJoinStyle;
    private Boolean bufferSingleSide;

    private String returnType;
    private String extractType;
    private Integer outputFieldIndex;

    private boolean withSecondGeometry;
    private boolean withDistance;
    private boolean withExtractType;
    private boolean withExplode;

    private Geometry getGeoprocessingResult(Object[] row) throws KettleException {

        Geometry firstGeometry = new ValueMetaGeometry().getGeometry(row[firstGeometryFieldIndex]);

        Double distance = null;
        if (withDistance) {

            if (distanceFieldIndex != null) {

                distance = getInputRowMeta().getNumber(row, distanceFieldIndex);

                if (distance == null) {
                    throw new KettleException("Distance can not be null");
                }

            } else {
                distance = distanceValue;
            }

        }

        if (withSecondGeometry) {

            Geometry secondGeometry = new ValueMetaGeometry().getGeometry(row[secondGeometryFieldIndex]);
            return getTwoGeometriesGeoprocessing(operator, firstGeometry, secondGeometry, distance);

        } else {

            if (operator.equalsIgnoreCase("EXTENDED_BUFFER")) {

                Geometry outGeometry = null;

                if (!GeometryUtils.isNullOrEmptyGeometry(firstGeometry)) {

                    if (distance != null) {

                        if (bufferSegmentsCount == null) {
                            bufferSegmentsCount = 8;
                        }

                        BufferParameters bufferParams = new BufferParameters();
                        bufferParams.setQuadrantSegments(bufferSegmentsCount);
                        bufferParams.setSingleSided(bufferSingleSide);

                        if (bufferCapStyle.endsWith("FLAT")) {
                            bufferParams.setEndCapStyle(BufferParameters.CAP_FLAT);

                        } else if (bufferCapStyle.endsWith("ROUND")) {
                            bufferParams.setEndCapStyle(BufferParameters.CAP_ROUND);

                        } else if (bufferCapStyle.endsWith("SQUARE")) {
                            bufferParams.setEndCapStyle(BufferParameters.CAP_SQUARE);
                        }

                        if (bufferJoinStyle.endsWith("BEVEL")) {
                            bufferParams.setJoinStyle(BufferParameters.JOIN_BEVEL);

                        } else if (bufferJoinStyle.endsWith("MITRE")) {
                            bufferParams.setJoinStyle(BufferParameters.JOIN_MITRE);

                        } else if (bufferJoinStyle.endsWith("ROUND")) {
                            bufferParams.setJoinStyle(BufferParameters.JOIN_ROUND);
                        }

                        if (!bufferParams.isSingleSided()) {
                            distance = Math.abs(distance);
                        }

                        outGeometry = GeometryUtils.getNonEmptyGeometry(firstGeometry.getSRID(),
                                BufferOp.bufferOp(firstGeometry, distance, bufferParams));

                    } else {

                        throw new KettleException("Distance can not be null");

                    }

                }

                return outGeometry;

            } else {
                return getOneGeometryGeoprocessing(operator, firstGeometry, distance);
            }

        }

    }

    public GisGeoprocessing(StepMeta s, StepDataInterface stepDataInterface, int c, TransMeta t, Trans dis) {
        super(s, stepDataInterface, c, t, dis);
    }

    public boolean processRow(StepMetaInterface smi, StepDataInterface sdi) throws KettleException {
        meta = (GisGeoprocessingMeta) smi;
        data = (GisGeoprocessingData) sdi;

        Geometry geoprocessingResult;

        Object[] r = getRow();

        if (r == null) {

            setOutputDone();
            return false;

        }

        if (first) {

            first = false;
            data.outputRowMeta = (RowMetaInterface) getInputRowMeta().clone();
            meta.getFields(data.outputRowMeta, getStepname(), null, null, this);

            operator = meta.getOperator();
            returnType = meta.getReturnType();

            // Rcupration des indexes des colonnes contenant les gomrtries
            // d'entre
            firstGeometryFieldIndex = getInputRowMeta().indexOfValue(meta.getFirstGeometryFieldName());

            // Besoin de 2 gomtries
            if (ArrayUtils.contains(meta.getTwoGeometriesOperators(), operator)) {
                withSecondGeometry = true;
                secondGeometryFieldIndex = getInputRowMeta().indexOfValue(meta.getSecondGeometryFieldName());
            } else {
                withSecondGeometry = false;
            }

            // Posibilits d'extraction de gomtries
            if (ArrayUtils.contains(meta.getWithExtractTypeOperators(), operator)) {

                withExtractType = true;
                extractType = meta.getExtractType();

            } else {
                withExtractType = false;
            }

            // Besoin de distance
            if (ArrayUtils.contains(meta.getWithDistanceOperators(), operator)) {

                withDistance = true;
                if (meta.isDynamicDistance()) {
                    distanceFieldIndex = getInputRowMeta().indexOfValue(meta.getDistanceFieldName());
                } else {

                    try {
                        distanceValue = Double.parseDouble(environmentSubstitute(meta.getDistanceValue()));
                    } catch (Exception e) {
                        throw new KettleException("Distance is not valid");
                    }
                }

            } else {
                withDistance = false;
            }

            // Rcupration de l'index de la colonne contenant le rsultat
            outputFieldIndex = data.outputRowMeta.indexOfValue(meta.getOutputFieldName());

            // Exploser les gomtries;
            if (operator.equalsIgnoreCase("EXPLODE")) {
                withExplode = true;
            } else {
                withExplode = false;
            }

            // Si buffer tendu
            if (operator.equalsIgnoreCase("EXTENDED_BUFFER")) {
                bufferSegmentsCount = meta.getBufferSegmentsCount();
                bufferCapStyle = meta.getBufferCapStyle();
                bufferJoinStyle = meta.getBufferJoinStyle();
                bufferSingleSide = meta.getBufferSingleSide();

            } else {
                bufferSegmentsCount = null;
                bufferCapStyle = null;
                bufferJoinStyle = null;
                bufferSingleSide = null;
            }

            logBasic("Initialized successfully");

        }

        Object[] currenRow = RowDataUtil.resizeArray(r, r.length + 1);
        geoprocessingResult = getGeoprocessingResult(r);
        Geometry[] resultGeometries = null;

        if (withExplode && geoprocessingResult != null) {

            int numGeometries = geoprocessingResult.getNumGeometries();
            resultGeometries = new Geometry[numGeometries];
            for (int i = 0; i < numGeometries; i++) {
                Geometry subGeometry = GeometryUtils.getNonEmptyGeometry(geoprocessingResult.getSRID(),
                        geoprocessingResult.getGeometryN(i));
                if (subGeometry instanceof LinearRing) {
                    int srid = subGeometry.getSRID();
                    subGeometry = geometryFactory.createLineString(subGeometry.getCoordinates());
                    subGeometry.setSRID(srid);
                }
                resultGeometries[i] = subGeometry;
            }

        } else {
            resultGeometries = new Geometry[] { geoprocessingResult };
        }

        for (Geometry resultGeometry : resultGeometries) {

            Object[] outputRow = currenRow.clone();

            if (withExtractType && !GeometryUtils.isNullOrEmptyGeometry(resultGeometry)) {

                if (extractType.equalsIgnoreCase("PUNTAL_ONLY")) {

                    resultGeometry = GeometryUtils.getGeometryFromType(resultGeometry, Puntal.class);

                } else if (extractType.equalsIgnoreCase("LINEAL_ONLY")) {

                    resultGeometry = GeometryUtils.getGeometryFromType(resultGeometry, Lineal.class);

                } else if (extractType.equalsIgnoreCase("POLYGONAL_ONLY")) {

                    resultGeometry = GeometryUtils.getGeometryFromType(resultGeometry, Polygonal.class);
                }

            }

            if (returnType.equalsIgnoreCase("ALL")) {

                outputRow[outputFieldIndex] = resultGeometry;
                putRow(data.outputRowMeta, outputRow);

            } else if (returnType.equalsIgnoreCase("NOT_NULL")) {

                if (!GeometryUtils.isNullOrEmptyGeometry(resultGeometry)) {

                    outputRow[outputFieldIndex] = resultGeometry;
                    putRow(data.outputRowMeta, outputRow);

                }
            }
        }

        incrementLinesInput();
        if (checkFeedback(getLinesRead())) {
            logBasic("Linenr " + getLinesRead());
        }

        return true;
    }

    public boolean init(StepMetaInterface smi, StepDataInterface sdi) {
        meta = (GisGeoprocessingMeta) smi;
        data = (GisGeoprocessingData) sdi;
        return super.init(smi, sdi);
    }

    public void dispose(StepMetaInterface smi, StepDataInterface sdi) {
        meta = (GisGeoprocessingMeta) smi;
        data = (GisGeoprocessingData) sdi;

        super.dispose(smi, sdi);
    }

    public void run() {
        logBasic("Starting to run...");
        try {
            while (processRow(meta, data) && !isStopped())
                ;
        } catch (Exception e) {
            logError("Unexpected error : " + e.toString());
            logError(Const.getStackTracker(e));
            setErrors(1);
            stopAll();
        } finally {
            dispose(meta, data);
            logBasic("Finished, processing " + getLinesRead() + " rows");
            markStop();
        }
    }

    private Geometry getOneGeometryGeoprocessing(String operator, Geometry inGeometry, Double distance)
            throws KettleException {

        Geometry outGeometry = null;

        if (!GeometryUtils.isNullOrEmptyGeometry(inGeometry)) {

            if (operator.equalsIgnoreCase("BOUNDARY")) {

                outGeometry = inGeometry.getBoundary();
                if (outGeometry instanceof LinearRing) {
                    outGeometry = geometryFactory.createLineString(outGeometry.getCoordinates());
                }

            } else if (operator.equalsIgnoreCase("INTERIOR_POINT")) {

                outGeometry = inGeometry.getInteriorPoint();

            } else if (operator.equalsIgnoreCase("CONVEX_HULL")) {

                outGeometry = inGeometry.convexHull();

            } else if (operator.equalsIgnoreCase("CONCAVE_HULL")) {

                if (distance != null) {

                    distance = Math.abs(distance);
                    ConcaveHull concaveHull = new ConcaveHull(inGeometry, distance);
                    outGeometry = concaveHull.getConcaveHull();

                } else {
                    throw new KettleException("Threshold can not be null");
                }

            } else if (operator.equalsIgnoreCase("BUFFER")) {

                if (distance != null) {

                    distance = Math.abs(distance);
                    outGeometry = inGeometry.buffer(distance);

                } else {
                    throw new KettleException("Distance can not be null");
                }

            } else if (operator.equalsIgnoreCase("EXTENTED_BUFFER")) {

                // Non gr ici

            } else if (operator.equalsIgnoreCase("EXPLODE")) {

                // Non gr ici mais avec paramtre withExplode"
                outGeometry = inGeometry;

            } else if (operator.equalsIgnoreCase("REVERSE")) {

                outGeometry = inGeometry.reverse();

            } else if (operator.equalsIgnoreCase("DENSIFY")) {

                if (distance != null) {

                    distance = Math.abs(distance);
                    Densifier densifier = new Densifier(inGeometry);
                    densifier.setDistanceTolerance(distance);
                    outGeometry = densifier.getResultGeometry();

                } else {
                    throw new KettleException("Distance can not be null");
                }

            } else if (operator.equalsIgnoreCase("SIMPLIFY")) {

                if (distance != null) {

                    distance = Math.abs(distance);
                    DouglasPeuckerSimplifier simplifier = new DouglasPeuckerSimplifier(inGeometry);
                    simplifier.setDistanceTolerance(distance);
                    outGeometry = simplifier.getResultGeometry();

                } else {
                    throw new KettleException("Distance can not be null");
                }

            } else if (operator.equalsIgnoreCase("TO_2D_GEOMETRY")) {

                outGeometry = GeometryUtils.get2DGeometry(inGeometry);

            } else if (operator.equalsIgnoreCase("TO_MULTI_GEOMETRY")) {

                outGeometry = GeometryUtils.getMultiGeometry(inGeometry);

            } else if (operator.equalsIgnoreCase("EXTRACT_COORDINATES")) {

                outGeometry = geometryFactory.createMultiPoint(inGeometry.getCoordinates());

            } else if (operator.equalsIgnoreCase("MBR")) {

                outGeometry = inGeometry.getEnvelope();

            } else if (operator.equalsIgnoreCase("CENTROID")) {

                outGeometry = inGeometry.getCentroid();

            }

        }

        return GeometryUtils.getNonEmptyGeometry(inGeometry.getSRID(), outGeometry);

    }

    private Geometry getTwoGeometriesGeoprocessing(String operator, Geometry inGeometryA, Geometry inGeometryB,
            Double distance) throws KettleException {

        Geometry outGeometry = null;

        if (!GeometryUtils.isNullOrEmptyGeometry(inGeometryA)
                && !GeometryUtils.isNullOrEmptyGeometry(inGeometryB)) {

            if (GeometryUtils.getSrid(inGeometryA).compareTo(GeometryUtils.getSrid(inGeometryB)) == 0) {

                if (operator.equalsIgnoreCase("UNION")) {
                    outGeometry = inGeometryA.union(inGeometryB);

                } else if (operator.equalsIgnoreCase("DIFFERENCE")) {
                    outGeometry = inGeometryA.difference(inGeometryB);

                } else if (operator.equalsIgnoreCase("INTERSECTION")) {
                    outGeometry = inGeometryA.intersection(inGeometryB);

                } else if (operator.equalsIgnoreCase("SYM_DIFFERENCE")) {
                    outGeometry = inGeometryA.symDifference(inGeometryB);

                } else if (operator.equalsIgnoreCase("SNAP_TO_GEOMETRY")) {

                    if (distance != null) {

                        distance = Math.abs(distance);
                        outGeometry = snapToGeometry(inGeometryA, inGeometryB, distance);

                    } else {
                        throw new KettleException("Snap distance can not be null");
                    }

                } else if (operator.equalsIgnoreCase("SIMPLIFY_POLYGON")) {

                    if (!(inGeometryA instanceof Polygonal)) {
                        throw new KettleException("The first geometry is not a POLYGON or a MULTIPOLYGON");
                    }

                    if (!(inGeometryA instanceof Polygonal)) {
                        throw new KettleException("The first geometry is not a POLYGON or a MULTIPOLYGON");
                    }

                    if (distance != null) {

                        distance = Math.abs(distance);
                        outGeometry = simplifyPolygon(inGeometryA, inGeometryB, distance);

                    } else {
                        throw new KettleException("Distance can not be null");
                    }

                } else {
                    throw new IllegalArgumentException("Function \"" + operator + "\" is not allowed");
                }

            } else {
                throw new KettleException("Unauthorized mixed srids : " + GeometryUtils.getSrid(inGeometryA)
                        + " with " + GeometryUtils.getSrid(inGeometryB));
            }

        } else {

            // Simplification de polygones sans voisins
            if (!GeometryUtils.isNullOrEmptyGeometry(inGeometryA)
                    && GeometryUtils.isNullOrEmptyGeometry(inGeometryB)) {

                if (operator.equalsIgnoreCase("SIMPLIFY_POLYGON")) {

                    outGeometry = getOneGeometryGeoprocessing("SIMPLIFY", inGeometryA, distance);
                }
            }
        }

        return GeometryUtils.getNonEmptyGeometry(inGeometryA.getSRID(), outGeometry);

    }

    @SuppressWarnings("unchecked")
    private Geometry simplifyPolygon(Geometry inGeometryA, Geometry inGeometryB, double distance) {

        distance = Math.abs(distance);

        List<Geometry> allBoundaries = new ArrayList<Geometry>();
        Geometry inputBoundary = inGeometryA.getBoundary();

        allBoundaries.add(inputBoundary);
        for (int i = 0; i < inGeometryB.getNumGeometries(); i++) {

            allBoundaries.add(inGeometryB.getGeometryN(i).getBoundary());
        }

        LineMerger lineMerger = new LineMerger();
        lineMerger.add(geometryFactory.buildGeometry(allBoundaries).union());
        Polygonizer polygonizer = new Polygonizer();
        for (LineString lineString : (List<LineString>) lineMerger.getMergedLineStrings()) {
            if (lineString.coveredBy(inputBoundary)) {

                DouglasPeuckerSimplifier simplifier = new DouglasPeuckerSimplifier(lineString);
                simplifier.setDistanceTolerance(distance);
                polygonizer.add(simplifier.getResultGeometry());
            }
        }

        return geometryFactory.buildGeometry(polygonizer.getPolygons());

    }

    @SuppressWarnings("static-access")
    private Geometry snapToGeometry(Geometry inGeometryA, Geometry inGeometryB, double distance) {

        Geometry outGeometry = null;

        // Si geometrie B est de type ponctuel -> accrochage des sommets aux
        // coordonnes
        if (inGeometryB instanceof Puntal) {

            GeometrySnapper geometrySnapper = new GeometrySnapper(inGeometryA);
            // Si point bug : ajout d'autres coordonne non accrochables
            if (inGeometryB instanceof Point) {
                CoordinateList coordinateList = new CoordinateList();
                coordinateList.add(inGeometryB.getCoordinate(), true);
                Envelope enveloppe = inGeometryA.getEnvelopeInternal();
                enveloppe.expandBy(distance * 2);
                coordinateList.add(geometryFactory.toGeometry(enveloppe).getCoordinates(), true);
                inGeometryB = geometryFactory.createMultiPoint(coordinateList.toCoordinateArray()).union();
            }

            outGeometry = geometrySnapper.snapTo(inGeometryB, distance);

            // Sinon, accrochage des sommets aux lments linraires
        } else {

            Geometry linearGeometry = geometryFactory
                    .buildGeometry(LinearComponentExtracter.getLines(inGeometryB, true));

            // Ralise une accroche sur les coordonnes
            // GeometrySnapper geometrySnapper = new
            // GeometrySnapper(inGeometryA);
            // outGeometry =
            // GeometryUtils.getNonEmptyGeometry(inGeometryA.getSRID(),geometrySnapper.snapTo(inGeometryB,
            // distance));
            outGeometry = inGeometryA;

            // Puis sur les lments linaires
            if (outGeometry instanceof Point) {

                outGeometry = geometryFactory
                        .createPoint(snapToLineString(outGeometry, linearGeometry, distance)[0]);

            } else if (outGeometry instanceof MultiPoint) {

                outGeometry = geometryFactory
                        .createMultiPoint(snapToLineString(outGeometry, linearGeometry, distance));

            } else if (outGeometry instanceof LineString) {

                outGeometry = geometryFactory
                        .createLineString(snapToLineString(outGeometry, linearGeometry, distance));

            } else if (outGeometry instanceof MultiLineString) {

                List<LineString> lineStrings = new ArrayList<LineString>();
                for (int iGeom = 0; iGeom < outGeometry.getNumGeometries(); iGeom++) {

                    lineStrings.add(geometryFactory.createLineString(
                            snapToLineString(outGeometry.getGeometryN(iGeom), linearGeometry, distance)));

                }

                outGeometry = geometryFactory.createMultiLineString(geometryFactory.toLineStringArray(lineStrings));

            } else if (outGeometry instanceof Polygon) {

                Polygon polygon = (Polygon) outGeometry;
                LinearRing exteriorRing = geometryFactory
                        .createLinearRing(snapToLineString(polygon.getExteriorRing(), linearGeometry, distance));
                List<LinearRing> interiorRings = new ArrayList<LinearRing>();

                for (int iRing = 0; iRing < polygon.getNumInteriorRing(); iRing++) {

                    interiorRings.add(geometryFactory.createLinearRing(
                            snapToLineString(polygon.getInteriorRingN(iRing), linearGeometry, distance)));

                }

                outGeometry = geometryFactory.createPolygon(exteriorRing,
                        geometryFactory.toLinearRingArray(interiorRings));

            } else if (outGeometry instanceof MultiPolygon) {

                MultiPolygon multiPolygon = (MultiPolygon) outGeometry;
                List<Polygon> polygons = new ArrayList<Polygon>();

                for (int iGeom = 0; iGeom < outGeometry.getNumGeometries(); iGeom++) {

                    Polygon polygon = (Polygon) multiPolygon.getGeometryN(iGeom);
                    LinearRing exteriorRing = geometryFactory.createLinearRing(
                            snapToLineString(polygon.getExteriorRing(), linearGeometry, distance));
                    List<LinearRing> interiorRings = new ArrayList<LinearRing>();

                    for (int iRing = 0; iRing < polygon.getNumInteriorRing(); iRing++) {

                        interiorRings.add(geometryFactory.createLinearRing(
                                snapToLineString(polygon.getInteriorRingN(iRing), linearGeometry, distance)));

                    }

                    polygons.add(geometryFactory.createPolygon(exteriorRing,
                            geometryFactory.toLinearRingArray(interiorRings)));

                }

                outGeometry = geometryFactory.createMultiPolygon(geometryFactory.toPolygonArray(polygons));

            } else {
                throw new IllegalArgumentException("Unauthorized geometry type");
            }

        }

        return GeometryUtils.getNonEmptyGeometry(inGeometryA.getSRID(), outGeometry);

    }

    @SuppressWarnings("unchecked")
    private Coordinate[] snapToLineString(Geometry geometry, Geometry linearGeometry, double distance) {

        MultiPoint linearCoordinates = geometryFactory.createMultiPoint(linearGeometry.getCoordinates());

        if (geometry instanceof LineString || geometry instanceof LinearRing) {

            LineMerger merger = new LineMerger();
            merger.add(geometryFactory
                    .createMultiLineString(splitLineStringAtCoordinates(linearCoordinates.getCoordinates(),
                            geometryFactory.createLineString(geometry.getCoordinates()), distance)));
            geometry = (LineString) merger.getMergedLineStrings().iterator().next();
        }

        CoordinateList newCoordinates = new CoordinateList();
        LengthIndexedLine linearIndexedLine = new LengthIndexedLine(linearGeometry);

        for (Coordinate coordinate : geometry.getCoordinates()) {

            Point inputPoint = geometryFactory.createPoint(coordinate);

            // Si coordonne n'est pas dj prsente sur la gomtrie 
            // accrocher
            if (!(CoordinateArrays.indexOf(inputPoint.getCoordinate(), linearGeometry.getCoordinates()) > 0)) {

                // double minDistanceCoords =
                // inputPoint.distance(linearCoordinates);
                double minDistanceCoords = inputPoint.distance(linearCoordinates);

                // Tentative de snapping si distance < au seuil
                if (minDistanceCoords <= distance) {
                    GeometrySnapper geometrySnapper = new GeometrySnapper(inputPoint);
                    inputPoint = (Point) geometrySnapper.snapTo(linearGeometry, minDistanceCoords + 1);
                }

                // Si coordonne snappe n'est pas dj prsente sur la
                // gomtrie  accrocher
                if (!(CoordinateArrays.indexOf(inputPoint.getCoordinate(), linearGeometry.getCoordinates()) > 0)) {

                    // Tentative d'accrochage au segment si distance < au seuil
                    if (inputPoint.distance(linearGeometry) <= distance) {

                        newCoordinates.add(linearIndexedLine
                                .extractPoint(linearIndexedLine.project(inputPoint.getCoordinate())));

                    } else {

                        newCoordinates.add(inputPoint.getCoordinate(), true);
                    }

                } else {

                    newCoordinates.add(inputPoint.getCoordinate());
                }

            } else {
                newCoordinates.add(coordinate);
            }

        }

        return CoordinateArrays.removeRepeatedPoints(newCoordinates.toCoordinateArray());

    }

    private static LineString[] splitLineStringAtCoordinates(Coordinate[] splitCoordinates,
            LineString inputLineString, double distance) {

        List<LineString> lineStrings = new ArrayList<LineString>();
        LengthIndexedLine lengthIndexedLine = new LengthIndexedLine(inputLineString);

        TreeSet<Double> indexesTree = new TreeSet<Double>();
        indexesTree.add(lengthIndexedLine.getStartIndex());
        indexesTree.add(lengthIndexedLine.getEndIndex());

        for (Coordinate splitCoordinate : splitCoordinates) {

            if (geometryFactory.createPoint(splitCoordinate).distance(inputLineString) <= distance) {

                indexesTree.add(lengthIndexedLine.project(splitCoordinate));

            }
        }

        Double[] indexes = indexesTree.toArray(new Double[indexesTree.size()]);
        for (int i = 0; i < indexes.length - 1; i++) {

            LineString splitedLineString = (LineString) lengthIndexedLine.extractLine(indexes[i], indexes[i + 1]);
            if (splitedLineString != null && !splitedLineString.isEmpty()) {
                lineStrings.add(splitedLineString);
            }

        }

        return lineStrings.toArray(new LineString[lineStrings.size()]);
    }

}