Mercurial > dive4elements > gnv-client
view gnv-artifacts/src/main/java/de/intevation/gnv/chart/HorizontalProfileChart.java @ 800:db5b04ecb426
ISSUE215: Improved ODV-Export. now all Columns which have identical values but different Parameters will be merged to one row.
gnv-artifacts/trunk@882 c6561f87-3c4e-4783-a992-168aeb5c3f6f
author | Tim Englich <tim.englich@intevation.de> |
---|---|
date | Tue, 06 Apr 2010 13:07:11 +0000 |
parents | e8a9e84a0328 |
children | 22c18083225e |
line wrap: on
line source
package de.intevation.gnv.chart; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.io.ParseException; import com.vividsolutions.jts.io.WKTReader; import de.intevation.gnv.geobackend.base.Result; import de.intevation.gnv.utils.DistanceCalculator; import java.util.Collection; import java.util.Locale; import java.util.Map; import org.apache.log4j.Logger; import org.jfree.chart.ChartTheme; import org.jfree.chart.plot.PlotOrientation; import org.jfree.data.general.Series; import org.jfree.data.xy.XYSeries; /** * This class is used to create xy-charts of horizontal profiles. * * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a> */ public class HorizontalProfileChart extends VerticalProfileChart { /** * Logger used for logging with log4j. */ private static Logger log = Logger.getLogger(HorizontalProfileChart.class); /** * <code>WKTReader</code> used to turn wkt strings into geometries. */ private static WKTReader wktReader = new WKTReader(); /** * The first point in a HorizontalProfileChart. It is used to calculate the * distance between the currently processed point an the start. */ private Point firstPoint; /** * Constructor used to create horizontal profile charts. * * @param labels Labels used to be displayed in title, subtitle and so on. * @param theme ChartTheme used to adjust the rendering of this chart. * @param parameters Collection containing a bunch of parameters. * @param measurements Collection containing a bunch of measurements. * @param dates Collection containing a bunch of date objects. * @param result Collection containing a bunch of <code>Result</code> * objects which contain the actual data items to be displayed. * @param timeGaps Collection with timegap definitions. * @param locale Locale used to specify the format of labels, numbers, ... * @param linesVisible Render lines between data points if true, otherwise * not. * @param shapesVisible Render vertices as points if true, otherwise not. */ public HorizontalProfileChart( ChartLabels labels, ChartTheme theme, Collection parameters, Collection measurements, Collection dates, Collection result, Collection timeGaps, Locale locale, boolean linesVisible, boolean shapesVisible ) { super( labels, theme, parameters, measurements, dates, result, timeGaps, locale, linesVisible, shapesVisible ); this.PLOT_ORIENTATION = PlotOrientation.VERTICAL; } /** * @see de.intevation.gnv.chart.VerticalProfileChart#getValue(Result) */ @Override protected Object getValue(Result row) { try { return (Point) wktReader.read(row.getString("SHAPE")); } catch(ParseException pe) { log.warn("No data found while parsing."); return null; } } /** * @see de.intevation.gnv.chart.VerticalProfileChart#gapDetection(Result, * Series, int, int) */ @Override protected void gapDetection( Result[] results, Series series, int startPos, int endPos ) { log.debug("Start gap detection."); try { Point startValue = getPoint(results[startPos]); Point endValue = getPoint(results[endPos-1]); if (results[0].getInteger("DATAID") == 2) addGapsOnGrid(results, series, startPos, endPos); else addGaps( results, series, startValue, endValue, startPos, endPos ); } catch (ParseException pe) { log.warn( "Error while parsing points for gap detection. " + "No gaps for current series will be detected." ); } log.debug("Gap detection finished."); } /** * @see de.intevation.gnv.chart.AbstractXYLineChart(Result, Series) */ @Override protected void addValue(Result row, Series series) { double distance = 0; try { Point point = (Point) wktReader.read(row.getString("SHAPE")); if (firstPoint != null) { distance = DistanceCalculator.calculateDistance( firstPoint, point ); } else { firstPoint = point; } ((XYSeries) series).add( distance, row.getDouble("YORDINATE") ); } catch(ParseException pe) { log.warn("No data found while parsing."); } } /** * @see de.intevation.gnv.chart.AbstractXYLineChart#addSeries(Series, * String, int) */ @Override protected void addSeries(Series series, String label, int idx) { super.addSeries(series, label, idx); // reset firstPoint for next series firstPoint = null; } /** * @see de.intevation.gnv.chart.VerticalProfileChart#prepareRangeAxis(String, * int) */ @Override protected void prepareRangeAxis(String seriesKey, int idx) { return; // do nothing here } /** * @param value * @see de.intevation.gnv.chart.VerticalProfileChart#storeMaxValue(Map, * Object, String) */ @Override protected void storeMaxValue(Map values, Object value, String parameter) { return; // do nothing here } /** * @see de.intevation.gnv.chart.AbstractXYLineChart#createSeriesName(String, * String, String) */ @Override protected String createSeriesName( String breakPoint1, String breakPoint2, String breakPoint3 ) { log.debug("create seriesname of horizontalprofile chart"); return super.createSeriesName( breakPoint1, breakPoint2, breakPoint3) + " " + findValueTitle(dates, breakPoint3); } /** * @see de.intevation.gnv.chart.VerticalProfileChart#addGapsOnGrid(Result[], * Series, int, int) */ @Override protected void addGapsOnGrid( Result[] results, Series series, int startPos, int endPos ) { String axis = getDependendAxisName( results[startPos], results[startPos+1] ); int last = 0; int current = 0; Point lastPoint = null; Point currentPoint = null; try { firstPoint = getPoint(results[0]); } catch (ParseException pe) { log.error("Unable to parse start point for gap detection."); return; } for (int i = startPos+1; i < endPos; i++) { try { last = results[i-1].getInteger(axis); lastPoint = getPoint(results[i-1]); current = results[i].getInteger(axis); currentPoint = getPoint(results[i]); double distance = DistanceCalculator.calculateDistance( firstPoint, currentPoint); double distanceOld = DistanceCalculator.calculateDistance( firstPoint, lastPoint); boolean detected = gridDetection(last, current); if (log.isDebugEnabled()) { log.debug("Last point: " + lastPoint.toString()); log.debug("Current point: " + currentPoint.toString()); log.debug("Current distance from start: " + distance); } if (detected) { log.info( "Gap detected on grid between " + distanceOld + " and " + distance); ((XYSeries) series).add(distance-1d, null); ((XYSeries) series).add(distanceOld+1d, null); } } catch (ParseException pe) { log.warn("Error while parsing point for gap detection.", pe); } } } /** * Method to add gaps between two data points. The real detection is done by * {@link #simpleDetection} and {@link #specialDetection}. * * @param results All data points in this dataset. * @param series Series to be processed. * @param startValue <code>Point</code> where the scan for gaps should begin. * @param endValue <code>Point</code> where the scan should end. * @param startPos Start position of this series in <code>results</code>. * @param endPos End position of a series in <code>results</code> */ protected void addGaps( Result[] results, Series series, Point startValue, Point endValue, int startPos, int endPos ) { double range = 0; Point last = null; Point now = null; for (int i = startPos+1; i < endPos; i++) { boolean detected = false; try { last = (Point) getPoint(results[i-1]); now = (Point) getPoint(results[i]); // gap detection for more than GAP_MAX_VALUES values if (results.length > GAP_MAX_VALUES) detected = simpleDetection(startValue, endValue, last, now); // gap detection for less than GAP_MAX_VALUES values else detected = specialDetection( startValue, endValue, last, now, results.length ); // gap detected, insert null value to break line if (detected) { log.info("Gap after " + range); double x = range + 0.0001; ((XYSeries)series).add(x, null); } range += DistanceCalculator.calculateDistance(last,now); } catch (ParseException pe) { log.warn("Error while parsing point."); } } } /** * Simple method to detect gaps. A gap is detected if the delta between two * data points (current, last) is bigger than <code>PERCENTAGE</code> percent * of delta of start and end. * <br> * (smallDelta > delta / 100 * PERCENTAGE) * * @param start First data point in a series * @param end Last data point in a series * @param last Left point * @param current Right point * * @return true, if a gap is detected between last and current - otherwise * false. */ protected boolean simpleDetection( Point start, Point end, Point last, Point current ) { double delta = DistanceCalculator.calculateDistance(start, end); double deltaSmall = DistanceCalculator.calculateDistance(last,current); return (deltaSmall > (delta / 100 * PERCENTAGE)); } /** * Method to detect gaps between two data points. Following formula is used * for detection:<br> * smallDelta > (3.0 / (count - 1) * delta)<br> * smallDelta = distance between <code>current</code> and <code>last</code> * <br> * delta = distance between <code>start</code> and <code>end</code> * * @param start First data point in a series * @param end Last data point in a series * @param last Left point * @param current Right point * @param count Number of datapoints * @return true, if a gap is detected between last and current - otherwise * false. */ protected boolean specialDetection( Point start, Point end, Point last, Point current, int count ) { double delta = Math.abs( DistanceCalculator.calculateDistance(end, start) ); double smallDelta = Math.abs( DistanceCalculator.calculateDistance(current, last) ); return (smallDelta > (3.0 / (count - 1) * delta)); } /** * @see de.intevation.gnv.chart.VerticalProfileChart#getDependendAxisName(Result, * Result) */ @Override protected String getDependendAxisName(Result first, Result second) { if (first.getInteger("IPOSITION") == second.getInteger("IPOSITION")) return "JPOSITION"; return "IPOSITION"; } /** * This method returns a point from a given wkt string stored in * <code>result</code>. * * @param result <code>Result</code> object which contains the wkt string. * The wkt string needs to be available under the key SHAPE. * * @return Point representation of wkt string. */ private Point getPoint(Result result) throws ParseException { return (Point) wktReader.read(result.getString("SHAPE")); } } // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :