diff gnv-artifacts/src/main/java/de/intevation/gnv/math/LinearToMap.java @ 1119:7c4f81f74c47

merged gnv-artifacts
author Thomas Arendsen Hein <thomas@intevation.de>
date Fri, 28 Sep 2012 12:14:00 +0200
parents f953c9a559d8
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/gnv-artifacts/src/main/java/de/intevation/gnv/math/LinearToMap.java	Fri Sep 28 12:14:00 2012 +0200
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2010 by Intevation GmbH
+ *
+ * This program is free software under the LGPL (>=v2.1)
+ * Read the file LGPL.txt coming with the software for details
+ * or visit http://www.gnu.org/licenses/ if it does not exist.
+ */
+
+package de.intevation.gnv.math;
+
+import com.vividsolutions.jts.geom.Coordinate;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Given a list of line segments instances of this class are able
+ * to span a metric system between a start and an end point
+ * represented as scalar values to 2D coordinate on the
+ * course of the segments.
+ *
+ * @author <a href="mailto:sascha.teichmann@intevation.de">Sascha L. Teichmann</a>
+ */
+public class LinearToMap
+{
+    /**
+     * Represents a segment of the line string.
+     */
+    public static final class Range {
+        private Range next;
+
+        private double from;
+        private double to;
+        private double b;
+
+        private Coordinate p1;
+        private Coordinate p2;
+
+        private Interpolator interpolator;
+
+        /**
+         * Default constructor.
+         */
+        public Range() {
+        }
+
+        /**
+         * Constructor to create a segment that maps
+         * a coordinate pair to two scalar values.
+         * Interpolations inside this segment are done with
+         * a given interpolator.
+         * @param from Start point of the segment.
+         * @param to End point of the segment.
+         * @param interpolator The interpolator.
+         * @param p1 The scalar value mapped to the start point.
+         * @param p2 The scalar value mappend to the end point.
+         */
+        public Range(
+            double       from,
+            double       to,
+            Interpolator interpolator,
+            Coordinate   p1,
+            Coordinate   p2
+        ) {
+            this.from         = from;
+            this.to           = to;
+            this.interpolator = interpolator;
+            this.p1           = p1;
+            this.p2           = p2;
+
+            b = from == to
+                ? 0d
+                : 1.0d/(to - from);
+        }
+
+        /**
+         * Interpolated a coordinate on the segment given a scalar value
+         * between the start and end point of the range.
+         * @param x The scalar value.
+         * @param v The interpolated value is stored here.
+         */
+        public void eval(double x, Coordinate v) {
+            interpolator.interpolate((x - from)*b, v);
+        }
+
+        /**
+         * Checks if a given value is inside this segment.
+         * @param x The value to test
+         * @return true if inside, else false.
+         */
+        public boolean inside(double x) {
+            return x >= from && x <= to;
+        }
+
+        /**
+         * Returns the start point of this segment.
+         * @return The start point.
+         */
+        public Coordinate startPoint() {
+            return p1;
+        }
+
+        /**
+         * Return the end point of this segment.
+         * @return The end point.
+         */
+        public Coordinate endPoint() {
+            return p2;
+        }
+    } // class Range
+
+    /**
+     * The head of the internal range list.
+     */
+    protected Range head;
+
+    /**
+     * The last accessed segment. Used to accelerate
+     * the access of the right segment.
+     */
+    protected Range last;
+
+    /**
+     * Default constructor.
+     */
+    public LinearToMap() {
+    }
+
+    /**
+     * Constructor to create a LinearToMap that maps
+     * given scalar values to coordinates of a given
+     * list of line segments.
+     * @param path The list of line segments.
+     * @param from The start value mapped to the start point
+     * of the first line segment.
+     * @param to The end value mapped to the end point of
+     * the last line segment.
+     * @param metrics The metric used to span the 2D space.
+     */
+    public LinearToMap(
+        List<? extends Coordinate> path,
+        double                     from,
+        double                     to,
+        Metrics                    metrics
+    ) {
+        double diagramLength = Math.abs(to - from);
+
+        double worldLength = length(path, metrics);
+
+        double rangeStart = from;
+
+        Range last = null;
+
+        for (int i = 1, N = path.size(); i < N; ++i) {
+            Coordinate p1 = path.get(i-1);
+            Coordinate p2 = path.get(i);
+            double segmentLength = metrics.distance(p1, p2);
+
+            double relativeLength = segmentLength / worldLength;
+
+            double rangeLength = diagramLength * relativeLength;
+
+            double rangeEnd = rangeStart + rangeLength;
+
+            Range range = new Range(
+                rangeStart, rangeEnd,
+                metrics.getInterpolator(p1, p2),
+                p1, p2);
+
+            if (last == null) {
+                last = head = range;
+            }
+            else {
+                last.next = range;
+                last = range;
+            }
+            rangeStart = rangeEnd;
+        }
+    }
+
+    /**
+     * Returns a segment on which a given value is found.
+     * @param diagramX The value.
+     * @return The segment or null if no matching segment was found.
+     */
+    protected Range locateRange(double diagramX) {
+
+        if (last != null && last.inside(diagramX)) {
+            return last;
+        }
+
+        Range current = head;
+        while (current != null) {
+            if (current.inside(diagramX)) {
+                return last = current;
+            }
+            current = current.next;
+        }
+
+        return null;
+    }
+
+    /**
+     * Interpolates a coordinate at a given scalar position.
+     * @param diagramX The scalar position.
+     * @param v The interpolated coordinate is stored here.
+     * @return true if the scalar position is inside the
+     * spanned range of the line string, else false.
+     */
+    public boolean locate(double diagramX, Coordinate v) {
+        Range range = locateRange(diagramX);
+        if (range == null) {
+            return false;
+        }
+        range.eval(diagramX, v);
+        return true;
+    }
+
+    /**
+     * Returns the length of a given line string using
+     * a given metric.
+     * @param path The line string.
+     * @param metrics The used metric.
+     * @return The length of the line string.
+     */
+    public static double length(
+        List<? extends Coordinate> path,
+        Metrics                    metrics
+    ) {
+        double sum = 0d;
+        for (int i = path.size()-1; i >= 1; --i) {
+            Coordinate p1 = path.get(i);
+            Coordinate p2 = path.get(i-1);
+            sum += metrics.distance(p1, p2);
+        }
+        return sum;
+    }
+
+    /**
+     * Return the number of segments in this map.
+     * @return the number of segments.
+     */
+    public int numRanges() {
+        int count = 0;
+        Range current = head;
+        while (current != null) {
+            ++count;
+            current = current.next;
+        }
+        return count;
+    }
+
+    /**
+     * Returns an iterator over all segments of this map.
+     * @return The iterator.
+     */
+    public Iterator ranges() {
+        return new Iterator() {
+
+            Range current = head;
+
+            public boolean hasNext() {
+                return current != null;
+            }
+
+            public Object next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                Range x = current;
+                current = current.next;
+                return x;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org