ingo@298: package de.intevation.gnv.chart; ingo@298: ingo@298: import java.util.Collection; ingo@334: import java.util.HashMap; ingo@298: import java.util.Iterator; ingo@298: import java.util.Locale; ingo@298: ingo@298: import org.apache.log4j.Logger; ingo@298: ingo@298: import org.jfree.chart.ChartTheme; ingo@315: import org.jfree.chart.axis.Axis; ingo@315: import org.jfree.chart.plot.XYPlot; ingo@315: import org.jfree.chart.plot.PlotOrientation; ingo@298: import org.jfree.data.xy.XYSeries; ingo@298: import org.jfree.data.general.Series; ingo@298: import org.jfree.data.xy.XYSeriesCollection; ingo@298: ingo@298: import de.intevation.gnv.geobackend.base.Result; tim@335: import de.intevation.gnv.state.describedata.KeyValueDescibeData; ingo@298: ingo@298: ingo@298: /** ingo@298: * @author Ingo Weinzierl ingo@298: */ ingo@298: public class VerticalProfileChart ingo@298: extends AbstractXYLineChart ingo@298: { ingo@530: public static final String DEFAULT_AXIS = "KPOSITION"; ingo@530: ingo@298: private static Logger log = Logger.getLogger(VerticalProfileChart.class); ingo@298: ingo@340: protected final double PERCENTAGE = 5.0; ingo@340: protected final double GAP_MAX_LEVEL = Math.sqrt(2.0); ingo@340: protected final int GAP_MAX_VALUES = 60; ingo@340: ingo@298: ingo@298: public VerticalProfileChart( ingo@298: ChartLabels labels, ingo@298: ChartTheme theme, ingo@298: Collection parameters, ingo@298: Collection measurements, ingo@310: Collection dates, ingo@298: Collection result, ingo@310: Collection timeGaps, ingo@327: Locale locale, ingo@327: boolean linesVisible, ingo@327: boolean shapesVisible ingo@298: ) { ingo@298: this.labels = labels; ingo@298: this.theme = theme; ingo@298: this.parameters = parameters; ingo@298: this.measurements = measurements; ingo@310: this.dates = dates; ingo@298: this.resultSet = result; ingo@310: this.timeGaps = timeGaps; ingo@298: this.locale = locale; ingo@298: this.PLOT_ORIENTATION = PlotOrientation.HORIZONTAL; ingo@327: this.linesVisible = linesVisible; ingo@327: this.shapesVisible = shapesVisible; ingo@334: this.datasets = new HashMap(); ingo@364: this.ranges = new HashMap(); ingo@298: } ingo@298: ingo@298: ingo@298: protected void initData() { ingo@298: log.debug("init data for VerticalProfileChart"); ingo@298: ingo@298: String breakPoint1 = null; ingo@298: String breakPoint2 = null; ingo@298: String breakPoint3 = null; ingo@298: ingo@298: Iterator iter = resultSet.iterator(); ingo@298: Result row = null; ingo@298: String seriesName = null; ingo@364: String parameter = null; ingo@298: XYSeries series = null; ingo@298: ingo@340: int idx = 0; ingo@340: int startPos = 0; ingo@340: int endPos = 0; ingo@340: double startValue = 0; ingo@340: double endValue = 0; ingo@340: ingo@340: Result[] results = ingo@340: (Result[]) resultSet.toArray(new Result[resultSet.size()]); ingo@298: ingo@298: while (iter.hasNext()) { ingo@298: row = (Result) iter.next(); ingo@298: ingo@298: // add current data to plot and prepare for next one ingo@298: if (!row.getString("GROUP1").equals(breakPoint1) || ingo@298: !row.getString("GROUP2").equals(breakPoint2) || ingo@298: !row.getString("GROUP3").equals(breakPoint3) ingo@298: ) { ingo@298: log.debug("prepare data/plot for next dataset"); ingo@298: ingo@298: if(series != null) { ingo@340: gapDetection(results, series, startPos, endPos); ingo@364: addSeries(series, parameter, idx); ingo@340: ingo@340: startPos = endPos +1; ingo@298: } ingo@298: ingo@298: // prepare variables for next plot ingo@298: breakPoint1 = row.getString("GROUP1"); ingo@298: breakPoint2 = row.getString("GROUP2"); ingo@298: breakPoint3 = row.getString("GROUP3"); ingo@298: ingo@298: seriesName = createSeriesName( ingo@298: breakPoint1, ingo@298: breakPoint2, ingo@298: breakPoint3 ingo@298: ); ingo@364: parameter = findParameter(seriesName); ingo@298: ingo@298: log.debug("next dataset is '" + seriesName + "'"); ingo@298: series = new XYSeries(seriesName); ingo@298: } ingo@298: ingo@298: addValue(row, series); ingo@416: Double value = row.getDouble("YORDINATE"); ingo@416: if (value != null) ingo@416: storeMaxRange(value, parameter); ingo@340: endPos++; ingo@298: } ingo@298: ingo@340: if (results.length == 0) ingo@340: return; ingo@340: ingo@340: gapDetection(results, series, startPos, endPos); ingo@364: addSeries(series, parameter, idx); ingo@310: ingo@334: addDatasets(); ingo@298: } ingo@298: ingo@298: ingo@340: protected void gapDetection( ingo@340: Result[] results, ingo@340: Series series, ingo@340: int startPos, ingo@340: int endPos ingo@340: ) { ingo@340: double startValue = results[startPos].getDouble("XORDINATE"); ingo@340: double endValue = results[endPos-1].getDouble("XORDINATE"); ingo@340: if (results[0].getInteger("DATAID") == 2) ingo@340: addGapsOnGrid(results, series, startPos, endPos); ingo@340: else ingo@340: addGaps(results, series, startValue, endValue, startPos, endPos); ingo@340: } ingo@340: ingo@340: ingo@298: protected void addValue(Result row, Series series) { ingo@298: ((XYSeries) series).add( ingo@298: row.getDouble("XORDINATE"), ingo@298: row.getDouble("YORDINATE") ingo@298: ); ingo@298: } ingo@298: ingo@298: ingo@364: protected void addSeries(Series series, String parameter, int idx) { ingo@364: log.debug("add series (" + parameter + ")to chart"); ingo@298: ingo@298: if (series == null) { ingo@298: log.warn("no data to add"); ingo@298: return; ingo@298: } ingo@298: ingo@334: XYSeriesCollection xysc = null; ingo@334: ingo@334: if (datasets.containsKey(parameter)) ingo@334: xysc = (XYSeriesCollection) datasets.get(parameter); ingo@334: else ingo@334: xysc = new XYSeriesCollection(); ingo@334: ingo@334: xysc.addSeries((XYSeries) series); ingo@334: datasets.put(parameter, xysc); ingo@334: } ingo@334: ingo@334: ingo@334: protected void addDatasets() { ingo@334: Iterator iter = parameters.iterator(); ingo@334: XYPlot plot = chart.getXYPlot(); ingo@334: int idx = 0; ingo@334: ingo@334: XYSeriesCollection xysc = null; ingo@334: KeyValueDescibeData data = null; ingo@334: String key = null; ingo@334: while (iter.hasNext()) { ingo@334: data = (KeyValueDescibeData) iter.next(); ingo@334: key = data.getValue(); ingo@334: ingo@334: if (datasets.containsKey(key)) { ingo@334: xysc = (XYSeriesCollection)datasets.get(key); ingo@334: plot.setDataset(idx, xysc ); ingo@334: log.debug("Added " + key + " parameter to plot."); ingo@334: prepareAxis(key, idx); ingo@334: adjustRenderer( ingo@334: idx++, ingo@334: xysc.getSeriesCount(), ingo@334: linesVisible, ingo@334: shapesVisible ingo@334: ); ingo@334: } ingo@334: } ingo@298: } ingo@298: ingo@298: ingo@315: protected void localizeDomainAxis(Axis axis, Locale locale) { ingo@315: // call localizeRangeAxis from superclass which formats NumberAxis ingo@315: super.localizeRangeAxis(axis, locale); ingo@315: } ingo@315: ingo@315: ingo@298: protected String createSeriesName( ingo@298: String breakPoint1, ingo@298: String breakPoint2, ingo@298: String breakPoint3 ingo@298: ) { ingo@298: log.debug("create seriesname of verticalprofile chart"); ingo@298: return findValueTitle(parameters, breakPoint1) + ingo@298: " " + ingo@298: findValueTitle(measurements, breakPoint2) + ingo@298: "m"; ingo@298: } ingo@340: ingo@340: ingo@340: protected void addGapsOnGrid( ingo@340: Result[] results, ingo@340: Series series, ingo@340: int startPos, ingo@340: int endPos ingo@340: ) { ingo@530: String axis = null; ingo@530: ingo@530: if (results.length > (startPos+1)) { ingo@530: axis = getDependendAxisName( ingo@530: results[startPos], ingo@530: results[startPos+1] ingo@530: ); ingo@530: } ingo@530: else { ingo@530: axis = DEFAULT_AXIS; ingo@530: } ingo@530: ingo@340: double range = 0; ingo@340: int last = 0; ingo@340: int current = 0; ingo@340: ingo@340: for (int i = startPos+1; i < endPos; i++) { ingo@340: last = results[i-1].getInteger(axis); ingo@340: current = results[i].getInteger(axis); ingo@340: ingo@340: boolean detected = gridDetection(last, current); ingo@340: ingo@340: if (detected) { ingo@340: double xOld = results[i-1].getDouble("XORDINATE"); ingo@340: double xNow = results[i].getDouble("XORDINATE"); ingo@340: log.debug("Gap detected on grid between "+ xOld +" and "+ xNow); ingo@340: ((XYSeries) series).add(xOld+0.0001, null); ingo@340: } ingo@340: } ingo@340: } ingo@340: ingo@340: ingo@340: protected void addGaps( ingo@340: Result[] results, ingo@340: Series series, ingo@340: double startValue, ingo@340: double endValue, ingo@340: int startPos, ingo@340: int endPos ingo@340: ) { ingo@340: ingo@340: double last = 0; ingo@340: double current = 0; ingo@340: int num = results.length; ingo@340: ingo@340: for (int i = startPos+1; i < endPos; i++) { ingo@340: boolean detected = false; ingo@340: ingo@340: last = results[i-1].getDouble("YORDINATE"); ingo@340: current = results[i].getDouble("YORDINATE"); ingo@340: ingo@340: // gap detection for more than GAP_MAX_VALUES values ingo@340: if (num > GAP_MAX_VALUES) ingo@340: detected = simpleDetection(startValue, endValue, last, current); ingo@340: // gap detection for less than GAP_MAX_VALUES values ingo@340: else ingo@340: detected = specialDetection( ingo@340: startValue, ingo@340: endValue, ingo@340: last, ingo@340: current, ingo@340: num ingo@340: ); ingo@340: ingo@340: if (detected) { ingo@340: log.info("Gap between " + last + " and " + current); ingo@340: ((XYSeries) series).add((last+current)/2, null); ingo@340: } ingo@340: } ingo@340: } ingo@340: ingo@340: ingo@340: protected boolean simpleDetection( ingo@340: double start, ingo@340: double end, ingo@340: double last, ingo@340: double current ingo@340: ) { ingo@340: double delta = Math.abs(end - start); ingo@340: double smallDelta = Math.abs(current - last); ingo@340: ingo@340: return (smallDelta > delta / 100 * PERCENTAGE); ingo@340: } ingo@340: ingo@340: ingo@340: protected boolean specialDetection( ingo@340: double start, ingo@340: double end, ingo@340: double last, ingo@340: double current, ingo@340: int count ingo@340: ) { ingo@340: double delta = Math.abs(end - start); ingo@340: double smallDelta = Math.abs(current - last); ingo@340: ingo@340: return (smallDelta > (3.0 / (count - 1) * delta)); ingo@340: } ingo@340: ingo@340: ingo@340: protected boolean gridDetection(double last, double current) { ingo@340: return (Math.abs(current - last) > GAP_MAX_LEVEL); ingo@340: } ingo@340: ingo@340: ingo@340: protected String getDependendAxisName(Result first, Result second) { ingo@370: return "KPOSITION"; ingo@340: } ingo@298: } ingo@315: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :