teichmann@5863: /* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde teichmann@5863: * Software engineering by Intevation GmbH teichmann@5863: * teichmann@5994: * This file is Free Software under the GNU AGPL (>=v3) teichmann@5863: * and comes with ABSOLUTELY NO WARRANTY! Check out the teichmann@5994: * documentation coming with Dive4Elements River for details. teichmann@5863: */ teichmann@5863: teichmann@5831: package org.dive4elements.river.exports; felix@1791: felix@1791: import org.apache.log4j.Logger; teichmann@5831: import org.dive4elements.river.artifacts.model.WKms; teichmann@5831: import org.dive4elements.river.artifacts.model.WQKms; teichmann@5831: import org.dive4elements.river.artifacts.model.WWQQ; d@9617: import org.jfree.data.xy.XYSeries; felix@1791: felix@1791: /** felix@1791: * Helper to create and modify StyledXYSeries. felix@1791: */ felix@1791: public class StyledSeriesBuilder { felix@1791: felix@2601: /** felix@2601: * JFreeChart and the area calculation will fail if we use Double.INFINITY felix@2601: * or Double.MAX_VALUE (probably because these are really used in felix@2601: * calculations). We define and use a more handy value instead. felix@2601: */ felix@2601: final static double BIG_DOUBLE_VALUE = 1234567d; felix@2601: d@9617: private static final Logger log = Logger.getLogger(StyledSeriesBuilder.class); felix@1791: felix@1791: /** felix@1791: * Trivial, hidden constructor. felix@1791: */ sascha@1886: private StyledSeriesBuilder() { sascha@1886: } felix@1791: felix@1791: /** tom@8856: * Add points to series, create gaps if certain distance tom@8856: * between points is met. felix@6212: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param points d@9617: * Points to add to series, points[0] to 1st dim, points[1] d@9617: * to 2nd dim. d@9617: * @param skipNANs d@9617: * if true, skip NAN values in points parameter. Otherwise, d@9617: * the NaNs lead to gaps in graph. d@9617: * @param distance d@9617: * if two consecutive entries in points[0] are more d@9617: * than distance apart, create a NaN value to skip d@9617: * in display. felix@6212: */ d@9617: public static void addPoints(final XYSeries series, final double[][] points, final boolean skipNANs, final double distance) { felix@6212: if (points == null || points.length <= 1) { felix@6212: return; felix@6212: } d@9617: final double[] xPoints = points[0]; d@9617: final double[] yPoints = points[1]; d@9617: gernotbelger@9347: Integer lastNonNaNIndex = null; d@9617: felix@6212: for (int i = 0; i < xPoints.length; i++) { d@9617: if (skipNANs && (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@6212: continue; felix@6212: } gernotbelger@9347: tom@8698: // Create gap if distance between points > distance. d@9617: if (i > 0 && lastNonNaNIndex != null) { d@9617: final double distanceToLastNonNan = Math.abs(xPoints[lastNonNaNIndex] - xPoints[i]); gernotbelger@9347: if (distanceToLastNonNan > distance && !Double.isNaN(yPoints[lastNonNaNIndex]) && !Double.isNaN(yPoints[i])) d@9617: series.add((xPoints[i - 1] + xPoints[i]) / 2, Double.NaN, false); felix@6212: } felix@6212: series.add(xPoints[i], yPoints[i], false); d@9617: gernotbelger@9590: // if (skipNANs && !Double.isNaN(xPoints[i]) && !Double.isNaN(yPoints[i])) d@9617: if (!Double.isNaN(xPoints[i]) && !Double.isNaN(yPoints[i])) // skipNaN-State irrelevant, since lastNonNaNIndex is used for interpolation d@9617: // after gap-distance-comparison gernotbelger@9590: lastNonNaNIndex = i; felix@6212: } felix@6212: } felix@6212: felix@6212: /** felix@1791: * Add points to series. felix@1791: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param points d@9617: * Points to add to series, points[0] to 1st dim, points[1] d@9617: * to 2nd dim. d@9617: * @param skipNANs d@9617: * if true, skip NAN values in points parameter. d@9617: * @param transY d@9617: * translate y-values by this value (before scale). d@9617: * @param factorY d@9617: * scale y-values by this value (after translation). felix@6465: */ d@9617: public static void addPoints(final XYSeries series, final double[][] points, final boolean skipNANs, final double transY, final double factorY) { felix@6465: if (transY == 0d && factorY == 1d) { felix@6465: addPoints(series, points, skipNANs); felix@6465: return; felix@6465: } felix@6465: if (points == null || points.length <= 1) { felix@6465: return; felix@6465: } d@9617: final double[] xPoints = points[0]; d@9617: final double[] yPoints = points[1]; felix@6465: for (int i = 0; i < xPoints.length; i++) { d@9617: if (skipNANs && (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@6465: continue; felix@6465: } d@9617: series.add(xPoints[i], factorY * (transY + yPoints[i]), false); felix@6465: } felix@6465: } felix@6465: felix@6465: /** felix@6465: * Add points to series. felix@6465: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param points d@9617: * Points to add to series, points[0] to 1st dim, points[1] d@9617: * to 2nd dim. d@9617: * @param skipNANs d@9617: * if true, skip NAN values in points parameter. felix@1791: */ d@9617: public static void addPoints(final XYSeries series, final double[][] points, final boolean skipNANs) { felix@1983: if (points == null || points.length <= 1) { felix@1975: return; felix@1975: } d@9617: final double[] xPoints = points[0]; d@9617: final double[] yPoints = points[1]; felix@1791: for (int i = 0; i < xPoints.length; i++) { d@9617: if (skipNANs && (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@2571: continue; felix@2571: } felix@1791: series.add(xPoints[i], yPoints[i], false); felix@1791: } felix@1791: } felix@1791: felix@1791: /** felix@1791: * Add points to series (km to 1st dim, w to 2nd dim). felix@1791: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wkms d@9617: * WKms to add to series. felix@1791: */ d@9617: public static void addPoints(final XYSeries series, final WKms wkms) { felix@2156: if (wkms == null) { felix@2156: return; felix@2156: } felix@2156: d@9617: final int size = wkms.size(); felix@1791: felix@1791: for (int i = 0; i < size; i++) { felix@1791: series.add(wkms.getKm(i), wkms.getW(i), false); felix@1791: } felix@1791: } felix@1791: felix@3395: /** felix@3395: * Add points to dataset with an offset (shift all points by given amount). d@9617: * d@9617: * @param series d@9617: * series to add data to. d@9617: * @param wkms d@9617: * WKms of which the Ws will be shifted. d@9617: * @param off d@9617: * the offset. felix@3395: */ d@9617: public static void addUpperBand(final XYSeries series, final WKms wkms, final double off) { felix@3394: if (wkms == null) { felix@3394: return; felix@3394: } felix@3394: d@9617: final int size = wkms.size(); felix@3394: felix@3394: for (int i = 0; i < size; i++) { d@9617: series.add(wkms.getKm(i), wkms.getW(i) + off, false); felix@3394: } felix@3394: } felix@3394: felix@3395: /** felix@3395: * Add points to dataset with an offset (shift all points 'down' by given felix@3395: * amount). d@9617: * d@9617: * @param series d@9617: * series to add data to. d@9617: * @param wkms d@9617: * WKms of which the Ws will be shifted. d@9617: * @param off d@9617: * the offset. felix@3395: */ d@9617: public static void addLowerBand(final XYSeries series, final WKms wkms, final double off) { felix@3394: addUpperBand(series, wkms, -off); felix@3394: } felix@1812: felix@1791: /** felix@1942: * Add points to series (km to 1st dim, q to 2nd dim). felix@1791: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wqkms d@9617: * WQKms to add to series. felix@1791: */ d@9617: public static void addPointsKmQ(final XYSeries series, final WQKms wqkms) { felix@2156: if (wqkms == null) { felix@2156: return; felix@2156: } felix@1791: d@9617: final int size = wqkms.size(); felix@2156: felix@2156: for (int i = 0; i < size; i++) { felix@2156: series.add(wqkms.getKm(i), wqkms.getQ(i), false); felix@2156: } felix@1791: } felix@1812: felix@1812: /** felix@3176: * Add points to series (km to 1st dim, q to 2nd dim), adding points felix@3176: * to achieve a step-like curve. felix@3176: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wqkms d@9617: * WQKms to add to series. felix@3176: */ d@9617: public static void addStepPointsKmQ(final XYSeries series, final WQKms wqkms) { felix@3176: if (wqkms == null) { felix@3176: return; felix@3176: } felix@3176: d@9617: final int size = wqkms.size(); felix@3176: felix@3176: for (int i = 0; i < size; i++) { d@9617: if (i == 0) { felix@3176: series.add(wqkms.getKm(i), wqkms.getQ(i), false); d@9617: } else if (i == size - 1) { andre@8507: series.add(wqkms.getKm(i), wqkms.getQ(i), false); andre@8507: } else { d@9617: // Add two points. andre@8507: double prevX; andre@8507: double prevQ; andre@8507: if (wqkms.getKm(i + 1) < wqkms.getKm(i)) { d@9617: /* d@9617: * Depending on the data direction the previous km / q tom@8856: * might have a larger index when we draw d@9617: * right to left data. d@9617: */ andre@8507: prevX = wqkms.getKm(i + 1); andre@8507: prevQ = wqkms.getQ(i + 1); andre@8507: } else { andre@8507: prevX = wqkms.getKm(i - 1); andre@8507: prevQ = wqkms.getQ(i - 1); andre@8507: } d@9617: final double halveX = (prevX + wqkms.getKm(i)) / 2d; andre@8507: series.add(halveX, prevQ, false); bjoern@4438: series.add(halveX, wqkms.getQ(i), false); felix@3176: } felix@3176: } felix@3176: } felix@3176: felix@3176: /** felix@1812: * Add points to series (q to 1st dim, w to 2nd dim). felix@1812: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wqkms d@9617: * WQKms to add to series. felix@1812: */ d@9617: public static void addPointsQW(final XYSeries series, final WQKms wqkms) { felix@2156: if (wqkms == null) { felix@2156: return; felix@2156: } felix@2156: d@9617: final int size = wqkms.size(); felix@1812: felix@1812: for (int i = 0; i < size; i++) { bjoern@4438: series.add(wqkms.getQ(i), wqkms.getW(i), false); felix@1812: } felix@1812: } felix@2601: felix@6428: /** felix@6428: * Add points to series (q to 1st dim, w to 2nd dim), adding wTrans to the felix@6428: * W values and scaling it with wScale. felix@6428: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param qws d@9617: * to add to series. d@9617: * @param wAdd d@9617: * Value to add to each Q while adding to series. d@9617: * @param wScale d@9617: * multiply with felix@6881: */ d@9617: public static void addPointsQW(final XYSeries series, final double[][] qws, final double wTrans, final double wScale) { felix@6881: if (qws == null || qws.length == 0) { felix@6881: return; felix@6881: } felix@6881: d@9617: final double x[] = qws[0]; d@9617: final double y[] = qws[1]; felix@6881: felix@6881: for (int i = 0; i < x.length; i++) { felix@6881: series.add(x[i], wScale * (y[i] + wTrans), false); felix@6881: } felix@6881: } d@9617: felix@6881: /** felix@6881: * Add points to series (q to 1st dim, w to 2nd dim), adding wTrans to the felix@6881: * W values and scaling it with wScale. felix@6881: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wqkms d@9617: * WQKms to add to series. d@9617: * @param wAdd d@9617: * Value to add to each Q while adding to series. d@9617: * @param wScale d@9617: * multiply with felix@6428: */ d@9617: public static void addPointsQW(final XYSeries series, final WQKms wqkms, final double wTrans, final double wScale) { felix@6428: if (wqkms == null) { felix@6428: return; felix@6428: } felix@6428: d@9617: final int size = wqkms.size(); felix@6428: felix@6428: for (int i = 0; i < size; i++) { felix@6428: series.add(wqkms.getQ(i), wScale * (wqkms.getW(i) + wTrans), false); felix@6428: } felix@6428: } felix@2601: felix@2601: /** felix@2601: * Add points to series (q to 1st dim, w to 2nd dim). felix@2601: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param qs d@9617: * the Qs to add, assumed same length than ws. d@9617: * @param ws d@9617: * the Ws to add, assumed same length than qs. felix@4364: */ d@9617: public static void addPointsQW(final XYSeries series, final double[] qs, final double ws[]) { felix@4364: if (ws == null || qs == null) { felix@4364: return; felix@4364: } felix@4364: d@9617: final int size = qs.length; felix@4364: felix@4364: for (int i = 0; i < size; i++) { bjoern@4438: series.add(qs[i], ws[i], false); felix@4364: } felix@4364: } felix@4364: felix@6465: /** felix@6465: * Add points to series (q to 1st dim, w to 2nd dim), with felix@6465: * scaling and translation. felix@6465: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param qs d@9617: * the Qs to add, assumed same length than ws. d@9617: * @param ws d@9617: * the Ws to add, assumed same length than qs. felix@6465: */ d@9617: public static void addPointsQW(final XYSeries series, final double[] qs, final double ws[], final double wTrans, final double wScale) { felix@6465: if (ws == null || qs == null) { felix@6465: return; felix@6465: } felix@6465: d@9617: final int size = qs.length; felix@6465: felix@6465: for (int i = 0; i < size; i++) { d@9617: series.add(qs[i], wScale * (ws[i] + wTrans), false); felix@6465: } felix@6465: } felix@6465: felix@4364: /** felix@4364: * Add points to series (q to 1st dim, w to 2nd dim). felix@4364: * d@9617: * @param series d@9617: * Series to add points to. d@9617: * @param wwqq d@9617: * WWQQ to add to series. felix@2601: */ d@9617: public static void addPoints(final XYSeries series, final WWQQ wwqq) { felix@2601: if (wwqq == null) { felix@2601: return; felix@2601: } felix@2601: d@9617: final int size = wwqq.size(); felix@2601: felix@2601: for (int i = 0; i < size; i++) { bjoern@4438: series.add(wwqq.getW1(i), wwqq.getW2(i), false); felix@2601: } felix@2601: } felix@2601: felix@2601: /** felix@2601: * Create a Series such that an infinitely big area can be filled felix@2601: * between the newly created and the given series. felix@2601: */ d@9617: public static XYSeries createGroundAtInfinity(final XYSeries series) { d@9617: final XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ d@9617: "INF"); felix@2601: ground.add(series.getMinX(), -BIG_DOUBLE_VALUE); felix@2601: ground.add(series.getMaxX(), -BIG_DOUBLE_VALUE); felix@2601: return ground; felix@2601: } felix@2601: felix@2601: /** felix@2601: * Create a Series such that an infinitely big area can be filled felix@2601: * between the newly created and the given series. felix@2601: */ d@9617: public static XYSeries createCeilingAtInfinity(final XYSeries series) { d@9617: final XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ d@9617: "INF"); felix@2601: ground.add(series.getMinX(), BIG_DOUBLE_VALUE); felix@2601: ground.add(series.getMaxX(), BIG_DOUBLE_VALUE); felix@2601: return ground; felix@2601: } gernotbelger@8935: d@9617: /** Checks if a given value is the BIG_DOUBLE_VALUE */ d@9617: public static boolean isBigDoubleValue(final Number value) { d@9617: if (value == null) d@9617: return false; gernotbelger@8935: d@9617: return Math.abs(BIG_DOUBLE_VALUE - Math.abs(value.doubleValue())) < 0.1; gernotbelger@8935: } felix@1791: } felix@1791: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :