org.kalypso.model.wspm.pdb.internal.waterlevel2d.ProjectedWaterlevels.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.model.wspm.pdb.internal.waterlevel2d.ProjectedWaterlevels.java

Source

/** This file is part of Kalypso
 *
 *  Copyright (c) 2012 by
 *
 *  Bjrnsen Beratende Ingenieure GmbH, Koblenz, Germany (Bjoernsen Consulting Engineers), http://www.bjoernsen.de
 *  Technische Universitt Hamburg-Harburg, Institut fr Wasserbau, Hamburg, Germany
 *  (Technical University Hamburg-Harburg, Institute of River and Coastal Engineering), http://www.tu-harburg.de/wb/
 *
 *  Kalypso is free software: you can redistribute it and/or modify it under the terms
 *  of the GNU Lesser General Public License (LGPL) as published by the Free Software
 *  Foundation, either version 3 of the License, or (at your option) any later version.
 *
 *  Kalypso 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 Kalypso.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.kalypso.model.wspm.pdb.internal.waterlevel2d;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

import org.apache.commons.lang3.Range;
import org.eclipse.core.runtime.IStatus;
import org.hibernatespatial.mgeom.MGeometryException;
import org.hibernatespatial.mgeom.MLineString;
import org.kalypso.contribs.eclipse.core.runtime.IStatusCollector;
import org.kalypso.contribs.eclipse.core.runtime.StatusCollector;
import org.kalypso.model.wspm.core.profil.IProfileObject;
import org.kalypso.model.wspm.core.profil.IProfileObjectRecord;
import org.kalypso.model.wspm.core.profil.IProfileObjectRecords;
import org.kalypso.model.wspm.core.profil.impl.GenericProfileHorizon;
import org.kalypso.model.wspm.core.util.JTSWaterlevelIntersector;
import org.kalypso.model.wspm.pdb.db.mapping.WaterlevelFixation;
import org.kalypso.model.wspm.pdb.gaf.GafPointCode;
import org.kalypso.model.wspm.pdb.gaf.IGafConstants;
import org.kalypso.model.wspm.pdb.internal.WspmPdbCorePlugin;
import org.kalypso.model.wspm.pdb.internal.i18n.Messages;
import org.kalypso.model.wspm.tuhh.core.IWspmTuhhConstants;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;

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

/**
 * Helper class that holds all the {@link ProjectedWaterlevel}s.
 * 
 * @author Gernot Belger
 */
public class ProjectedWaterlevels {
    private final IStatusCollector m_log = new StatusCollector(WspmPdbCorePlugin.PLUGIN_ID);

    private final ProjectedWaterlevel[] m_waterlevels;

    private final String m_eventName;

    private final BigDecimal m_station;

    private final MLineString m_profileLine;

    public ProjectedWaterlevels(final String eventName, final BigDecimal station, final MLineString profileLine,
            final WaterlevelFixation[] waterlevels) {
        m_eventName = eventName;
        m_station = station;
        m_profileLine = profileLine;

        m_waterlevels = new ProjectedWaterlevel[waterlevels.length];
        for (int i = 0; i < m_waterlevels.length; i++)
            m_waterlevels[i] = new ProjectedWaterlevel(profileLine, waterlevels[i]);
    }

    public IStatus getStatus() {
        return m_log.asMultiStatus(Messages.getString("ProjectedWaterlevels_0")); //$NON-NLS-1$
    }

    public IProfileObject[] createParts(final double douglasPeuckerDistance)
            throws MismatchedDimensionException, FactoryException, TransformException {
        /* create 'original' waterlevel: just the fixation points projected to the profile line */
        final ProjectOriginalWaterlevelWorker originalWorker = new ProjectOriginalWaterlevelWorker(m_eventName,
                m_waterlevels);
        final IStatus originalStatus = originalWorker.execute();
        if (!originalStatus.isOK())
            m_log.add(originalStatus);

        final IProfileObject originalWaterlevel = originalWorker.getResult();

        /* ignore empty waterlevels */
        if (originalWaterlevel.getRecords().size() == 0) {
            m_log.add(IStatus.WARNING, Messages.getString("ProjectedWaterlevels_1"), null, m_station); //$NON-NLS-1$
            return new IProfileObject[] {};
        }

        /* simplify original waterlevel, to avoid too many points */
        final SimplifyProjectedWaterlevelWorker simplifyWorker = new SimplifyProjectedWaterlevelWorker(
                originalWaterlevel, douglasPeuckerDistance);
        final IStatus simplifyStatus = simplifyWorker.execute();
        if (!simplifyStatus.isOK())
            m_log.add(simplifyStatus);

        final IProfileObject simplifiedWaterlevel = simplifyWorker.getResult();

        /* create 2d waterlevels */
        final IProfileObject[] waterlevels2d = create2Dwaterlevels(simplifiedWaterlevel);

        /* clean obviously bad points from original waterlevel */
        final CleanProjectedWaterlevelWorker cleanWorker = new CleanProjectedWaterlevelWorker(m_profileLine,
                simplifiedWaterlevel);
        final IStatus cleanStatus = cleanWorker.execute();
        if (!cleanStatus.isOK())
            m_log.add(cleanStatus);

        final IProfileObject cleanedOriginalWaterlevel = cleanWorker.getResult();

        /* build return set */
        final Collection<IProfileObject> allParts = new ArrayList<>();
        allParts.add(cleanedOriginalWaterlevel);
        allParts.addAll(Arrays.asList(waterlevels2d));

        return allParts.toArray(new IProfileObject[allParts.size()]);
    }

    private IProfileObject[] create2Dwaterlevels(final IProfileObject simplifiedWaterlevel) {
        final Coordinate[] profileCoordinates = extractWidthHeightCoordinates();
        if (profileCoordinates.length < 2)
            return new IProfileObject[] {};

        final Range<Double> profileWidthRange = calculateExtendedWidthRange(profileCoordinates);

        /* create waterlevel line */
        final LineString waterlevelLine = extractLine(simplifiedWaterlevel, profileWidthRange);
        if (waterlevelLine == null)
            return new IProfileObject[] {};

        /* intersect waterlevel with profile line */
        final JTSWaterlevelIntersector intersector = new JTSWaterlevelIntersector(profileCoordinates);
        final LineString[] waterlevelLines = intersector.createWaterlevels(waterlevelLine);

        /* build parts from intersections */
        return buildWaterlevel2dParts(waterlevelLines);
    }

    /**
     * Fetch coordinates in width/height coordinate system from profile line
     */
    private Coordinate[] extractWidthHeightCoordinates() {
        final Coordinate[] profileCoordinates = m_profileLine.getCoordinates();

        final Coordinate[] widthHeights = new Coordinate[profileCoordinates.length];

        for (int i = 0; i < widthHeights.length; i++) {
            final double width = m_profileLine.getMatN(i);
            final double height = profileCoordinates[i].z;

            widthHeights[i] = new Coordinate(width, height);
        }

        return widthHeights;
    }

    /**
     * Calculate min/max x value of coordinates.<br/>
     * The returned range is extended in both directions by the width of the real range.
     */
    private Range<Double> calculateExtendedWidthRange(final Coordinate[] profileCoordinates) {
        /* calculate min/max */
        double min = Double.MAX_VALUE;
        double max = -Double.MAX_VALUE;

        for (final Coordinate coordinate : profileCoordinates) {
            final double value = coordinate.x;

            min = Math.min(min, value);
            max = Math.max(max, value);
        }

        /* extend range */
        final double distance = max - min;

        return Range.between(min - distance, max + distance);
    }

    private LineString extractLine(final IProfileObject simplifiedWaterlevel,
            final Range<Double> profileWidthRange) {
        final CoordinateList waterlevelLocations = new CoordinateList();

        /* extract waterlevel locations */
        final IProfileObjectRecords records = simplifiedWaterlevel.getRecords();
        for (int i = 0; i < records.size(); i++) {
            final IProfileObjectRecord record = records.getRecord(i);
            final Coordinate widthHeight = record.getWidthHeightLocation();
            waterlevelLocations.add(widthHeight, false);
        }

        /* sort locations by x */
        sortCoordinatesByX(waterlevelLocations);

        /* deny empty waterlevels */
        if (waterlevelLocations.isEmpty())
            return null;

        /* extend waterlevel to left and right to make sure that it covers the whole profile */
        final Coordinate leftBorder = waterlevelLocations.getCoordinate(0);
        final double leftExtendX = Math.min(leftBorder.x, profileWidthRange.getMinimum());
        final Coordinate leftExtend = new Coordinate(leftExtendX, leftBorder.y);

        final Coordinate rightBorder = waterlevelLocations.getCoordinate(waterlevelLocations.size() - 1);
        final double rightExtendX = Math.min(rightBorder.x, profileWidthRange.getMaximum());
        final Coordinate rightExtend = new Coordinate(rightExtendX, leftBorder.y);

        waterlevelLocations.add(0, leftExtend, false);
        waterlevelLocations.add(rightExtend, false);

        /* create the waterlevel line */
        final Coordinate[] waterlevelcoordinates = waterlevelLocations.toCoordinateArray();
        return m_profileLine.getFactory().createLineString(waterlevelcoordinates);
    }

    /**
     * Extra method to suppress warning
     */
    @SuppressWarnings("unchecked")
    private void sortCoordinatesByX(final CoordinateList coordinates) {
        /* sort locations by x; fortunately the natural order is what we need */
        Collections.sort(coordinates);
    }

    private IProfileObject[] buildWaterlevel2dParts(final LineString[] waterlevelLines) {
        final Collection<IProfileObject> waterlevelParts = new ArrayList<>(waterlevelLines.length);
        for (final LineString waterlevelLine : waterlevelLines) {
            try {
                final IProfileObject waterlevelPart = buildWaterlevel2dPart(waterlevelLine);
                if (waterlevelPart != null) {
                    /* set general data */
                    final String waterlevelName = m_eventName + waterlevelParts.size() + 1;
                    // TODO: important, that name is unique withing the cross section, how can we force this here?
                    waterlevelPart.setValue(IGafConstants.PART_NAME, waterlevelName);
                    waterlevelParts.add(waterlevelPart);
                }
            } catch (final MGeometryException | MismatchedDimensionException | FactoryException
                    | TransformException e) {
                m_log.add(IStatus.WARNING, Messages.getString("ProjectedWaterlevels_2"), e); //$NON-NLS-1$
            }
        }

        return waterlevelParts.toArray(new IProfileObject[waterlevelParts.size()]);
    }

    private IProfileObject buildWaterlevel2dPart(final LineString waterlevelLine)
            throws MGeometryException, MismatchedDimensionException, FactoryException, TransformException {
        /* prepare to extract original points involved in this waterlevel part */
        final Range<Double> widthRange = Range.between(waterlevelLine.getStartPoint().getX(),
                waterlevelLine.getEndPoint().getX());

        /* create generic part of 2d waterlevel */
        final GenericProfileHorizon waterlevel2D = new GenericProfileHorizon(
                IWspmTuhhConstants.OBJECT_TYPE_WATERLEVEL_SEGMENT);

        /* get description only from involved points */
        final AggregatedWaterlevel aggregator = new AggregatedWaterlevel(m_waterlevels, widthRange);
        final String description = aggregator.getDescription();
        waterlevel2D.setDescription(description);

        /* get discharge only from involved points */
        final BigDecimal discharge = aggregator.getDischarge();
        if (discharge != null)
            waterlevel2D.setValue(IGafConstants.METADATA_WATERLEVEL_DISCHARGE, discharge.toString());

        /* convert to points */
        final IProfileObjectRecords records = waterlevel2D.getRecords();

        final Coordinate[] coordinates = waterlevelLine.getCoordinates();
        for (final Coordinate coordinate : coordinates) {
            final double width = coordinate.x;
            final double height = coordinate.y;

            /* extract location at width from profile */
            final Coordinate location = m_profileLine.getCoordinateAtM(width);

            /* create record and add values */
            final IProfileObjectRecord record = records.addNewRecord();

            record.setBreite(width);
            record.setHoehe(height);
            record.setComment(null);

            // FIXME: extrapolate location if outside profile
            if (location != null) {
                record.setRechtswert(location.x);
                record.setHochwert(location.y);
            }

            // FIXME: WS is of kind W, not W2
            record.setCode(GafPointCode.WS.getKey());
        }

        return waterlevel2D;
    }
}