org.kalypso.model.wspm.core.profil.util.ProfileUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.model.wspm.core.profil.util.ProfileUtil.java

Source

/*----------------    FILE HEADER KALYPSO ------------------------------------------
 *
 *  This file is part of kalypso.
 *  Copyright (C) 2004 by:
 *
 *  Technical University Hamburg-Harburg (TUHH)
 *  Institute of River and coastal engineering
 *  Denickestrae 22
 *  21073 Hamburg, Germany
 *  http://www.tuhh.de/wb
 *
 *  and
 *
 *  Bjoernsen Consulting Engineers (BCE)
 *  Maria Trost 3
 *  56070 Koblenz, Germany
 *  http://www.bjoernsen.de
 *
 *  This library 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 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *  Contact:
 *
 *  E-Mail:
 *  belger@bjoernsen.de
 *  schlienger@bjoernsen.de
 *  v.doemming@tuhh.de
 *
 *  ---------------------------------------------------------------------------*/
package org.kalypso.model.wspm.core.profil.util;

import java.awt.geom.Point2D;
import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.eclipse.core.runtime.Assert;
import org.kalypso.commons.java.lang.Objects;
import org.kalypso.commons.math.LinearEquation;
import org.kalypso.commons.math.LinearEquation.SameXValuesException;
import org.kalypso.contribs.java.util.Arrays;
import org.kalypso.model.wspm.core.IWspmPointProperties;
import org.kalypso.model.wspm.core.gml.IProfileFeature;
import org.kalypso.model.wspm.core.i18n.Messages;
import org.kalypso.model.wspm.core.profil.IProfile;
import org.kalypso.model.wspm.core.profil.IProfilePointMarker;
import org.kalypso.model.wspm.core.profil.visitors.ProfileVisitors;
import org.kalypso.model.wspm.core.profil.wrappers.IProfileRecord;
import org.kalypso.observation.result.ComponentUtilities;
import org.kalypso.observation.result.IComponent;
import org.kalypso.observation.result.IRecord;
import org.kalypso.observation.result.TupleResult;
import org.kalypsodeegree.model.geometry.GM_Curve;
import org.kalypsodeegree.model.geometry.GM_Exception;
import org.kalypsodeegree.model.geometry.GM_Position;
import org.kalypsodeegree_impl.model.geometry.GeometryFactory;
import org.kalypsodeegree_impl.model.geometry.JTSAdapter;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.LineString;

/**
 * @author kimwerner
 */
public final class ProfileUtil {
    private ProfileUtil() {
        throw new UnsupportedOperationException("Helper class, do not instantiate"); //$NON-NLS-1$
    }

    /**
     * @return the values of each point for this pointProperty in the correct order
     */
    public static Object[] getValuesFor(final IProfile profil, final IComponent pointProperty) {
        return getValuesFor(profil, pointProperty, false);
    }

    /**
     * @return the values of each point for this pointProperty in the correct order
     */
    public static Object[] getValuesFor(final IProfile profil, final IComponent pointProperty,
            final boolean skipNullValues) {
        final IProfileRecord[] points = profil.getPoints();
        return getValuesFor(points, pointProperty, skipNullValues);
    }

    public static IComponent getFeatureComponent(final String propertyId) {
        return ComponentUtilities.getFeatureComponent(propertyId);
    }

    /**
     * Converts a double valued station into a BigDecimal with a scale of {@value #STATION_SCALE}.
     *
     * @see #STATION_SCALE
     */
    public static BigDecimal stationToBigDecimal(final double station) {
        return new BigDecimal(station).setScale(IProfileFeature.STATION_SCALE, RoundingMode.HALF_UP);
    }

    /**
     * @return the DoubleValues of each point for this pointProperty in the correct order
     */
    public static Object[] getValuesFor(final IProfileRecord[] points, final IComponent pointProperty,
            final boolean skipNullValues) {
        return getValuesFor(points, pointProperty.getId(), skipNullValues);
    }

    public static Object[] getValuesFor(final IRecord[] points, final String component,
            final boolean skipNullValues) {
        if (points == null || points.length < 1)
            return new Object[0];

        final TupleResult owner = points[0].getOwner();
        final int iProp = owner.indexOfComponent(component);
        if (iProp < 0)
            throw new IllegalArgumentException(String.format("Unknown component: %s", component)); //$NON-NLS-1$

        return getValuesFor(points, iProp, skipNullValues);
    }

    public static Object[] getValuesFor(final IRecord[] points, final int componentIndex,
            final boolean skipNullValues) {
        final Collection<Object> values = new ArrayList<>(points.length);

        for (final IRecord point : points) {
            final Object value = point.getValue(componentIndex);
            if (value != null || !skipNullValues)
                values.add(value);
        }

        return values.toArray(new Object[values.size()]);
    }

    public static int getNextNonNull(final IRecord[] points, final int start, final int componentIndex) {
        for (int i = start + 1; i < points.length; i++) {

            if (points[i] != null && points[i].getValue(componentIndex) != null)
                return i;
        }
        return -1;
    }

    public static double getDoubleValueFor(final IComponent component, final IRecord point) {
        if (component == null)
            return Double.NaN;
        return getDoubleValueFor(component.getId(), point);
    }

    public static double getDoubleValueFor(final String componentID, final IRecord point) {
        final int iComponent = point == null ? -1 : point.indexOfComponent(componentID);
        if (iComponent == -1)
            return Double.NaN;

        return getDoubleValueFor(iComponent, point);
    }

    public static double getDoubleValueFor(final int componentIndex, final IRecord point) {
        try {
            final Object oValue = point.getValue(componentIndex);
            if (oValue instanceof Number)
                return ((Number) oValue).doubleValue();
            return Double.NaN;
        } catch (final IndexOutOfBoundsException e) {
            e.printStackTrace();
            return Double.NaN;
        }
    }

    /**
     * Returns all points between the given markers (closed interval).<br/>
     * If the marker does not exist (or not enough markers of that kind), the whole profile is returned.
     */
    public static List<IRecord> getInnerPoints(final IProfile profil, final IComponent markerTyp) {
        final IProfilePointMarker[] markers = profil.getPointMarkerFor(markerTyp);
        final IProfileRecord[] points = profil.getPoints();

        // REMARK: check both for length > 1, a single marker does not count
        final int leftPos = markers.length > 1 ? ArrayUtils.indexOf(points, markers[0].getPoint()) : 0;
        final int rightPos = markers.length > 1
                ? ArrayUtils.indexOf(points, markers[markers.length - 1].getPoint()) + 1
                : points.length - 1;

        return leftPos < rightPos ? profil.getResult().subList(leftPos, rightPos) : null;
    }

    /**
     * return true if all selected properties are equal
     */
    public static boolean comparePoints(final IComponent[] properties, final IRecord point1, final IRecord point2) {
        for (final IComponent property : properties) {
            final Double x1 = getDoubleValueFor(property.getId(), point1);
            final Double x2 = getDoubleValueFor(property.getId(), point2);

            if (x1.isNaN() || x2.isNaN()) {
                final int index = point1.getOwner().indexOfComponent(property);
                final Object o1 = point1.getValue(index);
                final Object o2 = point2.getValue(index);
                if (o1 == null || o2 == null || o1.equals(o2)) {
                    continue;
                }
            }

            if (Math.abs(x1 - x2) > property.getPrecision())
                return false;
        }
        return true;
    }

    public static boolean compareValues(final Double x1, final Double x2, final double precision) {
        return Math.abs(x1 - x2) <= precision;
    }

    public static IRecord splitSegment(final IProfile profile, final IProfileRecord startPoint,
            final IProfileRecord endPoint) {
        if (startPoint == null || endPoint == null)
            return null;

        final IProfileRecord point = profile.createProfilPoint();
        final IComponent[] properties = profile.getPointProperties();

        for (final IComponent property : properties) {
            final int index = profile.indexOfProperty(property);
            if (IWspmPointProperties.POINT_PROPERTY_BREITE.equals(property.getId())) {
                final Double b1 = startPoint.getBreite();
                final Double l = endPoint.getBreite() - b1;
                point.setValue(index, b1 + l / 2.0);
            } else if (IWspmPointProperties.POINT_PROPERTY_HOEHE.equals(property.getId())) {
                final Double h1 = (Double) startPoint.getValue(index);
                final Double z = (Double) endPoint.getValue(index) - h1;
                point.setValue(index, h1 + z / 2.0);
            } else if (profile.isPointMarker(property.getId())) {
                point.setValue(index, startPoint.getValue(index));
            }
        }

        return point;
    }

    /**
     * findet einen Punkt in einem Profil 1.hole Punkt[index] und vergleiche Punkt.breite mit breite -> 2.suche Punkt bei
     * breite mit einer Toleranz von delta 3.kein Punkt gefunden -> (return null)
     */
    public static IRecord findPoint(final IProfile profil, final int index, final double breite,
            final double delta) {
        final IProfileRecord[] points = profil.getPoints();
        final IProfileRecord point = index >= points.length || index < 0 ? null : points[index];
        if (Objects.isNull(point))
            return findPoint(profil, breite, delta);

        if (point.getBreite() == breite)
            return point;

        return findPoint(profil, breite, delta);

    }

    public static IProfileRecord[] getSegment(final IProfile profile, final double breite) {
        final IProfileRecord[] points = profile.getPoints();
        final IProfileRecord[] segment = new IProfileRecord[] { null, null };
        for (int i = 0; i < points.length - 1; i++) {
            final double segmentStartWidth = points[i].getBreite();
            final double segmentEndWidth = points[i + 1].getBreite();
            if (segmentStartWidth <= breite & segmentEndWidth >= breite) {
                segment[0] = points[i];
                segment[1] = points[i + 1];
            }
        }

        return segment;
    }

    public static Integer[] findNearestPointIndices(final IProfile profil, final double[] breite) {
        final Integer[] result = new Integer[breite.length];

        final IProfileRecord[] points = profil.getPoints();
        if (points.length == 0)
            return result;

        final double[] bestDist = new double[result.length];
        for (int i = 0; i < bestDist.length; i++) {
            bestDist[i] = Double.MAX_VALUE;
        }

        for (int i = 0; i < points.length; i++) {
            final IProfileRecord currentPoint = points[i];
            final Double current = currentPoint.getBreite();
            if (Objects.isNull(current)) {
                continue;
            }

            for (int k = 0; k < bestDist.length; k++) {
                final double dist = Math.abs(current - breite[k]);
                if (dist < bestDist[k]) {
                    bestDist[k] = dist;
                    result[k] = i;
                }
            }
        }

        return result;
    }

    public static IProfileRecord findPoint(final IProfile profil, final double breite, final double delta) {
        final IProfileRecord pkt = ProfileVisitors.findNearestPoint(profil, breite);
        if (pkt == null)
            return null;

        final double xpos = pkt.getBreite();
        return Math.abs(xpos - breite) <= delta ? pkt : null;
    }

    public static IComponent getComponentForID(final IComponent[] components, final String propertyID) {
        if (components == null || components.length < 1)
            return null;
        for (final IComponent component : components) {
            if (component.getId().equals(propertyID))
                return component;
        }
        return null;
    }

    public static Point2D getPoint2D(final IRecord p, final IComponent pointProperty) {
        final Double x = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_BREITE, p);
        final Double y = getDoubleValueFor(pointProperty.getId(), p);
        if (x.isNaN() || y.isNaN())
            throw new IllegalArgumentException(Messages
                    .getString("org.kalypso.model.wspm.core.profil.util.ProfilUtil.7", pointProperty.getName())); //$NON-NLS-1$
        return new Point2D.Double(x, y);
    }

    public static Point2D[] getPoints2D(final IProfile profil, final String propertyID) {
        if (profil.hasPointProperty(propertyID) == null)
            return new Point2D[] {};

        final IRecord[] points = profil.getPoints();
        final Point2D[] points2D = new Point2D[points.length];
        int i = 0;
        for (final IRecord p : points) {
            final Double x = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_BREITE, p);
            final Double y = getDoubleValueFor(propertyID, p);
            if (y.isNaN() || x.isNaN())
                return new Point2D[] {};
            points2D[i++] = new Point2D.Double(x, y);

        }
        return points2D;
    }

    @Deprecated
    // FIXME: what to use instead?!
    // FIXME: warum berhaupt deprecated?
    public static Double getSectionMinValueFor(final IRecord[] section, final IComponent property) {
        if (section.length == 0)
            return Double.NaN;

        final TupleResult owner = section[0].getOwner();
        final int index = owner.indexOfComponent(property);
        if (index < 0)
            return Double.NaN;

        Number minValue = getDoubleValueFor(property, section[0]);
        if (Double.isNaN(minValue.doubleValue()))
            return minValue.doubleValue();
        for (final IRecord rec : section) {
            final Object objVal = rec.getValue(index);
            if (objVal != null && objVal instanceof Number) {
                minValue = Math.min(minValue.doubleValue(), ((Number) objVal).doubleValue());
            }
        }
        return minValue.doubleValue();
    }

    @Deprecated
    public static Double getSectionMaxValueFor(final IRecord[] section, final IComponent property) {
        if (section.length == 0)
            return Double.NaN;
        final TupleResult owner = section[0].getOwner();
        final int index = owner.indexOfComponent(property);
        if (index < 0)
            return Double.NaN;
        Number maxValue = getDoubleValueFor(property, section[0]);
        if (Double.isNaN(maxValue.doubleValue()))
            return maxValue.doubleValue();
        for (final IRecord rec : section) {
            final Object value = rec.getValue(index);
            if (value instanceof Number) {
                maxValue = Math.max(maxValue.doubleValue(), ((Number) value).doubleValue());
            }
        }
        return maxValue.doubleValue();
    }

    /**
     * calculates the area of a given profile.<br>
     * the area is calculated in dependence of the max heigth value. input: IProfil<br>
     * output: area <br>
     */
    public static double calcArea(final IProfile profil) {
        double area = 0;
        double width = 0;
        final double maxZ = calcMaxZ(profil);
        final IRecord[] points = profil.getPoints();
        for (int i = 0; i < points.length - 1; i++) {
            final double z1 = maxZ - getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, points[i]);
            final double z2 = maxZ - getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, points[i + 1]);
            final double width1 = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_BREITE, points[i]);
            final double width2 = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_BREITE, points[i + 1]);
            width = width2 - width1;
            area = area + (z1 + z2) / 2 * width;
        }

        return area;
    }

    /**
     * gives the maximal z value of a coordinate array
     */
    private static double calcMaxZ(final IProfile profile) {
        final IRecord[] points = profile.getPoints();
        if (points.length < 1)
            return -Double.MAX_VALUE;
        Double maxZ = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, points[0]);
        for (int i = 1; i < points.length; i++) {
            maxZ = Math.max(maxZ, getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, points[i]));
        }
        return maxZ;
    }

    /**
     * This function returns the georeferenced points of a profile.
     *
     * @param profile
     *          The input profile.
     */
    public static IProfileRecord[] getGeoreferencedPoints(final IProfile profile) {
        /* List for storing points of the profile, which have a geo reference. */
        final IProfileRecord[] points = profile.getPoints();
        final ArrayList<IProfileRecord> geoReferencedPoints = new ArrayList<>(points.length);

        for (final IProfileRecord point : points) {
            final Coordinate coordinate = point.getCoordinate();
            if (Objects.isNotNull(coordinate))
                geoReferencedPoints.add(point);
        }

        return geoReferencedPoints.toArray(new IProfileRecord[] {});
    }

    public static GM_Curve getLine(final IProfile profile) throws GM_Exception {
        final String srsName = profile.getSrsName();

        final IRecord[] georeferencedPoints = getGeoreferencedPoints(profile);
        final GM_Position[] pos = new GM_Position[georeferencedPoints.length];

        for (int i = 0; i < georeferencedPoints.length; i++) {
            final Double x = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_RECHTSWERT,
                    georeferencedPoints[i]);
            final Double y = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOCHWERT,
                    georeferencedPoints[i]);
            final Double z = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, georeferencedPoints[i]);
            pos[i] = GeometryFactory.createGM_Position(x, y, z);
        }

        return GeometryFactory.createGM_Curve(pos, srsName);
    }

    public static LineString getLineString(final IProfile profile) {
        final String srsName = profile.getSrsName();

        final IProfileRecord[] georeferencedPoints = getGeoreferencedPoints(profile);
        final Coordinate[] pos = new Coordinate[georeferencedPoints.length];

        for (int i = 0; i < georeferencedPoints.length; i++) {
            final IProfileRecord record = georeferencedPoints[i];
            final Double x = record.getRechtswert();
            final Double y = record.getHochwert();
            final Double z = record.getHoehe();
            pos[i] = new Coordinate(x, y, z);
        }

        final LineString line = JTSAdapter.jtsFactory.createLineString(pos);
        line.setSRID(JTSAdapter.toSrid(srsName));

        return line;
    }

    /**
     * Returns the profile line as jts coordinate in ascending order. Profile points that are not georeferenced, are left
     * out.
     */
    public static Coordinate[] getLineCoordinates(final IProfile profile) {
        final IRecord[] georeferencedPoints = getGeoreferencedPoints(profile);
        final Coordinate[] crds = new Coordinate[georeferencedPoints.length];

        for (int i = 0; i < georeferencedPoints.length; i++) {
            final Double x = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_RECHTSWERT,
                    georeferencedPoints[i]);
            final Double y = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOCHWERT,
                    georeferencedPoints[i]);
            final Double z = getDoubleValueFor(IWspmPointProperties.POINT_PROPERTY_HOEHE, georeferencedPoints[i]);
            crds[i] = new Coordinate(x, y, z);
        }

        return crds;
    }

    public static Double[] getDoubleValuesFor(final IProfile profil, final IComponent pointProperty,
            final boolean skipNullValues) {
        final List<Double> myValues = new ArrayList<>();

        final Object[] values = getValuesFor(profil, pointProperty);
        for (final Object object : values) {
            if (object instanceof Double)
                myValues.add((Double) object);
            else if (object instanceof Number)
                myValues.add(((Number) object).doubleValue());
            else if (object instanceof String) {
                // TODO: dubious...
                myValues.add(Double.valueOf(object.toString()));
            } else if (object == null && !skipNullValues)
                myValues.add(null);
        }

        return myValues.toArray(new Double[] {});
    }

    /**
     * Fills missing values of one property by interpolating between neighbors.
     */
    public static void interpolateProperty(final IProfile profil, final int valueCompIndex) {
        final int distanceCompIndex = profil.indexOfProperty(IWspmPointProperties.POINT_PROPERTY_BREITE);

        IRecord lastGood = null;
        final List<IRecord> toInterpolate = new ArrayList<>();

        for (final IRecord point : profil.getPoints()) {
            final Object value = point.getValue(valueCompIndex);

            if (value == null) {
                if (lastGood != null) {
                    toInterpolate.add(point);
                }
            } else {
                if (!toInterpolate.isEmpty()) {
                    final IRecord[] pointsToInterpolate = toInterpolate.toArray(new IRecord[toInterpolate.size()]);
                    doInterpolate(distanceCompIndex, valueCompIndex, lastGood, point, pointsToInterpolate);
                }

                lastGood = point;
                toInterpolate.clear();
            }
        }
    }

    private static void doInterpolate(final int distanceCompIndex, final int valueCompIndex,
            final IRecord prevPoint, final IRecord nextPoint, final IRecord[] toInterpolate) {
        final Double prevDistance = prevPoint == null ? null : (Double) prevPoint.getValue(distanceCompIndex);
        final Double prevValue = prevPoint == null ? null : (Double) prevPoint.getValue(valueCompIndex);
        final Double nextDistance = nextPoint == null ? null : (Double) nextPoint.getValue(distanceCompIndex);
        final Double nextValue = nextPoint == null ? null : (Double) nextPoint.getValue(valueCompIndex);

        if (prevDistance == null || prevValue == null) {
            for (final IRecord point : toInterpolate) {
                point.setValue(valueCompIndex, nextValue);
            }
        } else if (nextDistance == null || nextValue == null) {
            for (final IRecord point : toInterpolate) {
                point.setValue(valueCompIndex, prevValue);
            }
        } else {
            for (final IRecord point : toInterpolate) {
                final Double distance = (Double) point.getValue(distanceCompIndex);
                if (distance != null) {
                    try {
                        final LinearEquation le = new LinearEquation(prevDistance, prevValue, nextDistance,
                                nextValue);
                        final double value = le.computeY(distance);
                        point.setValue(valueCompIndex, value);
                    } catch (final IndexOutOfBoundsException e) {
                        e.printStackTrace();
                    } catch (final SameXValuesException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * Same as {@link #getValuesFor(IRecord[], String)}, but casts the result to the given type.
     */
    public static <T> T[] getValuesFor(final IRecord[] points, final String component, final Class<T> type,
            final boolean skipNullValues) {
        final Object[] values = getValuesFor(points, component, skipNullValues);
        final T[] targetArray = (T[]) Array.newInstance(type, values.length);
        return Arrays.castArray(values, targetArray);
    }

    public static Double[] getValuesFor(final IProfile profil, final String component, final Class<Double> type) {
        return getValuesFor(profil.getPoints(), component, type, false);
    }

    /**
     * Gets all values of the given component and interpolates missing values by the help of the 'BREITE' component.<br>
     * Works only for components with numeric values.
     *
     * @return All values of the profile of the given component. The length of the array is equal to the number of
     *         records.
     */
    public static Double[] getInterpolatedValues(final IProfile profil, final String component) {
        final Double[] widthValues = getValuesFor(profil, IWspmPointProperties.POINT_PROPERTY_BREITE, Double.class);
        final Double[] componentValues = getValuesFor(profil, component, Double.class);

        Assert.isTrue(widthValues.length == componentValues.length);

        int lastValid = -1;
        for (int i = 0; i < widthValues.length; i++) {
            if (componentValues[i] != null) {
                if (lastValid != -1) {
                    try {
                        final Double width1 = widthValues[lastValid];
                        final Double value1 = componentValues[lastValid];
                        final Double width2 = widthValues[i];
                        final Double value2 = componentValues[i];

                        if (width1 != null && width2 != null && value1 != null && value2 != null) {
                            final LinearEquation linearEquation = new LinearEquation(width1, value1, width2,
                                    value2);

                            /* Loop over all invalid values */
                            for (int j = lastValid + 1; j < i - 1; j++) {
                                componentValues[j] = linearEquation.computeY(widthValues[j]);
                            }
                        }
                    } catch (final SameXValuesException e) {
                        e.printStackTrace();
                    }
                }

                lastValid = i;
            }
        }

        return componentValues;
    }

    /**
     * This function thins the profile and removes unnecessary points. It uses the Douglas Peucker algorithm.
     *
     * @param profile
     *          The profile, which should be simplified.
     * @param allowedDistance
     *          The allowed distance [m].
     */
    public static void simplifyProfile(final IProfile profile, final double allowedDistance) {
        /* Get the profile changes. */
        final IProfileRecord[] pointsToSimplify = profile.getPoints();

        final IProfileRecord[] pointsToRemoved = DouglasPeuckerHelper.reduce(allowedDistance, pointsToSimplify,
                profile);
        profile.removePoints(pointsToRemoved);
    }

    /**
     * This function thins the profile and removes unnecessary points. It uses the Douglas Peucker algorithm.
     *
     * @param profile
     *          The profile, which should be simplified.
     * @param allowedDistance
     *          The allowed distance [m].
     */
    public static void simplifyProfile(final IProfile profile, final double allowedDistance, final int startPoint,
            final int endPoint) {
        /* Get the profile changes. */
        final IProfileRecord[] pointsToSimplify = profile.getPoints(startPoint, endPoint);

        final IProfileRecord[] pointsToRemove = DouglasPeuckerHelper.reduce(allowedDistance, pointsToSimplify,
                profile);
        profile.removePoints(pointsToRemove);
    }

    /**
     * Either gets and existing component, or creates it if it doesn't exist yet.
     *
     * @return The index of the component
     */
    public static int getOrCreateComponent(final IProfile profil, final String componentID) {
        final int index = profil.indexOfProperty(componentID);
        if (index != -1)
            return index;

        profil.addPointProperty(ProfileUtil.getFeatureComponent(componentID));
        return profil.indexOfProperty(componentID);
    }
}