view flys-artifacts/src/main/java/de/intevation/flys/artifacts/geom/Lines.java @ 2089:0da8874bd378

Added initial state to map artifact to be able to advance and step back. The map artifact overrides describe() to have the complete UI information in the describe response document. flys-artifacts/trunk@3613 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author Raimund Renkert <raimund.renkert@intevation.de>
date Fri, 06 Jan 2012 12:02:10 +0000
parents a7def20539fb
children a6fa128e4654
line wrap: on
line source
package de.intevation.flys.geom;

import java.util.ArrayList;
import java.util.List;
import java.util.Iterator;

import java.awt.geom.Point2D;
import java.awt.geom.Line2D;

import de.intevation.flys.artifacts.math.Linear;

import org.apache.log4j.Logger;

import gnu.trove.TDoubleArrayList;

public class Lines
{
    private static Logger log = Logger.getLogger(Lines.class);

    public static final double EPSILON = 1e-4;

    public static enum Mode { UNDEF, WET, DRY };

    protected Lines() {
    }

    public static List<Line2D> fillWater(List<Point2D> points, double waterLevel) {

        boolean debug = log.isDebugEnabled();

        if (debug) {
            log.debug("fillWater");
            log.debug("----------------------------");
        }

        List<Line2D> result = new ArrayList();

        int N = points.size();

        if (N == 0) {
            return result;
        }

        if (N == 1) {
            Point2D p = points.get(0);
            // Only generate point if over water
            if (waterLevel > p.getY()) {
                result.add(new Line2D.Double(
                    p.getX(), waterLevel,
                    p.getX(), waterLevel));
            }
            return result;
        }

        double minX =  Double.MAX_VALUE;
        double minY =  Double.MAX_VALUE;
        double maxX = -Double.MAX_VALUE;
        double maxY = -Double.MAX_VALUE;

        // To ensure for sequences of equals x's that
        // the original index order is preserved.
        for (Point2D p: points) {
            double x = p.getX(), y = p.getY();
            if (x < minX) minX = x;
            if (x > maxX) maxX = x;
            if (y < minY) minY = y;
            if (y > maxY) maxY = y;
        }

        if (minY > waterLevel) { // profile completely over water level
            log.debug("complete over water");
            return result;
        }

        if (waterLevel > maxY) { // water floods profile
            log.debug("complete under water");
            result.add(new Line2D.Double(minX, waterLevel, maxX, waterLevel));
            return result;
        }

        Mode mode = Mode.UNDEF;

        double startX = minX;

        for (int i = 1; i < N; ++i) {
            Point2D p1 = points.get(i-1);
            Point2D p2 = points.get(i);

            if (p1.getY() < waterLevel && p2.getY() < waterLevel) {
                // completely under water
                if (debug) {
                    log.debug("under water: " + p1 + " " + p2);
                }
                if (mode != Mode.WET) {
                    startX = p1.getX();
                    mode = Mode.WET;
                }
                continue;
            }

            if (p1.getY() > waterLevel && p2.getY() > waterLevel) {
                if (debug) {
                    log.debug("over water: " + p1 + " " + p2);
                }
                // completely over water
                if (mode == Mode.WET) {
                    log.debug("over/wet");
                    result.add(new Line2D.Double(
                        startX, waterLevel,
                        p1.getX(), waterLevel));
                }
                mode = Mode.DRY;
                continue;
            }

            if (Math.abs(p1.getX() - p2.getX()) < EPSILON) {
                // vertical line
                switch (mode) {
                    case WET:
                        log.debug("vertical/wet");
                        mode = Mode.DRY;
                        result.add(new Line2D.Double(
                            startX, waterLevel,
                            p1.getX(), waterLevel));
                        break;
                    case DRY:
                        log.debug("vertical/dry");
                        mode = Mode.WET;
                        startX = p2.getX();
                        break;
                    default: // UNDEF
                        log.debug("vertical/undef");
                        if (p2.getY() < waterLevel) {
                            mode = Mode.WET;
                            startX = p2.getX();
                        }
                        else {
                            mode = Mode.DRY;
                        }
                }
                continue;
            }

            // check if waterlevel directly hits the vertices;

            boolean p1W = Math.abs(waterLevel - p1.getY()) < EPSILON;
            boolean p2W = Math.abs(waterLevel - p2.getY()) < EPSILON;

            if (p1W || p2W) {
                if (debug) {
                    log.debug("water hits vertex: " + p1 + " " + p2 + " " + mode);
                }
                if (p1W && p2W) { // parallel to water -> dry
                    log.debug("water hits both vertices");
                    if (mode == Mode.WET) {
                        result.add(new Line2D.Double(
                            startX, waterLevel,
                            p1.getX(), waterLevel));
                    }
                    mode = Mode.DRY;
                }
                else if (p1W) { // p1 == waterlevel
                    log.debug("water hits first vertex");
                    if (p2.getY() > waterLevel) { // --> dry
                        if (mode == Mode.WET) {
                            result.add(new Line2D.Double(
                                startX, waterLevel,
                                p1.getX(), waterLevel));
                        }
                        mode = Mode.DRY;
                    }
                    else { // --> wet
                        if (mode != Mode.WET) {
                            startX = p1.getX();
                            mode = Mode.WET;
                        }
                    }
                }
                else { // p2 == waterlevel
                    log.debug("water hits second vertex");
                    if (p1.getY() > waterLevel) { // --> wet
                        if (mode != Mode.WET) {
                            startX = p2.getX();
                            mode = Mode.WET;
                        }
                    }
                    else { // --> dry
                        if (mode == Mode.WET) {
                            result.add(new Line2D.Double(
                                startX, waterLevel,
                                p2.getX(), waterLevel));
                        }
                        mode = Mode.DRY;
                    }
                }
                if (debug) {
                    log.debug("mode is now: " + mode);
                }
                continue;
            }

            // intersection case
            double x = Linear.linear(
                waterLevel,
                p1.getY(), p2.getY(),
                p1.getX(), p2.getX());

            if (debug) {
                log.debug("intersection p1:" + p1);
                log.debug("intersection p2:" + p2);
                log.debug("intersection at x: " + x);
            }

            switch (mode) {
                case WET:
                    log.debug("intersect/wet");
                    mode = Mode.DRY;
                    result.add(new Line2D.Double(
                        startX, waterLevel,
                        x, waterLevel));
                    break;

                case DRY:
                    log.debug("intersect/dry");
                    mode   = Mode.WET;
                    startX = x;
                    break;

                default: // UNDEF
                    log.debug("intersect/undef");
                    if (p2.getY() > waterLevel) {
                        log.debug("intersect/undef/over");
                        mode = Mode.DRY;
                        result.add(new Line2D.Double(
                            p1.getX(), waterLevel,
                            x, waterLevel));
                    }
                    else {
                        mode = Mode.WET;
                        startX = x;
                    }
            } // switch mode
        } // for all points p[i] and p[i-1]

        if (mode == Mode.WET) {
            result.add(new Line2D.Double(
                startX, waterLevel,
                maxX, waterLevel));
        }

        return result;
    }

    public static double [][] createWaterLines(
        List<Point2D> points,
        double        waterlevel
    ) {
        List<Line2D> lines = fillWater(points, waterlevel);

        TDoubleArrayList lxs = new TDoubleArrayList();
        TDoubleArrayList lys = new TDoubleArrayList();

        for (Iterator<Line2D> iter = lines.iterator(); iter.hasNext();) {
            Line2D  l  = iter.next();
            Point2D p1 = l.getP1();
            Point2D p2 = l.getP2();
            lxs.add(p1.getX());
            lys.add(p1.getY());
            lxs.add(p2.getX());
            lys.add(p2.getY());
            if (iter.hasNext()) {
                lxs.add(Double.NaN);
                lys.add(Double.NaN);
            }
        }

        return new double [][] { lxs.toNativeArray(), lys.toNativeArray() };
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org