Mercurial > dive4elements > gnv-client
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 :