ingo@298: package de.intevation.gnv.chart;
ingo@298: 
sascha@779: import de.intevation.gnv.geobackend.base.Result;
sascha@779: 
sascha@779: import de.intevation.gnv.state.describedata.KeyValueDescibeData;
sascha@779: 
ingo@298: import java.util.Collection;
ingo@334: import java.util.HashMap;
ingo@298: import java.util.Iterator;
ingo@298: import java.util.Locale;
sascha@779: import java.util.Map;
ingo@298: 
ingo@298: import org.apache.log4j.Logger;
ingo@298: 
ingo@298: import org.jfree.chart.ChartTheme;
sascha@779: 
ingo@315: import org.jfree.chart.axis.Axis;
ingo@656: import org.jfree.chart.axis.NumberAxis;
sascha@779: 
ingo@315: import org.jfree.chart.plot.PlotOrientation;
sascha@779: import org.jfree.chart.plot.XYPlot;
sascha@779: 
ingo@656: import org.jfree.data.Range;
sascha@779: 
ingo@656: import org.jfree.data.general.Series;
sascha@779: 
ingo@298: import org.jfree.data.xy.XYSeries;
ingo@298: import org.jfree.data.xy.XYSeriesCollection;
ingo@298: 
ingo@298: /**
ingo@767:  * This class is used to create xy charts of vertical profiles.
ingo@767:  *
ingo@767:  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
ingo@298:  */
ingo@298: public class VerticalProfileChart
ingo@298: extends      AbstractXYLineChart
ingo@298: {
ingo@767:     /**
ingo@767:      * Default axis identifier which is used if @see #getDependendAxisName does
ingo@767:      * not return a value. The value of this field is {@value}.
ingo@767:      */
ingo@530:     public static final String DEFAULT_AXIS = "KPOSITION";
ingo@530: 
ingo@767:     /**
ingo@767:      * Logger used for logging with log4j.
ingo@767:      */
ingo@298:     private static Logger log = Logger.getLogger(VerticalProfileChart.class);
ingo@298: 
ingo@767:     /**
ingo@767:      * Constant used for gap detection. Its value is {@value}.
ingo@767:      */
ingo@831:     protected static int PERCENTAGE     = 5;
ingo@767: 
ingo@767:     /**
ingo@815:      * Constnat used for gap detection in @see #gridDetection.
ingo@767:      */
ingo@340:     protected final double GAP_MAX_LEVEL  = Math.sqrt(2.0);
ingo@767: 
ingo@767:     /**
ingo@767:      * Constant used for gap detection in @see #addGaps. Its value is {@value}.
ingo@767:      */
ingo@340:     protected final int    GAP_MAX_VALUES = 60;
ingo@340: 
ingo@767:     /**
sascha@778:      * Map to store max ranges of each parameter
sascha@778:      * (org.jfree.chart.axis.Axis.setAutoRange(true) doesn't seem to work
ingo@767:      * properly.
ingo@767:      */
ingo@656:     protected Map values;
ingo@656: 
ingo@831:     static {
ingo@831:         /* The percentage defining the width of a gap should be configured in 
ingo@831:          * conf.xml instead of being configured in a system property */
ingo@831:         PERCENTAGE = Integer.getInteger("chart.gap.percentage", PERCENTAGE);
ingo@831:     }
ingo@831: 
ingo@298: 
ingo@767:     /**
ingo@767:      * Constructor used to create xy-charts.
ingo@767:      *
ingo@767:      * @param labels Labels used to be displayed in title, subtitle and so on.
ingo@767:      * @param theme ChartTheme used to adjust the rendering of this chart.
ingo@767:      * @param parameters Collection containing a bunch of parameters.
ingo@767:      * @param measurements Collection containing a bunch of measurements.
ingo@767:      * @param dates Collection containing a bunch of date objects.
ingo@767:      * @param result Collection containing a bunch of <code>Result</code>
ingo@767:      * objects which contain the actual data items to be displayed.
ingo@767:      * @param timeGaps Collection with timegap definitions.
ingo@767:      * @param locale Locale used to specify the format of labels, numbers, ...
ingo@767:      * @param linesVisible Render lines between data points if true, otherwise
ingo@767:      * not.
ingo@767:      * @param shapesVisible Render vertices as points if true, otherwise not.
ingo@767:      */
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@656:         this.values           = new HashMap();
ingo@298:     }
ingo@298: 
ingo@298: 
ingo@767:     /**
ingo@767:      * @see de.intevation.gnv.chart.AbstractXYLineChart#initData()
ingo@767:      */
ingo@767:     @Override
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@656:             Object x = getValue(row);
ingo@656:             Double y = row.getDouble("YORDINATE");
ingo@656:             if (x != null && y != null) {
ingo@656:                 storeMaxRange(ranges, y, parameter);
ingo@656:                 storeMaxValue(values, x, parameter);
ingo@656:             }
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@767:     /**
ingo@767:      * Extract the important value from <code>Result</code> object.
ingo@767:      *
ingo@767:      * @param row <code>Result</code> object which contains a required value.
ingo@767:      *
ingo@767:      * @return X-ordinate
ingo@767:      */
ingo@656:     protected Object getValue(Result row) {
ingo@656:         return row.getDouble("XORDINATE");
ingo@656:     }
ingo@656: 
ingo@656: 
ingo@767:     /**
ingo@767:      * General method to start a gap detection. The switch between standard gap
ingo@767:      * detection method <code>addGaps</code> and a specialized method
ingo@767:      * <code>addGapsOnGrid</code> is done by a parameter <code>DATEID</code>
ingo@767:      * which is stored a each <code>Result</code> object. Specialized method is
ingo@767:      * used if <code>DATEID</code> equals 2, otherwise the standard method is
ingo@767:      * used.
ingo@767:      *
ingo@767:      * @param results Array of <code>Result</code> objects storing data of
ingo@767:      * this chart.
ingo@767:      * @param series Series used to add gaps.
ingo@767:      * @param startPos Index of first element of series in results.
ingo@767:      * @param endPos Index of last element of series in results.
ingo@767:      */
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@767:     /**
ingo@767:      * Method to expand max range of a range axis identified by seriesKey.
ingo@767:      * <code>LOWER_MARGIN</code> and <code>UPPER_MARGIN</code> are used to
ingo@767:      * expand the range.
ingo@767:      *
ingo@767:      * @param seriesKey Key to identify the series stored at the current
ingo@767:      * Dataset.
ingo@767:      * @param idx Currently not used.
ingo@767:      */
ingo@656:     protected void prepareRangeAxis(String seriesKey, int idx) {
ingo@656:         XYPlot plot      = chart.getXYPlot();
ingo@656:         NumberAxis xAxis = (NumberAxis) plot.getDomainAxis();
ingo@656: 
ingo@656:         Range xRange     = (Range) values.get(seriesKey);
ingo@656:         xAxis.setRange(Range.expand(xRange, LOWER_MARGIN, UPPER_MARGIN));
ingo@656:         log.debug("Max X-Range of dataset is: " + xRange.toString());
ingo@656:     }
ingo@656: 
ingo@656: 
ingo@767:     /**
ingo@767:      * @see de.intevation.gnv.chart.AbstractXYLineChart#addValue(Result, Series)
ingo@767:      */
ingo@767:     @Override
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@767:     /**
sascha@803:      * @param parameter
ingo@767:      * @see de.intevation.gnv.chart.AbstractXYLineChart#addSeries(Series, String,
ingo@767:      * int)
ingo@767:      */
ingo@767:     @Override
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@767:     /**
ingo@767:      * Method to add processed datasets to plot. Each dataset is adjusted using
ingo@767:      * <code>prepareAxis</code> and <code>adjustRenderer</code> methods.
ingo@767:      */
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@656:                 prepareRangeAxis(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@767:     /**
ingo@767:      * Method used to store the max y-range of each parameter in this chart.
ingo@767:      *
ingo@767:      * @param values Map to store max values for each parameter.
ingo@767:      * @param val Value used to be a Double.
ingo@767:      * @param parameter Title used to identify a range object stored in values.
ingo@767:      */
ingo@656:     protected void storeMaxValue(Map values, Object val, String parameter) {
ingo@656:         double value = ((Double) val).doubleValue();
ingo@656:         Range  range = null;
ingo@656: 
ingo@656:         range = values.containsKey(parameter)
ingo@656:             ? (Range) values.get(parameter)
ingo@656:             : new Range(value, value);
ingo@656: 
ingo@656:         double lower = range.getLowerBound();
ingo@656:         double upper = range.getUpperBound();
ingo@656: 
ingo@656:         lower = value < lower ? value : lower;
ingo@656:         upper = value > upper ? value : upper;
ingo@656: 
ingo@656:         values.put(parameter, new Range(lower, upper));
ingo@656:     }
ingo@656: 
ingo@656: 
ingo@767:     /**
ingo@788:      * @param locale
ingo@767:      * @see de.intevation.gnv.chart.AbstractXYLineChart#localizeDomainAxis(Axis,
ingo@767:      * Locale)
ingo@767:      */
ingo@767:     @Override
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@767:     /**
ingo@767:      * @see de.intevation.gnv.chart.AbstractXYLineChart#createSeriesName(String,
ingo@767:      * String, String)
ingo@767:      */
ingo@767:     @Override
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@767:     /**
ingo@767:      * Method used to add gaps between data points on grids. The real detection
ingo@767:      * is done in <code>gridDetection</code>.
ingo@767:      *
ingo@767:      * @param results Array of <code>Result</code> objects storing the relevant
ingo@767:      * values.
ingo@767:      * @param series Series object where the gaps are added to.
ingo@767:      * @param startPos Index of first element which should be used in gap
ingo@767:      * detection. Other series are stored in results as well.
ingo@767:      * @param endPos Index of last element which should be used in gap
ingo@767:      * detection.
ingo@767:      */
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;
sascha@778: 
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@767:     /**
ingo@767:      * Standarad method to add gaps. There are two different methods to detect
ingo@767:      * gaps. <code>simpleDetection</code> is used if the number of data points
ingo@767:      * in this chart is lower than <code>GAP_MAX_VALUES</code>. Otherwise
ingo@767:      * <code>specialDetection</code> is used. A data point with
ingo@767:      * <code>null</code> value is added where a gap should be. This lets
ingo@767:      * JFreeChart break the current graph.
ingo@767:      *
ingo@767:      * @param results Array of <code>Result</code> objects storing the relevant
ingo@767:      * values.
ingo@767:      * @param series Series object where the gaps are added to.
ingo@767:      * @param startValue First data point value in series.
ingo@767:      * @param endValue Last data point value in series.
ingo@767:      * @param startPos Index of first data point in results which contains all
ingo@767:      * data points of all series.
ingo@767:      * @param endPos Index of last data point in results which contains all data
ingo@767:      * points of all series.
ingo@767:      */
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@767:     /**
sascha@778:      * Simple method to detect gaps. A gap is detected if the delta between two
sascha@778:      * data points (current, last) is bigger than <code>PERCENTAGE</code> percent
sascha@778:      * of delta of start and end.
ingo@767:      * <br>
ingo@767:      * (smallDelta &gt; delta / 100 * PERCENTAGE)
ingo@767:      *
ingo@767:      * @param start First data point value in a series.
ingo@767:      * @param end Last data point value in a series.
ingo@767:      * @param last Left value
ingo@767:      * @param current Right value
ingo@767:      *
ingo@767:      * @return true, if a gap is detected between last and current - otherwise
ingo@767:      * false.
ingo@767:      */
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@767:     /**
ingo@767:      * Method to detect gaps between two data points. Following formula is used
ingo@767:      * for detection:<br>
ingo@767:      * smallDelta &gt; (3.0 / (count - 1) * delta)<br>
ingo@767:      * smallDelta = current - last<br>
ingo@767:      * delta      = end - start
ingo@767:      *
ingo@767:      * @param start First data point value in a series.
ingo@767:      * @param end Last data point value in a series.
ingo@767:      * @param last Left value
ingo@767:      * @param current Right value
ingo@767:      *
ingo@788:      * @param count
ingo@767:      * @return true, if a gap is detected between last and current - otherwise
ingo@767:      * false.
ingo@767:      */
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: 
sascha@778: 
ingo@767:     /**
ingo@767:      * Method used to detect gaps between two data points grids. If the delta
ingo@767:      * between current and last is bigger than <code>GAP_MAX_LEVEL</code>, a gap
ingo@767:      * is detected.
ingo@767:      *
ingo@767:      * @param last Left value
ingo@767:      * @param current Right value
ingo@767:      *
ingo@767:      * @return True, if a gap was detected - otherwise false.
ingo@767:      */
ingo@340:     protected boolean gridDetection(double last, double current) {
ingo@643:         if (log.isDebugEnabled()) {
ingo@643:             log.debug("######################################################");
ingo@643:             log.debug("Parameters for gap detection");
ingo@643:             log.debug("Defined gap size for grids: " + GAP_MAX_LEVEL);
ingo@643:             log.debug("1st value to compare: " + last);
ingo@643:             log.debug("2nd value to compare: " + current);
ingo@643:             log.debug("Difference: " + Math.abs(current - last));
ingo@643:         }
ingo@340:         return (Math.abs(current - last) > GAP_MAX_LEVEL);
ingo@340:     }
ingo@340: 
ingo@340: 
ingo@767:     /**
ingo@767:      * This method returns the key which is used to retrieve the y-value served
sascha@778:      * by a <code>Result</code> object.
ingo@767:      *
ingo@767:      * @param first <code>Result</code> object - not used in this class.
ingo@767:      * @param second <code>Result</code> object - not used in this class.
ingo@767:      *
ingo@767:      * @return the string "KPOSITION"
ingo@767:      */
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 :