org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.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.kalypsomodel1d2d.ui.map;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.kalypso.contribs.eclipse.core.runtime.StatusUtilities;
import org.kalypso.kalypsomodel1d2d.KalypsoModel1D2DPlugin;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.DiscretisationModelUtils;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IElement1D;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IFE1D2DEdge;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IFE1D2DElement;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IFE1D2DNode;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IFEDiscretisationModel1d2d;
import org.kalypso.kalypsomodel1d2d.schema.binding.discr.IPolyElement;
import org.kalypso.kalypsomodel1d2d.ui.i18n.Messages;
import org.kalypso.kalypsomodel1d2d.ui.map.element1d.Create2dElementCommand;
import org.kalypso.kalypsomodel1d2d.ui.map.util.UtilMap;
import org.kalypso.ogc.gml.IKalypsoFeatureTheme;
import org.kalypsodeegree.KalypsoDeegreePlugin;
import org.kalypsodeegree.graphics.transformation.GeoTransform;
import org.kalypsodeegree.model.feature.Feature;
import org.kalypsodeegree.model.feature.FeatureList;
import org.kalypsodeegree.model.geometry.GM_Curve;
import org.kalypsodeegree.model.geometry.GM_Exception;
import org.kalypsodeegree.model.geometry.GM_Object;
import org.kalypsodeegree.model.geometry.GM_Point;
import org.kalypsodeegree.model.geometry.GM_Polygon;
import org.kalypsodeegree.model.geometry.GM_Position;
import org.kalypsodeegree_impl.model.geometry.GeometryFactory;
import org.kalypsodeegree_impl.tools.GeometryUtilities;

/**
 * TODO: separate (again) geometry building from 1d2d element building! This class is used to build new 1D2D Elements
 * 
 * @author Gernot Belger
 */
public class ElementGeometryBuilder {
    /**
     * Stores the count of points which this geometry must have. If it is 0, there is no rule.
     */
    private final int m_cnt_points;

    /** A list of locations for new nodes. */
    private final List<GM_Point> m_nodes = new ArrayList<>();

    private final IKalypsoFeatureTheme m_nodeTheme;

    private boolean m_valid;

    /**
     * The constructor.
     * 
     * @param cnt_points
     *          If >0 the the geometry will be finished, if the count of points is reached. If 0 no rule regarding the
     *          count of the points will apply.
     * @param targetCrs
     *          The target coordinate system.
     */
    public ElementGeometryBuilder(final int cnt_points, final IKalypsoFeatureTheme nodeTheme) {
        m_nodeTheme = nodeTheme;
        if (cnt_points > 0)
            m_cnt_points = cnt_points;
        else
            m_cnt_points = 0;
    }

    /**
     * Adds a node or a would-be node (i.e. a GM_Point) to this builder.<br>
     * REMARK: No validity check is done here. Call {@link #checkNewNode(Object)} before a new node is added.
     */
    public final Create2dElementCommand addNode(final GM_Point node) throws Exception {
        m_nodes.add(node);
        removeDuplicates(m_nodes);

        if (m_nodes.size() == m_cnt_points && m_cnt_points != 0) {
            return finish();
        }

        return null;
    }

    /**
     * REMARK: No validity check is done here. Call {@link #checkNewNode(Object)} before a new node is added.
     */
    public final Create2dElementCommand finish() throws Exception {
        final FeatureList featureList = m_nodeTheme.getFeatureList();
        final Feature parentFeature = featureList.getOwner();

        /* Initialize elements needed for edges and elements */
        final IFEDiscretisationModel1d2d discModel = (IFEDiscretisationModel1d2d) parentFeature;

        final GM_Point[] nodes = m_nodes.toArray(new GM_Point[m_nodes.size()]);
        return new Create2dElementCommand(discModel, nodes);
    }

    public void paint(final Graphics g, final GeoTransform projection, final Point currentPoint) {
        // IMPORTANT: we remember GM_Points (not Point's) and re-transform them for painting
        // because the projection depends on the current map-extent, so this builder
        // is stable in regard to zoom in/out
        final int[][] points = UtilMap.getPointArrays(currentPoint, m_nodes, projection);

        final int[] arrayX = points[0];
        final int[] arrayY = points[1];

        /* Paint a linestring. */
        final Color color = g.getColor();

        Color preViewColor;
        if (m_valid == true)
            preViewColor = new Color(100, 255, 100);
        else
            preViewColor = new Color(255, 100, 100);

        g.setColor(preViewColor);

        g.drawPolygon(arrayX, arrayY, arrayX.length);
        UtilMap.drawHandles(g, arrayX, arrayY);

        g.setColor(color);

    }

    public final List<GM_Point> getNodes() {
        return m_nodes;
    }

    public int getNumberOfNodes() {
        return m_nodes.size();
    }

    public boolean contains(final IFE1D2DNode snapNode) {
        return m_nodes.contains(snapNode);
    }

    private static void removeDuplicates(final List<GM_Point> list) {
        Object last = null;
        for (final Iterator<GM_Point> iterator = list.iterator(); iterator.hasNext();) {
            final GM_Point next = iterator.next();
            if (ObjectUtils.equals(last, next))
                iterator.remove();
            last = next;
        }
    }

    public IStatus checkNewNode(final GM_Point newNode) {
        return checkNewNode(newNode, true);
    }

    /**
     * Checks, if the resulting element would be valid, if the given new node would be inserted at the given position.
     */
    public IStatus checkNewNode(final GM_Point newNode, final boolean pBoolFinalPoint) {
        final List<GM_Point> list = new ArrayList<>(m_nodes);
        list.add(newNode);

        removeDuplicates(list);

        final IStatus status = checkNewElement(list.toArray(new GM_Point[list.size()]), pBoolFinalPoint);

        if (status == Status.OK_STATUS)
            m_valid = true;
        else
            m_valid = false;

        return status;
    }

    // REMARK: some optimization is done here, in order to enhance performance.
    // We assume, that the same checks has been done for every newly added node, so we check only
    // Criteria, which could go wrong for the new node (i.e. the last one in the array).
    private IStatus checkNewElement(final GM_Point[] allNodes, final boolean pBoolFinalPont) {
        try {
            final GM_Point newPoint = allNodes[allNodes.length - 1];

            final IFEDiscretisationModel1d2d discModel = DiscretisationModelUtils.modelForTheme(m_nodeTheme);

            // 0) Node was already grabbed
            if (ArrayUtils.indexOf(allNodes, newPoint) != allNodes.length - 1)
                return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                        Messages.getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.0")); //$NON-NLS-1$

            // 1) New Node lies inside an element (only for non-grabbed point)

            final IPolyElement elementForNewNode = discModel.find2DElement(newPoint, 0.0);
            if (elementForNewNode != null) {
                final IFE1D2DNode foundNode = discModel.findNode(newPoint,
                        IFEDiscretisationModel1d2d.CLUSTER_TOLERANCE);
                if (foundNode == null) {
                    final GM_Polygon surface = elementForNewNode.getGeometry();
                    if (surface.contains(newPoint))
                        return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                                org.kalypso.kalypsomodel1d2d.ui.i18n.Messages
                                        .getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.6")); //$NON-NLS-1$
                }
            }

            // 1a) Node lies on 1d node
            final IFE1D2DNode node = discModel.findNode(newPoint, IFEDiscretisationModel1d2d.CLUSTER_TOLERANCE);
            if (node != null) {
                final IFE1D2DEdge[] nodeEdges = node.getLinkedEdges();
                for (final IFE1D2DEdge nodeItem : nodeEdges) {
                    final IFE1D2DElement[] containers = nodeItem.getLinkedElements();
                    for (final IFE1D2DElement edgeContainer : containers) {
                        if (edgeContainer instanceof IElement1D)
                            return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                                    Messages.getString("ElementGeometryBuilder.0")); //$NON-NLS-1$
                    }
                }
            }

            if (allNodes.length < 2)
                return Status.OK_STATUS;

            // 2) New Edge crosses other edge

            // 2b) First edge crosses other elements
            if (allNodes.length == 2) {
                final GM_Position[] line = ElementGeometryHelper.linePositionsFromNodes(allNodes);

                final GM_Curve curve = GeometryFactory.createGM_Curve(line,
                        KalypsoDeegreePlugin.getDefault().getCoordinateSystem());
                final List<IFE1D2DElement> elements = discModel.queryElements(curve.getEnvelope(), null);
                for (final IFE1D2DElement element : elements) {
                    if (element instanceof IPolyElement) {
                        final GM_Polygon eleGeom = ((IPolyElement) element).getGeometry();
                        if (eleGeom == null) {
                            // check for null geometries... What to do?
                            // delete the elements???

                            return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID, Messages
                                    .getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.2")); //$NON-NLS-1$
                        }
                        if (eleGeom.intersects(curve)) {
                            final GM_Object intersection = eleGeom.intersection(curve);
                            if (intersection instanceof GM_Curve) {
                                final GM_Curve intersCurve = (GM_Curve) intersection;
                                final GM_Point startPoint = intersCurve.getAsLineString().getStartPoint();
                                final GM_Point endPoint = intersCurve.getAsLineString().getEndPoint();

                                if (checkIntersectionCurve(allNodes, startPoint, endPoint))
                                    return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                                            Messages.getString(
                                                    "org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.3")); //$NON-NLS-1$
                            }
                        }
                    }
                }
            }

            // 3) New edge links two non-adjacent points

            if (allNodes.length < 3)
                return Status.OK_STATUS;

            final GM_Position[] ring = ElementGeometryHelper.ringPositionsFromNodes(allNodes);

            // 4) New Element self-intersects
            if (GeometryUtilities.isSelfIntersecting(ring) && pBoolFinalPont)
                return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                        Messages.getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.4")); //$NON-NLS-1$

            final GM_Polygon newSurface = GeometryFactory.createGM_Surface(ring, new GM_Position[][] {},
                    KalypsoDeegreePlugin.getDefault().getCoordinateSystem());

            // new element is not convex
            if (m_cnt_points > 0 && newSurface.getConvexHull().difference(newSurface) != null)
                return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID,
                        Messages.getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.7")); //$NON-NLS-1$

            // new element intersects other elements
            final List<IFE1D2DElement> elements = discModel.queryElements(newSurface.getEnvelope(), null);
            for (final IFE1D2DElement element : elements) {
                if (element instanceof IPolyElement) {
                    final GM_Polygon eleGeom = ((IPolyElement) element).getGeometry();
                    if (eleGeom.intersects(newSurface) && pBoolFinalPont) {
                        final GM_Object intersection = eleGeom.intersection(newSurface);
                        if (intersection instanceof GM_Polygon)
                            return new Status(IStatus.ERROR, KalypsoModel1D2DPlugin.PLUGIN_ID, Messages
                                    .getString("org.kalypso.kalypsomodel1d2d.ui.map.ElementGeometryBuilder.5")); //$NON-NLS-1$
                    }
                }
            }

            return Status.OK_STATUS;
        } catch (final GM_Exception e) {
            e.printStackTrace();
            return StatusUtilities.statusFromThrowable(e);
        }
    }

    /**
     * checks, if the first and last point lies on the same position as the intersection curve. If this is the case,
     * return false, else return true.
     */
    private boolean checkIntersectionCurve(final GM_Point[] allNodes, final GM_Point startPoint,
            final GM_Point endPoint) {
        if (startPoint.isWithinDistance(allNodes[0], 0.0) && endPoint.isWithinDistance(allNodes[1], 0.0))
            return false;
        if (startPoint.isWithinDistance(allNodes[1], 0.0) && endPoint.isWithinDistance(allNodes[0], 0.0))
            return false;

        return true;

    }

    public void removeLast() {
        if (m_nodes != null && m_nodes.size() > 0)
            m_nodes.remove(m_nodes.size() - 1);
    }
}