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; felix@1791: felix@1791: import org.jfree.data.xy.XYSeries; felix@1791: 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; 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: felix@2571: private static final Logger logger = Logger.getLogger felix@2571: (StyledSeriesBuilder.class); felix@1791: felix@1791: felix@1791: /** felix@1791: * Trivial, hidden constructor. felix@1791: */ sascha@1886: private StyledSeriesBuilder() { sascha@1886: } felix@1791: felix@1791: felix@1791: /** felix@6862: * Add points to series, create gaps if certain distance between felix@6862: * points is met and scale the Y value. felix@6862: * felix@6862: * @param series Series to add points to. felix@6862: * @param points Points to add to series, points[0] to 1st dim, points[1] felix@6862: * to 2nd dim. felix@6862: * @param skipNANs if true, skip NAN values in points parameter. Otherwise, felix@6862: * the NaNs lead to gaps in graph. felix@6862: * @param distance if two consecutive entries in points[0] are more felix@6862: * than distance apart, create a NaN value to skip in display. felix@6862: * Still, create a line segment. felix@6862: * @param factor Factor by which to scale the y value (points[1]). felix@6862: */ felix@6862: public static void addPointsFactorY(XYSeries series, felix@6862: double[][] points, felix@6862: boolean skipNANs, felix@6862: double distance, felix@6862: double factor felix@6862: ) { felix@6862: if (points == null || points.length <= 1) { felix@6862: return; felix@6862: } felix@6862: double [] xPoints = points[0]; felix@6862: double [] yPoints = points[1]; felix@6862: for (int i = 0; i < xPoints.length; i++) { felix@6862: if (skipNANs && felix@6862: (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@6862: logger.warn ("Skipping NaN in StyledSeriesBuilder."); felix@6862: continue; felix@6862: } felix@6862: // Create gap if distance >= distance. felix@6862: if (i != 0 && Math.abs(xPoints[i-1] - xPoints[i]) >= distance) { felix@6862: // Create at least a small segment for last point. felix@6862: if (!Double.isNaN(yPoints[i-1])) { felix@6862: series.add(xPoints[i-1]+0.99d*(distance)/2.d, yPoints[i-1]*factor, false); felix@6862: } felix@6862: felix@6862: if (!Double.isNaN(yPoints[i-1]) && !Double.isNaN(yPoints[i])) { felix@6862: series.add((xPoints[i-1]+xPoints[i])/2.d, Double.NaN, false); felix@6862: } felix@6862: } felix@6862: series.add(xPoints[i], yPoints[i]*factor, false); felix@6862: } felix@6862: } felix@6862: felix@6862: felix@6862: /** felix@6212: * Add points to series, create gaps if certain distance between points is met. felix@6212: * felix@6212: * @param series Series to add points to. felix@6212: * @param points Points to add to series, points[0] to 1st dim, points[1] felix@6212: * to 2nd dim. felix@6769: * @param skipNANs if true, skip NAN values in points parameter. Otherwise, felix@6769: * the NaNs lead to gaps in graph. felix@6212: * @param distance if two consecutive entries in points[0] are more felix@6212: * than distance apart, create a NaN value to skip in display. felix@6778: * Still, create a line segment. felix@6212: */ felix@6212: public static void addPoints(XYSeries series, double[][] points, boolean skipNANs, double distance) { felix@6212: if (points == null || points.length <= 1) { felix@6212: return; felix@6212: } felix@6212: double [] xPoints = points[0]; felix@6212: double [] yPoints = points[1]; felix@6212: for (int i = 0; i < xPoints.length; i++) { felix@6212: if (skipNANs && felix@6212: (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@6212: logger.warn ("Skipping NaN in StyledSeriesBuilder."); felix@6212: continue; felix@6212: } felix@6212: // Create gap if distance >= distance. felix@6212: if (i != 0 && Math.abs(xPoints[i-1] - xPoints[i]) >= distance) { felix@6778: // Create at least a small segment for last point. felix@6778: if (!Double.isNaN(yPoints[i-1])) { felix@6778: series.add(xPoints[i-1]+0.99d*(distance)/2.d, yPoints[i-1], false); felix@6778: } felix@6778: felix@6212: if (!Double.isNaN(yPoints[i-1]) && !Double.isNaN(yPoints[i])) { felix@6212: series.add((xPoints[i-1]+xPoints[i])/2.d, Double.NaN, false); felix@6212: } felix@6212: } felix@6212: series.add(xPoints[i], yPoints[i], false); felix@6212: } felix@6212: } felix@6212: felix@6212: /** felix@1791: * Add points to series. felix@1791: * felix@1791: * @param series Series to add points to. felix@1791: * @param points Points to add to series, points[0] to 1st dim, points[1] felix@1791: * to 2nd dim. felix@2686: * @param skipNANs if true, skip NAN values in points parameter. felix@6465: * @param transY translate y-values by this value (before scale). felix@6465: * @param factorY scale y-values by this value (after translation). felix@6465: */ felix@6465: public static void addPoints(XYSeries series, double[][] points, felix@6465: boolean skipNANs, double transY, 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: } felix@6465: double [] xPoints = points[0]; felix@6465: double [] yPoints = points[1]; felix@6465: for (int i = 0; i < xPoints.length; i++) { felix@6465: if (skipNANs && felix@6465: (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@6465: logger.warn ("Skipping NaN in StyledSeriesBuilder."); felix@6465: continue; felix@6465: } felix@6465: 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: * felix@6465: * @param series Series to add points to. felix@6465: * @param points Points to add to series, points[0] to 1st dim, points[1] felix@6465: * to 2nd dim. felix@6465: * @param skipNANs if true, skip NAN values in points parameter. felix@1791: */ felix@2686: public static void addPoints(XYSeries series, double[][] points, boolean skipNANs) { felix@1983: if (points == null || points.length <= 1) { felix@1975: return; felix@1975: } felix@1791: double [] xPoints = points[0]; felix@1791: double [] yPoints = points[1]; felix@1791: for (int i = 0; i < xPoints.length; i++) { felix@2686: if (skipNANs && felix@2686: (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) { felix@2571: logger.warn ("Skipping NaN in StyledSeriesBuilder."); felix@2571: continue; felix@2571: } felix@1791: series.add(xPoints[i], yPoints[i], false); felix@1791: } felix@1791: } felix@1791: felix@1791: felix@1791: /** felix@1791: * Add points to series (km to 1st dim, w to 2nd dim). felix@1791: * felix@1791: * @param series Series to add points to. felix@3269: * @param wkms WKms to add to series. felix@1791: */ felix@1791: public static void addPoints(XYSeries series, WKms wkms) { felix@2156: if (wkms == null) { felix@2156: return; felix@2156: } felix@2156: felix@1791: 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: /** felix@3395: * Add points to dataset with an offset (shift all points by given amount). felix@3395: * @param series series to add data to. felix@3395: * @param wkms WKms of which the Ws will be shifted. felix@3395: * @param off the offset. felix@3395: */ felix@3394: public static void addUpperBand(XYSeries series, WKms wkms, double off) { felix@3394: if (wkms == null) { felix@3394: return; felix@3394: } felix@3394: felix@3394: int size = wkms.size(); felix@3394: felix@3394: for (int i = 0; i < size; i++) { felix@3394: series.add(wkms.getKm(i), wkms.getW(i)+off, false); felix@3394: } felix@3394: } felix@3394: felix@3395: felix@3395: /** felix@3395: * Add points to dataset with an offset (shift all points 'down' by given felix@3395: * amount). felix@3395: * @param series series to add data to. felix@3395: * @param wkms WKms of which the Ws will be shifted. felix@3395: * @param off the offset. felix@3395: */ felix@3394: public static void addLowerBand(XYSeries series, WKms wkms, double off) { felix@3394: addUpperBand(series, wkms, -off); felix@3394: } felix@1812: felix@3395: felix@1791: /** felix@1942: * Add points to series (km to 1st dim, q to 2nd dim). felix@1791: * felix@1791: * @param series Series to add points to. felix@3269: * @param wqkms WQKms to add to series. felix@1791: */ felix@1791: public static void addPointsKmQ(XYSeries series, WQKms wqkms) { felix@2156: if (wqkms == null) { felix@2156: return; felix@2156: } felix@1791: felix@2156: 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@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: * felix@3176: * @param series Series to add points to. felix@3269: * @param wqkms WQKms to add to series. felix@3176: */ felix@3176: public static void addStepPointsKmQ(XYSeries series, WQKms wqkms) { felix@3176: if (wqkms == null) { felix@3176: return; felix@3176: } felix@3176: felix@3176: int size = wqkms.size(); felix@3176: felix@3176: for (int i = 0; i < size; i++) { felix@3176: if (i==0) { felix@3176: series.add(wqkms.getKm(i), wqkms.getQ(i), false); felix@3176: } felix@3176: else { felix@3176: //Add two points. felix@3176: double halveX = (wqkms.getKm(i-1) + wqkms.getKm(i)) / 2d; bjoern@4438: series.add(halveX, wqkms.getQ(i-1), false); bjoern@4438: series.add(halveX, wqkms.getQ(i), false); felix@3176: } felix@3176: if (i == size-1) { felix@3176: series.add(wqkms.getKm(i), wqkms.getQ(i), false); felix@3176: } 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: * felix@1812: * @param series Series to add points to. felix@3284: * @param wqkms WQKms to add to series. felix@1812: */ felix@1812: public static void addPointsQW(XYSeries series, WQKms wqkms) { felix@2156: if (wqkms == null) { felix@2156: return; felix@2156: } felix@2156: felix@1812: 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: * felix@6428: * @param series Series to add points to. felix@6881: * @param qws to add to series. felix@6881: * @param wAdd Value to add to each Q while adding to series. felix@6881: * @param wScale multiply with felix@6881: */ felix@6881: public static void addPointsQW(XYSeries series, double[][] qws, double wTrans, double wScale) { felix@6881: if (qws == null || qws.length == 0) { felix@6881: return; felix@6881: } felix@6881: felix@6881: double x[] = qws[0]; felix@6881: 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: } 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: * felix@6881: * @param series Series to add points to. felix@6428: * @param wqkms WQKms to add to series. felix@6428: * @param wAdd Value to add to each Q while adding to series. felix@6428: * @param wScale multiply with felix@6428: */ felix@6428: public static void addPointsQW(XYSeries series, WQKms wqkms, double wTrans, double wScale) { felix@6428: if (wqkms == null) { felix@6428: return; felix@6428: } felix@6428: felix@6428: 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: * felix@2601: * @param series Series to add points to. felix@4365: * @param qs the Qs to add, assumed same length than ws. felix@4365: * @param ws the Ws to add, assumed same length than qs. felix@4364: */ felix@4364: public static void addPointsQW(XYSeries series, double[] qs, double ws[]) { felix@4364: if (ws == null || qs == null) { felix@4364: return; felix@4364: } felix@4364: felix@4364: 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: * felix@6465: * @param series Series to add points to. felix@6465: * @param qs the Qs to add, assumed same length than ws. felix@6465: * @param ws the Ws to add, assumed same length than qs. felix@6465: */ felix@6465: public static void addPointsQW(XYSeries series, double[] qs, double ws[], felix@6465: double wTrans, double wScale) { felix@6465: if (ws == null || qs == null) { felix@6465: return; felix@6465: } felix@6465: felix@6465: int size = qs.length; felix@6465: felix@6465: for (int i = 0; i < size; i++) { felix@6465: series.add(qs[i], wScale * (ws[i]+wTrans), false); felix@6465: } felix@6465: } felix@6465: felix@6465: felix@4364: felix@4364: /** felix@4364: * Add points to series (q to 1st dim, w to 2nd dim). felix@4364: * felix@4364: * @param series Series to add points to. felix@3284: * @param wwqq WWQQ to add to series. felix@2601: */ felix@2601: public static void addPoints(XYSeries series, WWQQ wwqq) { felix@2601: if (wwqq == null) { felix@2601: return; felix@2601: } felix@2601: felix@2601: 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: /** 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: */ felix@2601: public static XYSeries createGroundAtInfinity(XYSeries series) { felix@2601: XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "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: /** 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: */ felix@2601: public static XYSeries createCeilingAtInfinity(XYSeries series) { felix@2601: XYSeries ground = new XYSeries(series.getKey() + /** TODO rand + */ "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@1791: } felix@1791: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :