changeset 7116:3c7471b929d1

Merge branch generator-refectoring into default.
author Andre Heinecke <aheinecke@intevation.de>
date Tue, 24 Sep 2013 12:29:44 +0200
parents bdadffad35b1 (current diff) a0b00cfe1799 (diff)
children 4605d6ca4ee9
files artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java
diffstat 76 files changed, 5203 insertions(+), 1252 deletions(-) [+]
line wrap: on
line diff
--- a/artifacts/doc/conf/conf.xml	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/doc/conf/conf.xml	Tue Sep 24 12:29:44 2013 +0200
@@ -7,6 +7,7 @@
     <!ENTITY rest-server SYSTEM "rest-server.xml">
     <!ENTITY floodmap    SYSTEM "floodmap.xml">
     <!ENTITY rivermap	 SYSTEM "rivermap.xml">
+    <!ENTITY generators  SYSTEM "generators.xml">
 ]>
 <artifact-database>
     <export-secret>YOUR_SECRET</export-secret>
@@ -284,104 +285,7 @@
         </hook>
     </hooks>
 
-    <output-generators>
-        <output-generator name="discharge_curve">org.dive4elements.river.exports.DischargeCurveGenerator</output-generator>
-        <output-generator name="discharge_curve_chartinfo">org.dive4elements.river.exports.DischargeCurveInfoGenerator</output-generator>
-        <output-generator name="cross_section">org.dive4elements.river.exports.CrossSectionGenerator</output-generator>
-        <output-generator name="cross_section_chartinfo">org.dive4elements.river.exports.CrossSectionInfoGenerator</output-generator>
-        <output-generator name="computed_discharge_curve">org.dive4elements.river.exports.ComputedDischargeCurveGenerator</output-generator>
-        <output-generator name="computed_discharge_curve_chartinfo">org.dive4elements.river.exports.ComputedDischargeCurveInfoGenerator</output-generator>
-        <output-generator name="longitudinal_section">org.dive4elements.river.exports.LongitudinalSectionGenerator</output-generator>
-        <output-generator name="longitudinal_section_chartinfo">org.dive4elements.river.exports.LongitudinalSectionInfoGenerator</output-generator>
-        <output-generator name="duration_curve">org.dive4elements.river.exports.DurationCurveGenerator</output-generator>
-        <output-generator name="duration_curve_chartinfo">org.dive4elements.river.exports.DurationCurveInfoGenerator</output-generator>
-        <output-generator name="discharge_longitudinal_section">org.dive4elements.river.exports.DischargeLongitudinalSectionGenerator</output-generator>
-        <output-generator name="discharge_longitudinal_section_chartinfo">org.dive4elements.river.exports.DischargeLongitudinalSectionInfoGenerator</output-generator>
-        <output-generator name="waterlevel_export">org.dive4elements.river.exports.WaterlevelExporter</output-generator>
-        <output-generator name="extreme_wq_curve">org.dive4elements.river.exports.extreme.ExtremeWQCurveGenerator</output-generator>
-        <output-generator name="extreme_curve_export">org.dive4elements.river.exports.WaterlevelExporter</output-generator>
-        <output-generator name="extreme_wq_curve_chartinfo">org.dive4elements.river.exports.extreme.ExtremeWQCurveInfoGenerator</output-generator>
-        <output-generator name="fix_wq_curve">org.dive4elements.river.exports.fixings.FixWQCurveGenerator</output-generator>
-        <output-generator name="fix_wq_curve_chartinfo">org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator</output-generator>
-        <output-generator name="durationcurve_export">org.dive4elements.river.exports.DurationCurveExporter</output-generator>
-        <output-generator name="computed_dischargecurve_export">org.dive4elements.river.exports.ComputedDischargeCurveExporter</output-generator>
-        <output-generator name="discharge_longitudinal_section_export">org.dive4elements.river.exports.DischargeLongitudinalSectionExporter</output-generator>
-        <output-generator name="w_differences">org.dive4elements.river.exports.WDifferencesCurveGenerator</output-generator>
-        <output-generator name="w_differences_chartinfo">org.dive4elements.river.exports.WDifferencesCurveInfoGenerator</output-generator>
-        <output-generator name="w_differences_export">org.dive4elements.river.exports.WDifferencesExporter</output-generator>
-        <output-generator name="floodmap">org.dive4elements.river.exports.MapGenerator</output-generator>
-        <output-generator name="map">org.dive4elements.river.exports.MapGenerator</output-generator>
-        <output-generator name="reference_curve">org.dive4elements.river.exports.ReferenceCurveGenerator</output-generator>
-        <output-generator name="reference_curve_normalized">org.dive4elements.river.exports.NormalizedReferenceCurveGenerator</output-generator>
-        <output-generator name="reference_curve_normalized_chartinfo">org.dive4elements.river.exports.NormalizedReferenceCurveInfoGenerator</output-generator>
-        <output-generator name="reference_curve_chartinfo">org.dive4elements.river.exports.ReferenceCurveInfoGenerator</output-generator>
-        <output-generator name="reference_curve_export">org.dive4elements.river.exports.ReferenceCurveExporter</output-generator>
-        <output-generator name="historical_discharge">org.dive4elements.river.exports.HistoricalDischargeCurveGenerator</output-generator>
-        <output-generator name="historical_discharge_chartinfo">org.dive4elements.river.exports.HistoricalDischargeCurveInfoGenerator</output-generator>
-        <output-generator name="historical_discharge_wq">org.dive4elements.river.exports.HistoricalDischargeWQCurveGenerator</output-generator>
-        <output-generator name="historical_discharge_wq_chartinfo">org.dive4elements.river.exports.HistoricalDischargeWQCurveInfoGenerator</output-generator>
-        <output-generator name="historical_discharge_export">org.dive4elements.river.exports.HistoricalDischargeCurveExporter</output-generator>
-        <output-generator name="flow_velocity">org.dive4elements.river.exports.FlowVelocityGenerator</output-generator>
-        <output-generator name="flow_velocity_chartinfo">org.dive4elements.river.exports.FlowVelocityInfoGenerator</output-generator>
-        <output-generator name="flow_velocity_export">org.dive4elements.river.exports.FlowVelocityExporter</output-generator>
-        <output-generator name="bedheight_middle">org.dive4elements.river.exports.MiddleBedHeightGenerator</output-generator>
-        <output-generator name="bedheight_middle_chartinfo">org.dive4elements.river.exports.MiddleBedHeightInfoGenerator</output-generator>
-        <output-generator name="bedheight_middle_export">org.dive4elements.river.exports.MiddleBedHeightExporter</output-generator>
-        <output-generator name="bed_longitudinal_section">org.dive4elements.river.exports.minfo.BedQualityGenerator</output-generator>
-        <output-generator name="bed_longitudinal_section_chartinfo">org.dive4elements.river.exports.minfo.BedQualityInfoGenerator</output-generator>
-        <output-generator name="bed_quality_export">org.dive4elements.river.exports.minfo.BedQualityExporter</output-generator>
-        <output-generator name="bed_difference_year">org.dive4elements.river.exports.minfo.BedDifferenceYearGenerator</output-generator>
-        <output-generator name="bed_difference_year_chartinfo">org.dive4elements.river.exports.minfo.BedDiffYearInfoGenerator</output-generator>
-        <output-generator name="bed_difference_epoch">org.dive4elements.river.exports.minfo.BedDifferenceEpochGenerator</output-generator>
-        <output-generator name="bed_difference_epoch_chartinfo">org.dive4elements.river.exports.minfo.BedDiffEpochInfoGenerator</output-generator>
-        <output-generator name="bed_difference_height_year">org.dive4elements.river.exports.minfo.BedDiffHeightYearGenerator</output-generator>
-        <output-generator name="bed_difference_height_year_chartinfo">org.dive4elements.river.exports.minfo.BedDiffHeightYearInfoGenerator</output-generator>
-        <output-generator name="bedheight_difference_export">org.dive4elements.river.exports.minfo.BedDifferenceExporter</output-generator>
-        <output-generator name="sq_relation_a">org.dive4elements.river.exports.sq.SQRelationGeneratorA</output-generator>
-        <output-generator name="sq_relation_b">org.dive4elements.river.exports.sq.SQRelationGeneratorB</output-generator>
-        <output-generator name="sq_relation_c">org.dive4elements.river.exports.sq.SQRelationGeneratorC</output-generator>
-        <output-generator name="sq_relation_d">org.dive4elements.river.exports.sq.SQRelationGeneratorD</output-generator>
-        <output-generator name="sq_relation_e">org.dive4elements.river.exports.sq.SQRelationGeneratorE</output-generator>
-        <output-generator name="sq_relation_f">org.dive4elements.river.exports.sq.SQRelationGeneratorF</output-generator>
-        <output-generator name="sq_relation_a_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_b_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_c_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_d_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_e_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_f_chartinfo">org.dive4elements.river.exports.sq.SQRelationInfoGenerator</output-generator>
-        <output-generator name="sq_relation_export">org.dive4elements.river.exports.sq.SQRelationExporter</output-generator>
-        <output-generator name="sq_overview">org.dive4elements.river.exports.sq.SQOverviewGenerator</output-generator>
-        <output-generator name="fix_parameters_export">org.dive4elements.river.exports.fixings.ParametersExporter</output-generator>
-        <output-generator name="fix_deltawt_export">org.dive4elements.river.exports.fixings.DeltaWtExporter</output-generator>
-        <output-generator name="fix_deltawt_curve">org.dive4elements.river.exports.fixings.FixDeltaWtGenerator</output-generator>
-        <output-generator name="fix_deltawt_curve_chartinfo">org.dive4elements.river.exports.fixings.FixDeltaWtInfoGenerator</output-generator>
-        <output-generator name="fix_longitudinal_section_curve">org.dive4elements.river.exports.fixings.FixLongitudinalSectionGenerator</output-generator>
-        <output-generator name="fix_longitudinal_section_curve_chartinfo">org.dive4elements.river.exports.fixings.FixLongitudinalSectionInfoGenerator</output-generator>
-        <output-generator name="fix_derivate_curve">org.dive4elements.river.exports.fixings.FixDerivedCurveGenerator</output-generator>
-        <output-generator name="fix_derivate_curve_chartinfo">org.dive4elements.river.exports.fixings.FixDerivedCurveInfoGenerator</output-generator>
-        <output-generator name="fix_waterlevel_export">org.dive4elements.river.exports.WaterlevelExporter</output-generator>
-        <output-generator name="fix_vollmer_wq_curve">org.dive4elements.river.exports.fixings.FixWQCurveGenerator</output-generator>
-        <output-generator name="fix_vollmer_wq_curve_chartinfo">org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator</output-generator>
-        <output-generator name="sedimentload_ls">org.dive4elements.river.exports.minfo.SedimentLoadLSGenerator</output-generator>
-        <output-generator name="sedimentload_ls_export">org.dive4elements.river.exports.minfo.SedimentLoadExporter</output-generator>
-        <output-generator name="sedimentload_ls_chartinfo">org.dive4elements.river.exports.minfo.SedimentLoadLSInfoGenerator</output-generator>
-        <!-- Error report generators. -->
-        <output-generator name="discharge_longitudinal_section_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="waterlevel_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="computed_dischargecurve_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="durationcurve_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="wsplgen_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="historical_discharge_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="reference_curve_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="fix_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="extreme_curve_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <output-generator name="sedimentload_ls_report">org.dive4elements.river.exports.ReportGenerator</output-generator>
-        <!-- AT exporter. -->
-        <output-generator name="computed_dischargecurve_at_export">org.dive4elements.river.exports.ATExporter</output-generator>
-        <output-generator name="gauge_discharge_curve_at_export">org.dive4elements.river.exports.ATExporter</output-generator>
-        <output-generator name="fix_wq_curve_at_export">org.dive4elements.river.exports.fixings.FixATExport</output-generator>
-        <output-generator name="wsplgen">org.dive4elements.river.exports.ShapeExporter</output-generator>
-    </output-generators>
+    &generators;
 
     <!-- Path to the template file of the meta data. -->
     <metadata>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/doc/conf/generators.xml	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,146 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<output-generators>
+    <output-generator names="discharge_curve" class="org.dive4elements.river.exports.DischargeCurveGenerator"/>
+    <output-generator names="discharge_curve_chartinfo" class="org.dive4elements.river.exports.DischargeCurveInfoGenerator"/>
+    <output-generator names="cross_section" class="org.dive4elements.river.exports.CrossSectionGenerator"/>
+    <output-generator names="cross_section_chartinfo" class="org.dive4elements.river.exports.CrossSectionInfoGenerator"/>
+    <output-generator names="computed_discharge_curve" class="org.dive4elements.river.exports.ComputedDischargeCurveGenerator"/>
+    <output-generator names="computed_discharge_curve_chartinfo" class="org.dive4elements.river.exports.ComputedDischargeCurveInfoGenerator"/>
+    <output-generator names="longitudinal_section,longitudinal_section_chartinfo,discharge_longitudinal_section,discharge_longitudinal_section_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <axis name="W"/>
+        <axis name="D"/>
+        <axis name="Q" include-zero="true"/>
+        <axis name="" type="X"/>
+        <title key="chart.longitudinal.section.title" default="W-Längsschnitt"/>
+        <subtitle key="chart.longitudinal.section.subtitle" default="-">
+            <arg expr="artifact.river"/>
+            <arg expr="artifact.ld_from" type="double"/>
+            <arg expr="artifact.ld_to" type="double"/>
+        </subtitle>
+        <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km">
+            <arg expr="artifact.river"/>
+        </domain-axis>
+        <processor class="org.dive4elements.river.exports.process.AnnotationProcessor" axis="none"/>
+        <processor class="org.dive4elements.river.exports.process.WOutProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.WDiffProcessor" axis="D"/>
+        <processor class="org.dive4elements.river.exports.process.QOutProcessor" axis="Q"/>
+        <processor class="org.dive4elements.river.exports.process.BedheightProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.BedDiffYearProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.BedDiffHeightYearProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/>
+        <!-- The area processor decides based on the facet name which axis to choose.
+             You have to ensure that there is a W and Q axis accordingly -->
+        <processor class="org.dive4elements.river.exports.process.AreaProcessor" axis="none"/>
+    </output-generator>
+    <output-generator names="w_differences,w_differences_chartinfo"
+        class="org.dive4elements.river.exports.DiagramGenerator"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <axis name="W"/>
+        <axis name="D"/>
+        <axis name="Q" include-zero="true"/>
+        <axis name="" type="X"/>
+        <title key="chart.w_differences.title" default="Differences"/>
+        <subtitle key="chart.w_differences.subtitle" default="-">
+            <arg expr="artifact.river"/>
+        </subtitle>
+        <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km">
+            <arg expr="artifact.river"/>
+        </domain-axis>
+        <processor class="org.dive4elements.river.exports.process.AnnotationProcessor" axis="none"/>
+        <processor class="org.dive4elements.river.exports.process.WOutProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.WDiffProcessor" axis="D"/>
+        <processor class="org.dive4elements.river.exports.process.QOutProcessor" axis="Q"/>
+        <processor class="org.dive4elements.river.exports.process.BedheightProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.BedDiffYearProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.BedDiffHeightYearProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/>
+        <processor class="org.dive4elements.river.exports.process.AreaProcessor" axis="none"/>
+    </output-generator>
+    <output-generator names="duration_curve" class="org.dive4elements.river.exports.DurationCurveGenerator"/>
+    <output-generator names="duration_curve_chartinfo" class="org.dive4elements.river.exports.DurationCurveInfoGenerator"/>
+    <output-generator names="waterlevel_export" class="org.dive4elements.river.exports.WaterlevelExporter"/>
+    <output-generator names="extreme_wq_curve" class="org.dive4elements.river.exports.extreme.ExtremeWQCurveGenerator"/>
+    <output-generator names="extreme_curve_export" class="org.dive4elements.river.exports.WaterlevelExporter"/>
+    <output-generator names="extreme_wq_curve_chartinfo" class="org.dive4elements.river.exports.extreme.ExtremeWQCurveInfoGenerator"/>
+    <output-generator names="fix_wq_curve" class="org.dive4elements.river.exports.fixings.FixWQCurveGenerator"/>
+    <output-generator names="fix_wq_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator"/>
+    <output-generator names="durationcurve_export" class="org.dive4elements.river.exports.DurationCurveExporter"/>
+    <output-generator names="computed_dischargecurve_export" class="org.dive4elements.river.exports.ComputedDischargeCurveExporter"/>
+    <output-generator names="discharge_longitudinal_section_export" class="org.dive4elements.river.exports.DischargeLongitudinalSectionExporter"/>
+    <output-generator names="w_differences_export" class="org.dive4elements.river.exports.WDifferencesExporter"/>
+    <output-generator names="floodmap" class="org.dive4elements.river.exports.MapGenerator"/>
+    <output-generator names="map" class="org.dive4elements.river.exports.MapGenerator"/>
+    <output-generator names="reference_curve" class="org.dive4elements.river.exports.ReferenceCurveGenerator"/>
+    <output-generator names="reference_curve_normalized" class="org.dive4elements.river.exports.NormalizedReferenceCurveGenerator"/>
+    <output-generator names="reference_curve_normalized_chartinfo" class="org.dive4elements.river.exports.NormalizedReferenceCurveInfoGenerator"/>
+    <output-generator names="reference_curve_chartinfo" class="org.dive4elements.river.exports.ReferenceCurveInfoGenerator"/>
+    <output-generator names="reference_curve_export" class="org.dive4elements.river.exports.ReferenceCurveExporter"/>
+    <output-generator names="historical_discharge" class="org.dive4elements.river.exports.HistoricalDischargeCurveGenerator"/>
+    <output-generator names="historical_discharge_chartinfo" class="org.dive4elements.river.exports.HistoricalDischargeCurveInfoGenerator"/>
+    <output-generator names="historical_discharge_wq" class="org.dive4elements.river.exports.HistoricalDischargeWQCurveGenerator"/>
+    <output-generator names="historical_discharge_wq_chartinfo" class="org.dive4elements.river.exports.HistoricalDischargeWQCurveInfoGenerator"/>
+    <output-generator names="historical_discharge_export" class="org.dive4elements.river.exports.HistoricalDischargeCurveExporter"/>
+    <output-generator names="flow_velocity" class="org.dive4elements.river.exports.FlowVelocityGenerator"/>
+    <output-generator names="flow_velocity_chartinfo" class="org.dive4elements.river.exports.FlowVelocityInfoGenerator"/>
+    <output-generator names="flow_velocity_export" class="org.dive4elements.river.exports.FlowVelocityExporter"/>
+    <output-generator names="bedheight_middle" class="org.dive4elements.river.exports.MiddleBedHeightGenerator"/>
+    <output-generator names="bedheight_middle_chartinfo" class="org.dive4elements.river.exports.MiddleBedHeightInfoGenerator"/>
+    <output-generator names="bedheight_middle_export" class="org.dive4elements.river.exports.MiddleBedHeightExporter"/>
+    <output-generator names="bed_longitudinal_section" class="org.dive4elements.river.exports.minfo.BedQualityGenerator"/>
+    <output-generator names="bed_longitudinal_section_chartinfo" class="org.dive4elements.river.exports.minfo.BedQualityInfoGenerator"/>
+    <output-generator names="bed_quality_export" class="org.dive4elements.river.exports.minfo.BedQualityExporter"/>
+    <output-generator names="bed_difference_year" class="org.dive4elements.river.exports.minfo.BedDifferenceYearGenerator"/>
+    <output-generator names="bed_difference_year_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffYearInfoGenerator"/>
+    <output-generator names="bed_difference_epoch" class="org.dive4elements.river.exports.minfo.BedDifferenceEpochGenerator"/>
+    <output-generator names="bed_difference_epoch_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffEpochInfoGenerator"/>
+    <output-generator names="bed_difference_height_year" class="org.dive4elements.river.exports.minfo.BedDiffHeightYearGenerator"/>
+    <output-generator names="bed_difference_height_year_chartinfo" class="org.dive4elements.river.exports.minfo.BedDiffHeightYearInfoGenerator"/>
+    <output-generator names="bedheight_difference_export" class="org.dive4elements.river.exports.minfo.BedDifferenceExporter"/>
+    <output-generator names="sq_relation_a" class="org.dive4elements.river.exports.sq.SQRelationGeneratorA"/>
+    <output-generator names="sq_relation_b" class="org.dive4elements.river.exports.sq.SQRelationGeneratorB"/>
+    <output-generator names="sq_relation_c" class="org.dive4elements.river.exports.sq.SQRelationGeneratorC"/>
+    <output-generator names="sq_relation_d" class="org.dive4elements.river.exports.sq.SQRelationGeneratorD"/>
+    <output-generator names="sq_relation_e" class="org.dive4elements.river.exports.sq.SQRelationGeneratorE"/>
+    <output-generator names="sq_relation_f" class="org.dive4elements.river.exports.sq.SQRelationGeneratorF"/>
+    <output-generator names="sq_relation_a_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_b_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_c_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_d_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_e_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_f_chartinfo" class="org.dive4elements.river.exports.sq.SQRelationInfoGenerator"/>
+    <output-generator names="sq_relation_export" class="org.dive4elements.river.exports.sq.SQRelationExporter"/>
+    <output-generator names="sq_overview" class="org.dive4elements.river.exports.sq.SQOverviewGenerator"/>
+    <output-generator names="fix_parameters_export" class="org.dive4elements.river.exports.fixings.ParametersExporter"/>
+    <output-generator names="fix_deltawt_export" class="org.dive4elements.river.exports.fixings.DeltaWtExporter"/>
+    <output-generator names="fix_deltawt_curve" class="org.dive4elements.river.exports.fixings.FixDeltaWtGenerator"/>
+    <output-generator names="fix_deltawt_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixDeltaWtInfoGenerator"/>
+    <output-generator names="fix_longitudinal_section_curve" class="org.dive4elements.river.exports.fixings.FixLongitudinalSectionGenerator"/>
+    <output-generator names="fix_longitudinal_section_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixLongitudinalSectionInfoGenerator"/>
+    <output-generator names="fix_derivate_curve" class="org.dive4elements.river.exports.fixings.FixDerivedCurveGenerator"/>
+    <output-generator names="fix_derivate_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixDerivedCurveInfoGenerator"/>
+    <output-generator names="fix_waterlevel_export" class="org.dive4elements.river.exports.WaterlevelExporter"/>
+    <output-generator names="fix_vollmer_wq_curve" class="org.dive4elements.river.exports.fixings.FixWQCurveGenerator"/>
+    <output-generator names="fix_vollmer_wq_curve_chartinfo" class="org.dive4elements.river.exports.fixings.FixWQCurveInfoGenerator"/>
+    <output-generator names="sedimentload_ls" class="org.dive4elements.river.exports.minfo.SedimentLoadLSGenerator"/>
+    <output-generator names="sedimentload_ls_export" class="org.dive4elements.river.exports.minfo.SedimentLoadExporter"/>
+    <output-generator names="sedimentload_ls_chartinfo" class="org.dive4elements.river.exports.minfo.SedimentLoadLSInfoGenerator"/>
+    <!-- Error report generators. -->
+    <output-generator names="discharge_longitudinal_section_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="waterlevel_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="computed_dischargecurve_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="durationcurve_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="wsplgen_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="historical_discharge_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="reference_curve_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="fix_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="extreme_curve_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <output-generator names="sedimentload_ls_report" class="org.dive4elements.river.exports.ReportGenerator"/>
+    <!-- AT exporter. -->
+    <output-generator names="computed_dischargecurve_at_export" class="org.dive4elements.river.exports.ATExporter"/>
+    <output-generator names="gauge_discharge_curve_at_export" class="org.dive4elements.river.exports.ATExporter"/>
+    <output-generator names="fix_wq_curve_at_export" class="org.dive4elements.river.exports.fixings.FixATExport"/>
+    <output-generator names="wsplgen" class="org.dive4elements.river.exports.ShapeExporter"/>
+</output-generators>
+
--- a/artifacts/pom.xml	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/pom.xml	Tue Sep 24 12:29:44 2013 +0200
@@ -75,9 +75,9 @@
       <version>1.0-SNAPSHOT</version>
     </dependency>
     <dependency>
-      <groupId>jfree</groupId>
+      <groupId>org.jfree</groupId>
       <artifactId>jfreechart</artifactId>
-      <version>1.0.13</version>
+      <version>1.0.15</version>
     </dependency>
     <dependency>
       <groupId>org.apache.xmlgraphics</groupId>
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java	Tue Sep 24 12:29:44 2013 +0200
@@ -17,6 +17,7 @@
 import org.dive4elements.artifactdatabase.DefaultArtifactContext;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.exports.OutGenerator;
+import org.dive4elements.river.utils.Pair;
 
 
 /**
@@ -99,17 +100,26 @@
             ? (RiverContext) context
             : (RiverContext) context.globalContext();
 
-        Map<String, Class> generators = (Map<String, Class>)
-            flysContext.get(RiverContext.OUTGENERATORS_KEY);
+        Map<String, Pair<Class<OutGenerator>, Object>> generators =
+            (Map<String, Pair<Class<OutGenerator>, Object>>)flysContext
+                .get(RiverContext.OUTGENERATORS_KEY);
 
         if (generators == null) {
             return null;
         }
 
-        Class clazz = generators.get(name);
+        Pair<Class<OutGenerator>, Object> pair = generators.get(name);
+
+        if (pair == null) {
+            logger.warn("No generator class found for " + name);
+            return null;
+        }
 
         try {
-            return clazz != null ? (OutGenerator) clazz.newInstance() : null;
+            Class<OutGenerator> clazz = pair.getA();
+            OutGenerator generator = clazz.newInstance();
+            generator.setup(pair.getB());
+            return generator;
         }
         catch (InstantiationException ie) {
             logger.error(ie, ie);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java	Tue Sep 24 12:29:44 2013 +0200
@@ -8,24 +8,8 @@
 
 package org.dive4elements.river.artifacts.context;
 
-import org.dive4elements.artifactdatabase.state.State;
-import org.dive4elements.artifactdatabase.state.StateEngine;
-import org.dive4elements.artifactdatabase.transition.Transition;
-import org.dive4elements.artifactdatabase.transition.TransitionEngine;
-import org.dive4elements.artifacts.ArtifactContextFactory;
-import org.dive4elements.artifacts.GlobalContext;
-import org.dive4elements.artifacts.common.utils.Config;
-import org.dive4elements.artifacts.common.utils.XMLUtils;
-import org.dive4elements.river.artifacts.model.Module;
-import org.dive4elements.river.artifacts.model.ZoomScale;
-import org.dive4elements.river.artifacts.states.StateFactory;
-import org.dive4elements.river.artifacts.transitions.TransitionFactory;
-import org.dive4elements.river.themes.Theme;
-import org.dive4elements.river.themes.ThemeFactory;
-import org.dive4elements.river.themes.ThemeGroup;
-import org.dive4elements.river.themes.ThemeMapping;
+import java.io.File;
 
-import java.io.File;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -34,12 +18,41 @@
 import javax.xml.xpath.XPathConstants;
 
 import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifactdatabase.state.StateEngine;
+
+import org.dive4elements.artifactdatabase.transition.Transition;
+import org.dive4elements.artifactdatabase.transition.TransitionEngine;
+
+import org.dive4elements.artifacts.ArtifactContextFactory;
+import org.dive4elements.artifacts.GlobalContext;
+
+import org.dive4elements.artifacts.common.utils.Config;
+import org.dive4elements.artifacts.common.utils.ElementConverter;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.model.Module;
+import org.dive4elements.river.artifacts.model.ZoomScale;
+
+import org.dive4elements.river.artifacts.states.StateFactory;
+
+import org.dive4elements.river.artifacts.transitions.TransitionFactory;
+
+import org.dive4elements.river.exports.OutGenerator;
+
+import org.dive4elements.river.themes.Theme;
+import org.dive4elements.river.themes.ThemeFactory;
+import org.dive4elements.river.themes.ThemeGroup;
+import org.dive4elements.river.themes.ThemeMapping;
+
+import org.dive4elements.river.utils.Pair;
+
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
-
 /**
  * The ArtifactContextFactory is used to initialize basic components and put
  * them into the global context of the application.
@@ -307,7 +320,6 @@
      * @param context the RiverContext.
      */
     protected void configureOutGenerators(Document config, RiverContext context){
-        Map<String, Class<?>> generators = new HashMap<String, Class<?>>();
 
         NodeList outGenerators = (NodeList) XMLUtils.xpath(
             config,
@@ -323,28 +335,60 @@
 
         logger.info("Found " + num + " configured output generators.");
 
+        Map<String, Pair<Class<OutGenerator>, Object>> generators =
+            new HashMap<String, Pair<Class<OutGenerator>, Object>>();
+
         int idx = 0;
 
         for (int i = 0; i < num; i++) {
-            Node item = outGenerators.item(i);
+            Element item = (Element)outGenerators.item(i);
 
-            String name = (String) XMLUtils.xpath(
-                item, "@name", XPathConstants.STRING);
+            String names = item.getAttribute("names").trim();
+            String clazz = item.getAttribute("class").trim();
+            String converter = item.getAttribute("converter").trim();
 
-            String clazz = (String) XMLUtils.xpath(
-                item, "text()", XPathConstants.STRING);
-
-            if (name == null || clazz == null) {
+            if (names.isEmpty() || clazz.isEmpty()) {
                 continue;
             }
 
+            Class<OutGenerator> generatorClass = null;
+
             try {
-                generators.put(name, Class.forName(clazz));
-
-                idx++;
+                generatorClass = (Class<OutGenerator>)Class.forName(clazz);
             }
             catch (ClassNotFoundException cnfe) {
-                logger.warn(cnfe, cnfe);
+                logger.error(cnfe, cnfe);
+                continue;
+            }
+
+            Object cfg = null;
+
+            if (!converter.isEmpty()) {
+                try {
+                    ElementConverter ec =
+                        (ElementConverter)Class.forName(converter)
+                            .newInstance();
+                    cfg = ec.convert(item);
+                }
+                catch (ClassNotFoundException cnfe) {
+                    logger.error(cnfe, cnfe);
+                }
+                catch (InstantiationException ie) {
+                    logger.error(ie);
+                }
+                catch (IllegalAccessException iae) {
+                    logger.error(iae);
+                }
+            }
+
+            Pair<Class<OutGenerator>, Object> pair =
+                new Pair<Class<OutGenerator>, Object>(generatorClass, cfg);
+
+            for (String key: names.split("[\\s,]")) {
+                if (!(key = key.trim()).isEmpty()) {
+                    generators.put(key, pair);
+                    idx++;
+                }
             }
         }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java	Tue Sep 24 12:29:44 2013 +0200
@@ -109,7 +109,7 @@
     }
 
     public boolean guessWaterIncreasing(float factor) {
-        return DataUtil.guessWaterIncreasing(ws, factor);
+        return DataUtil.guessDataIncreasing(ws, factor);
     }
 
     public int [] longestIncreasingWRangeIndices() {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java	Tue Sep 24 12:29:44 2013 +0200
@@ -24,5 +24,10 @@
     TDoubleArrayList allWs();
 
     boolean guessWaterIncreasing();
+
+    /** Guess if the Water flows from right to left.
+     *
+     * @return True if km's and ws's both grow in the same direction */
+    boolean guessRTLData();
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java	Tue Sep 24 12:29:44 2013 +0200
@@ -82,11 +82,16 @@
 
     @Override
     public boolean guessWaterIncreasing() {
-        return guessWaterIncreasing(0.05f);
+        return guessDataIncreasing(0.05f);
     }
 
-    protected boolean guessWaterIncreasing(float factor) {
-        return DataUtil.guessWaterIncreasing(ws, factor);
+    protected boolean guessDataIncreasing(float factor) {
+        return DataUtil.guessDataIncreasing(ws, factor);
+    }
+
+    @Override
+    public boolean guessRTLData() {
+        return DataUtil.guessSameDirectionData(ws, allKms());
     }
 
     @Override
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java	Tue Sep 24 12:29:44 2013 +0200
@@ -8,6 +8,7 @@
 
 package org.dive4elements.river.artifacts.model;
 
+import org.dive4elements.river.utils.DataUtil;
 import org.dive4elements.river.utils.DoubleUtil;
 
 import gnu.trove.TDoubleArrayList;
@@ -153,5 +154,10 @@
         /* Behold the first km might be larger then the last! */
         return new double[] {getKm(0), getKm(size()-1)};
     }
+
+    @Override
+    public boolean guessRTLData() {
+        return DataUtil.guessSameDirectionData(ws, allKms());
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java	Tue Sep 24 12:29:44 2013 +0200
@@ -77,7 +77,7 @@
         CALCULATION_DISCHARGE_CURVE,
         CALCULATION_HISTORICAL_DISCHARGE_CURVE,
         CALCULATION_DURATION_CURVE,
-//        CALCULATION_DISCHARGE_LONGITUDINAL_CURVE,
+        CALCULATION_DISCHARGE_LONGITUDINAL_CURVE,
         CALCULATION_W_DIFFERENCES,
         CALCULATION_REFERENCE_CURVE //,
 //        CALCULATION_EXTREME
--- a/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java	Tue Sep 24 12:29:44 2013 +0200
@@ -330,7 +330,7 @@
 
         // XXX NOTE: the outGen is not able to process its generate() operation,
         // because it has no OutputStream set!
-        outGen.init(XMLUtils.newDocument(), null, getContext());
+        outGen.init(out, XMLUtils.newDocument(), null, getContext());
         prepareMasterArtifact(outGen);
 
         try {
@@ -382,16 +382,12 @@
 
         // If type contains 'chartinfo' use a generator that
         // just allow access to width, height etc.
-        OutGenerator generator = null;
-        if (type != null
-             && type.length() > 0
-             && type.indexOf("chartinfo") > 0)
-        {
-            generator = RiverContext.getOutGenerator(context, type, subtype);
-        }
-        else {
-            generator = RiverContext.getOutGenerator(context, name, subtype);
-        }
+
+        String key = type != null && !type.isEmpty() && type.indexOf("chartinfo") > 0
+            ? type
+            : name;
+
+        OutGenerator generator = RiverContext.getOutGenerator(context, key, subtype);
 
         if (generator == null) {
             log.error("There is no generator specified for output: " + name);
@@ -417,7 +413,7 @@
             }
         }
 
-        generator.init(format, out, context);
+        generator.init(key, format, out, context);
         generator.setSettings(settings);
         generator.setCollection(this);
         prepareMasterArtifact(generator);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -44,7 +44,8 @@
     protected WQ           data;
     protected CallContext  context;
     protected OutputStream out;
-    protected D4EArtifact master;
+    protected D4EArtifact  master;
+    protected String       outName;
 
     protected D4EArtifactCollection collection;
 
@@ -53,7 +54,13 @@
     }
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void setup(Object config) {
+        logger.debug("ATExporter.setup");
+    }
+
+    @Override
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        this.outName = outName;
         this.context = context;
         this.out     = out;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -65,6 +65,9 @@
     /** XPath that points to the desired export facet. */
     public static final String XPATH_FACET = "/art:action/@art:type";
 
+    /** The out name to serve. */
+    protected String outName;
+
     /** The document of the incoming out() request. */
     protected Document request;
 
@@ -115,11 +118,21 @@
      */
     protected abstract void addData(Object data);
 
+    public void setup(Object config) {
+        logger.debug("AbstractExporter.setup");
+    }
+
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void init(
+        String       outName,
+        Document     request,
+        OutputStream out,
+        CallContext  context
+    ) {
         logger.debug("AbstractExporter.init");
 
+        this.outName = outName;
         this.request = request;
         this.out     = out;
         this.context = context;
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -30,6 +30,7 @@
 import org.dive4elements.river.jfree.Style;
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledSeries;
+import org.dive4elements.river.jfree.AxisDataset;
 import org.dive4elements.river.model.River;
 import org.dive4elements.river.themes.LineStyle;
 import org.dive4elements.river.themes.TextStyle;
@@ -136,6 +137,8 @@
     /** List of annotations to insert in plot. */
     protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>();
 
+    protected String outName;
+
     /**
      * A mini interface that allows to walk over the YAXIS enums defined in
      * subclasses.
@@ -149,26 +152,6 @@
 
 
 
-    public interface AxisDataset {
-
-        void addDataset(XYDataset dataset);
-
-        XYDataset[] getDatasets();
-
-        boolean isEmpty();
-
-        void setRange(Range range);
-
-        Range getRange();
-
-        boolean isArea(XYDataset dataset);
-
-        void setPlotAxisIndex(int idx);
-
-        int getPlotAxisIndex();
-
-    } // end of AxisDataset interface
-
 
 
     /**
@@ -178,6 +161,10 @@
         datasets = new TreeMap<Integer, AxisDataset>();
     }
 
+    @Override
+    public void setup(Object config) {
+        logger.debug("ChartGenerator.setup");
+    }
 
     /**
      * Adds annotations to list. The given annotation will be visible.
@@ -699,9 +686,10 @@
 
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
         logger.debug("ChartGenerator.init");
 
+        this.outName = outName;
         this.request = request;
         this.out     = out;
         this.context = context;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,1572 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Stroke;
+import java.awt.TexturePaint;
+import java.awt.Transparency;
+
+import java.awt.geom.Rectangle2D;
+
+import java.awt.image.BufferedImage;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.xml.xpath.XPathConstants;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Settings;
+
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.PreferredLocale;
+
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+
+import org.dive4elements.river.artifacts.D4EArtifact;
+
+import org.dive4elements.river.artifacts.access.RangeAccess;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+
+import org.dive4elements.river.collections.D4EArtifactCollection;
+
+import org.dive4elements.river.java2d.NOPGraphics2D;
+
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.EnhancedLineAndShapeRenderer;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.StableXYDifferenceRenderer;
+import org.dive4elements.river.jfree.Style;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledSeries;
+
+import org.dive4elements.river.model.River;
+
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.utils.Formatter;
+import org.dive4elements.river.utils.RiverUtils;
+
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+
+import org.jfree.chart.axis.NumberAxis;
+
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
+
+import org.jfree.chart.title.TextTitle;
+
+import org.jfree.data.Range;
+
+import org.jfree.data.general.Series;
+
+import org.jfree.data.xy.XYDataset;
+
+import org.jfree.ui.RectangleInsets;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Implementation of the OutGenerator interface for charts.
+ * It should provide some basic things that equal in all chart types.
+ *
+ */
+public abstract class ChartGenerator2 implements OutGenerator {
+
+    private static Logger logger = Logger.getLogger(ChartGenerator2.class);
+
+    public static final boolean USE_NOP_GRAPHICS =
+        Boolean.getBoolean("info.rendering.nop.graphics");
+
+
+    public static final int    DEFAULT_CHART_WIDTH     = 600;
+    public static final int    DEFAULT_CHART_HEIGHT    = 400;
+    public static final String DEFAULT_CHART_FORMAT    = "png";
+    public static final Color  DEFAULT_GRID_COLOR      = Color.GRAY;
+    public static final float  DEFAULT_GRID_LINE_WIDTH = 0.3f;
+    public static final int    DEFAULT_FONT_SIZE       = 12;
+    public static final String DEFAULT_FONT_NAME       = "Tahoma";
+
+
+    public static final String XPATH_CHART_SIZE =
+        "/art:action/art:attributes/art:size";
+
+    public static final String XPATH_CHART_FORMAT =
+        "/art:action/art:attributes/art:format/@art:value";
+
+    public static final String XPATH_CHART_X_RANGE =
+        "/art:action/art:attributes/art:xrange";
+
+    public static final String XPATH_CHART_Y_RANGE =
+        "/art:action/art:attributes/art:yrange";
+
+
+    /** The document of the incoming out() request.*/
+    protected Document request;
+
+    /** The output stream where the data should be written to.*/
+    protected OutputStream out;
+
+    /** The CallContext object.*/
+    protected CallContext context;
+
+    protected D4EArtifactCollection collection;
+
+    /** The artifact that is used to decorate the chart with meta information.*/
+    protected Artifact master;
+
+    /** The settings that should be used during output creation.*/
+    protected Settings settings;
+
+    /** Map of datasets ("index"). */
+    protected SortedMap<Integer, AxisDataset> datasets;
+
+    /** List of annotations to insert in plot. */
+    protected List<RiverAnnotation> annotations = new ArrayList<RiverAnnotation>();
+
+    protected abstract List<AxisSection> buildYAxisSections();
+
+    protected String outName;
+
+    /**
+     * Default constructor that initializes internal data structures.
+     */
+    public ChartGenerator2() {
+        datasets = new TreeMap<Integer, AxisDataset>();
+    }
+
+    /**
+     * Adds annotations to list. The given annotation will be visible.
+     */
+    public void addAnnotations(RiverAnnotation annotation) {
+        annotations.add(annotation);
+    }
+
+    /**
+     * This method needs to be implemented by concrete subclasses to create new
+     * instances of JFreeChart.
+     *
+     * @return a new instance of a JFreeChart.
+     */
+    public abstract JFreeChart generateChart();
+
+
+    /** For every outable (i.e. facets), this function is
+     * called and handles the data accordingly. */
+    @Override
+    public abstract void doOut(
+        ArtifactAndFacet bundle,
+        ThemeDocument    attr,
+        boolean          visible);
+
+
+
+    protected abstract Series getSeriesOf(XYDataset dataset, int idx);
+
+    /**
+     * Returns the default title of a chart.
+     *
+     * @return the default title of a chart.
+     */
+    protected abstract String getDefaultChartTitle();
+
+    protected abstract String getDefaultYAxisLabel(String axisName);
+
+
+    /**
+     * Returns the default X-Axis label of a chart.
+     *
+     * @return the default X-Axis label of a chart.
+     */
+    protected abstract String getDefaultXAxisLabel();
+
+    /**
+     * This method is used to create new AxisDataset instances which may differ
+     * in concrete subclasses.
+     *
+     * @param idx The index of an axis.
+     */
+    protected abstract AxisDataset createAxisDataset(int idx);
+
+
+    /**
+     * Combines the ranges of the X axis at index <i>idx</i>.
+     *
+     * @param bounds A new Bounds.
+     * @param idx The index of the X axis that should be comined with
+     * <i>range</i>.
+     */
+    protected abstract void combineXBounds(Bounds bounds, int idx);
+
+
+    /**
+     * Combines the ranges of the Y axis at index <i>idx</i>.
+     *
+     * @param bounds A new Bounds.
+     * @param index The index of the Y axis that should be comined with.
+     * <i>range</i>.
+     */
+    protected abstract void combineYBounds(Bounds bounds, int index);
+
+
+    /**
+     * This method is used to determine the ranges for axes at a given index.
+     *
+     * @param index The index of the axes at the plot.
+     *
+     * @return a Range[] with [xrange, yrange];
+     */
+    public abstract Range[] getRangesForAxis(int index);
+
+    public abstract Bounds getXBounds(int axis);
+
+    protected abstract void setXBounds(int axis, Bounds bounds);
+
+    public abstract Bounds getYBounds(int axis);
+
+    protected abstract void setYBounds(int axis, Bounds bounds);
+
+
+    /**
+     * This method retrieves the chart subtitle by calling getChartSubtitle()
+     * and adds it as TextTitle to the chart.
+     * The default implementation of getChartSubtitle() returns the same
+     * as getDefaultChartSubtitle() which must be implemented by derived
+     * classes. If you want to add multiple subtitles to the chart override
+     * this method and add your subtitles manually.
+     *
+     * @param chart The JFreeChart chart object.
+     */
+    protected void addSubtitles(JFreeChart chart) {
+        String subtitle = getChartSubtitle();
+
+        if (subtitle != null && subtitle.length() > 0) {
+            chart.addSubtitle(new TextTitle(subtitle));
+        }
+    }
+
+    /**
+     * Generate chart.
+     */
+    @Override
+    public void generate() throws IOException {
+
+        logger.debug("ChartGenerator2.generate");
+
+        if (outName.indexOf("chartinfo") > 0) {
+            generateInfo();
+        }
+        else {
+            generateImage();
+        }
+    }
+
+
+    /** Generate only meta infos */
+    private void generateInfo() throws IOException {
+
+        logger.debug("ChartInfoGenerator2.generateInfo");
+
+        JFreeChart chart = generateChart();
+
+        int[] size = getSize();
+        if (size == null) {
+            size = getDefaultSize();
+        }
+
+        ChartRenderingInfo info = new ChartRenderingInfo();
+
+        long startTime = System.currentTimeMillis();
+
+        if (USE_NOP_GRAPHICS) {
+            BufferedImage image =
+                new BufferedImage(size[0], size[1], Transparency.BITMASK);
+
+            Graphics2D g2d  = image.createGraphics();
+            Graphics2D nop = new NOPGraphics2D(g2d);
+
+            chart.draw(
+                nop,
+                new Rectangle2D.Double(0, 0, size[0], size[1]),
+                null,
+                info);
+
+            nop.dispose();
+        }
+        else {
+            chart.createBufferedImage(
+                size[0], size[1], Transparency.BITMASK, info);
+        }
+
+        long stopTime = System.currentTimeMillis();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Rendering info took: " +
+                (stopTime-startTime) + "ms");
+        }
+
+
+        InfoGeneratorHelper2 helper = new InfoGeneratorHelper2(this);
+        Document doc = helper.createInfoDocument(chart, info);
+
+        XMLUtils.toStream(doc, out);
+    }
+
+    /** Generate the diagram as an image. */
+    private void generateImage() throws IOException {
+        logger.debug("ChartGenerator2.generateImage");
+
+        JFreeChart chart = generateChart();
+
+        String format = getFormat();
+        int[]  size   = getSize();
+
+        if (size == null) {
+            size = getExportDimension();
+        }
+
+        context.putContextValue("chart.width",  size[0]);
+        context.putContextValue("chart.height", size[1]);
+
+        if (format.equals(ChartExportHelper.FORMAT_PNG)) {
+            context.putContextValue("chart.image.format", "png");
+
+            ChartExportHelper.exportImage(
+                out,
+                chart,
+                context);
+        }
+        else if (format.equals(ChartExportHelper.FORMAT_PDF)) {
+            preparePDFContext(context);
+
+            ChartExportHelper.exportPDF(
+                out,
+                chart,
+                context);
+        }
+        else if (format.equals(ChartExportHelper.FORMAT_SVG)) {
+            prepareSVGContext(context);
+
+            ChartExportHelper.exportSVG(
+                out,
+                chart,
+                context);
+        }
+        else if (format.equals(ChartExportHelper.FORMAT_CSV)) {
+            context.putContextValue("chart.image.format", "csv");
+
+            ChartExportHelper.exportCSV(
+                out,
+                chart,
+                context);
+        }
+    }
+
+
+    @Override
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        logger.debug("ChartGenerator2.init");
+
+        this.outName = outName;
+        this.request = request;
+        this.out     = out;
+        this.context = context;
+    }
+
+
+    /** Sets the master artifact. */
+    @Override
+    public void setMasterArtifact(Artifact master) {
+        this.master = master;
+    }
+
+
+    /**
+     * Gets the master artifact.
+     * @return the master artifact.
+     */
+    public Artifact getMaster() {
+        return master;
+    }
+
+
+    /** Sets the collection. */
+    @Override
+    public void setCollection(D4EArtifactCollection collection) {
+        this.collection = collection;
+    }
+
+
+    @Override
+    public void setSettings(Settings settings) {
+        this.settings = settings;
+    }
+
+
+    /**
+     * Returns an instance of <i>ChartSettings</i> with a chart specific section
+     * but with no axes settings.
+     *
+     * @return an instance of <i>ChartSettings</i>.
+     */
+    @Override
+    public Settings getSettings() {
+        if (this.settings != null) {
+            return this.settings;
+        }
+
+        ChartSettings settings = new ChartSettings();
+
+        ChartSection  chartSection  = buildChartSection();
+        LegendSection legendSection = buildLegendSection();
+        ExportSection exportSection = buildExportSection();
+
+        settings.setChartSection(chartSection);
+        settings.setLegendSection(legendSection);
+        settings.setExportSection(exportSection);
+
+        List<AxisSection> axisSections = buildAxisSections();
+        for (AxisSection axisSection: axisSections) {
+            settings.addAxisSection(axisSection);
+        }
+
+        return settings;
+    }
+
+
+    /**
+     * Creates a new <i>ChartSection</i>.
+     *
+     * @return a new <i>ChartSection</i>.
+     */
+    protected ChartSection buildChartSection() {
+        ChartSection chartSection = new ChartSection();
+        chartSection.setTitle(getChartTitle());
+        chartSection.setSubtitle(getChartSubtitle());
+        chartSection.setDisplayGrid(isGridVisible());
+        chartSection.setDisplayLogo(showLogo());
+        chartSection.setLogoVPlacement(logoVPlace());
+        chartSection.setLogoHPlacement(logoHPlace());
+        return chartSection;
+    }
+
+
+    /**
+     * Creates a new <i>LegendSection</i>.
+     *
+     * @return a new <i>LegendSection</i>.
+     */
+    protected LegendSection buildLegendSection() {
+        LegendSection legendSection = new LegendSection();
+        legendSection.setVisibility(isLegendVisible());
+        legendSection.setFontSize(getLegendFontSize());
+        legendSection.setAggregationThreshold(10);
+        return legendSection;
+    }
+
+
+    /**
+     * Creates a new <i>ExportSection</i> with default values <b>WIDTH=600</b>
+     * and <b>HEIGHT=400</b>.
+     *
+     * @return a new <i>ExportSection</i>.
+     */
+    protected ExportSection buildExportSection() {
+        ExportSection exportSection = new ExportSection();
+        exportSection.setWidth(600);
+        exportSection.setHeight(400);
+        return exportSection;
+    }
+
+
+    /**
+     * Creates a list of Sections that contains all axes of the chart (including
+     * X and Y axes).
+     *
+     * @return a list of Sections for each axis in this chart.
+     */
+    protected List<AxisSection> buildAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+
+        axisSections.addAll(buildXAxisSections());
+        axisSections.addAll(buildYAxisSections());
+
+        return axisSections;
+    }
+
+
+    /**
+     * Creates a new Section for chart's X axis.
+     *
+     * @return a List that contains a Section for the X axis.
+     */
+    protected List<AxisSection> buildXAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+
+        String identifier = "X";
+
+        AxisSection axisSection = new AxisSection();
+        axisSection.setIdentifier(identifier);
+        axisSection.setLabel(getXAxisLabel());
+        axisSection.setFontSize(14);
+        axisSection.setFixed(false);
+
+        // XXX We are able to find better default ranges that [0,0], but the Y
+        // axes currently have no better ranges set.
+        axisSection.setUpperRange(0d);
+        axisSection.setLowerRange(0d);
+
+        axisSections.add(axisSection);
+
+        return axisSections;
+    }
+
+
+    /**
+     * Returns the <i>settings</i> as <i>ChartSettings</i>.
+     *
+     * @return the <i>settings</i> as <i>ChartSettings</i> or null, if
+     * <i>settings</i> is not an instance of <i>ChartSettings</i>.
+     */
+    public ChartSettings getChartSettings() {
+        if (settings instanceof ChartSettings) {
+            return (ChartSettings) settings;
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Returns the chart title provided by <i>settings</i>.
+     *
+     * @param settings A ChartSettings object.
+     *
+     * @return the title provided by <i>settings</i> or null if no
+     * <i>ChartSection</i> is provided by <i>settings</i>.
+     *
+     * @throws NullPointerException if <i>settings</i> is null.
+     */
+    public String getChartTitle(ChartSettings settings) {
+        ChartSection cs = settings.getChartSection();
+        return cs != null ? cs.getTitle() : null;
+    }
+
+
+    /**
+     * Returns the chart subtitle provided by <i>settings</i>.
+     *
+     * @param settings A ChartSettings object.
+     *
+     * @return the subtitle provided by <i>settings</i> or null if no
+     * <i>ChartSection</i> is provided by <i>settings</i>.
+     *
+     * @throws NullPointerException if <i>settings</i> is null.
+     */
+    public String getChartSubtitle(ChartSettings settings) {
+        ChartSection cs = settings.getChartSection();
+        return cs != null ? cs.getSubtitle() : null;
+    }
+
+
+    /**
+     * Returns a boolean object that determines if the chart grid should be
+     * visible or not. This information needs to be provided by <i>settings</i>,
+     * otherweise the default is true.
+     *
+     * @param settings A ChartSettings object.
+     *
+     * @return true, if the chart grid should be visible otherwise false.
+     *
+     * @throws NullPointerException if <i>settings</i> is null.
+     */
+    public boolean isGridVisible(ChartSettings settings) {
+        ChartSection     cs = settings.getChartSection();
+        Boolean displayGrid = cs.getDisplayGrid();
+
+        return displayGrid != null ? displayGrid : true;
+    }
+
+
+    /**
+     * Returns a boolean object that determines if the chart legend should be
+     * visible or not. This information needs to be provided by <i>settings</i>,
+     * otherwise the default is true.
+     *
+     * @param settings A ChartSettings object.
+     *
+     * @return true, if the chart legend should be visible otherwise false.
+     *
+     * @throws NullPointerException if <i>settings</i> is null.
+     */
+    public boolean isLegendVisible(ChartSettings settings) {
+        LegendSection      ls = settings.getLegendSection();
+        Boolean displayLegend = ls.getVisibility();
+
+        return displayLegend != null ? displayLegend : true;
+    }
+
+
+    /**
+     * Returns the legend font size specified in <i>settings</i> or null if no
+     * <i>LegendSection</i> is provided by <i>settings</i>.
+     *
+     * @param settings A ChartSettings object.
+     *
+     * @return the legend font size or null.
+     *
+     * @throws NullPointerException if <i>settings</i> is null.
+     */
+    public Integer getLegendFontSize(ChartSettings settings) {
+        LegendSection ls = settings.getLegendSection();
+        return ls != null ? ls.getFontSize() : null;
+    }
+
+
+    /**
+     * Returns the title of a chart. The return value depends on the existence
+     * of ChartSettings: if there are ChartSettings set, this method returns the
+     * chart title provided by those settings. Otherwise, this method returns
+     * getDefaultChartTitle().
+     *
+     * @return the title of a chart.
+     */
+    protected String getChartTitle() {
+        ChartSettings chartSettings = getChartSettings();
+
+        if (chartSettings != null) {
+            return getChartTitle(chartSettings);
+        }
+
+        return getDefaultChartTitle();
+    }
+
+
+    /**
+     * Returns the subtitle of a chart. The return value depends on the
+     * existence of ChartSettings: if there are ChartSettings set, this method
+     * returns the chart title provided by those settings. Otherwise, this
+     * method returns getDefaultChartSubtitle().
+     *
+     * @return the subtitle of a chart.
+     */
+    protected String getChartSubtitle() {
+        ChartSettings chartSettings = getChartSettings();
+
+        if (chartSettings != null) {
+            return getChartSubtitle(chartSettings);
+        }
+
+        return getDefaultChartSubtitle();
+    }
+
+
+    /**
+     * This method always returns null. Override it in subclasses that require
+     * subtitles.
+     *
+     * @return null.
+     */
+    protected String getDefaultChartSubtitle() {
+        // Override this method in subclasses
+        return null;
+    }
+
+
+    /**
+     * This method is used to determine, if the chart's legend is visible or
+     * not. If a <i>settings</i> instance is set, this instance determines the
+     * visibility otherwise, this method returns true as default if no
+     * <i>settings</i> is set.
+     *
+     * @return true, if the legend should be visible, otherwise false.
+     */
+    protected boolean isLegendVisible() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            return isLegendVisible(chartSettings);
+        }
+
+        return true;
+    }
+
+
+    /** Where to place the logo. */
+    protected String logoHPlace() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            ChartSection cs    = chartSettings.getChartSection();
+            String       place = cs.getLogoHPlacement();
+
+            return place;
+        }
+        return "center";
+    }
+
+
+    /** Where to place the logo. */
+    protected String logoVPlace() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            ChartSection cs    = chartSettings.getChartSection();
+            String       place = cs.getLogoVPlacement();
+
+            return place;
+        }
+        return "top";
+    }
+
+
+    /** Return the logo id from settings. */
+    protected String showLogo(ChartSettings chartSettings) {
+        if (chartSettings != null) {
+            ChartSection cs   = chartSettings.getChartSection();
+            String       logo = cs.getDisplayLogo();
+
+            return logo;
+        }
+        return "none";
+    }
+
+
+    /**
+     * This method is used to determine if a logo should be added to the plot.
+     *
+     * @return logo name (null if none).
+     */
+    protected String showLogo() {
+        ChartSettings chartSettings = getChartSettings();
+        return showLogo(chartSettings);
+    }
+
+
+    /**
+     * This method is used to determine the font size of the chart's legend. If
+     * a <i>settings</i> instance is set, this instance determines the font
+     * size, otherwise this method returns 12 as default if no <i>settings</i>
+     * is set or if it doesn't provide a legend font size.
+     *
+     * @return a legend font size.
+     */
+    protected int getLegendFontSize() {
+        Integer fontSize = null;
+
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings != null) {
+            fontSize = getLegendFontSize(chartSettings);
+        }
+
+        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
+    }
+
+
+    /**
+     * This method is used to determine if the resulting chart should display
+     * grid lines or not. <b>Note: this method always returns true!</b>
+     *
+     * @return true, if the chart should display grid lines, otherwise false.
+     */
+    protected boolean isGridVisible() {
+        return true;
+    }
+
+
+    /**
+     * Returns the X-Axis label of a chart.
+     *
+     * @return the X-Axis label of a chart.
+     */
+    protected String getXAxisLabel() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return getDefaultXAxisLabel();
+        }
+
+        AxisSection as = chartSettings.getAxisSection("X");
+        if (as != null) {
+            String label = as.getLabel();
+
+            if (label != null) {
+                return label;
+            }
+        }
+
+        return getDefaultXAxisLabel();
+    }
+
+
+    /**
+     * This method returns the font size for the X axis. If the font size is
+     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
+     * returned. Otherwise the default font size 12 is returned.
+     *
+     * @return the font size for the x axis.
+     */
+    protected int getXAxisLabelFontSize() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return DEFAULT_FONT_SIZE;
+        }
+
+        AxisSection   as = chartSettings.getAxisSection("X");
+        Integer fontSize = as.getFontSize();
+
+        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
+    }
+
+    /**
+     * Glue between axis names and index.
+     */
+    protected abstract String axisIndexToName(int index);
+
+    /**
+     * This method returns the font size for an Y axis. If the font size is
+     * specified in ChartSettings (if <i>chartSettings</i> is set), this size is
+     * returned. Otherwise the default font size 12 is returned.
+     *
+     * @return the font size for the x axis.
+     */
+    protected int getYAxisFontSize(int index) {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return DEFAULT_FONT_SIZE;
+        }
+
+        AxisSection as = chartSettings.getAxisSection(axisIndexToName(index));
+        if (as == null) {
+            return DEFAULT_FONT_SIZE;
+        }
+        Integer fontSize = as.getFontSize();
+
+        return fontSize != null ? fontSize : DEFAULT_FONT_SIZE;
+    }
+
+    /**
+     * This method returns the export dimension specified in ChartSettings as
+     * int array [width,height].
+     *
+     * @return an int array with [width,height].
+     */
+    protected int[] getExportDimension() {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return new int[] { 600, 400 };
+        }
+
+        ExportSection export = chartSettings.getExportSection();
+        Integer width  = export.getWidth();
+        Integer height = export.getHeight();
+
+        if (width != null && height != null) {
+            return new int[] { width, height };
+        }
+
+        return new int[] { 600, 400 };
+    }
+
+    protected abstract String getYAxisLabel(String axisName);
+
+    /**
+     * This method searches for a specific axis in the <i>settings</i> if
+     * <i>settings</i> is set. If the axis was found, this method returns the
+     * specified axis range if the axis range is fixed. Otherwise, this method
+     * returns null.
+     *
+     * @param axisId The identifier of an axis.
+     *
+     * @return the specified axis range from <i>settings</i> if the axis is
+     * fixed, otherwise null.
+     */
+    public Range getRangeForAxisFromSettings(String axisId) {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return null;
+        }
+
+        AxisSection as = chartSettings.getAxisSection(axisId);
+
+        if (as == null) {
+            return null;
+        }
+
+        Boolean  fixed = as.isFixed();
+
+        if (fixed != null && fixed) {
+            Double upper = as.getUpperRange();
+            Double lower = as.getLowerRange();
+
+            if (upper != null && lower != null) {
+                return lower < upper
+                    ? new Range(lower, upper)
+                    : new Range(upper, lower);
+            }
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Adds a new AxisDataset which contains <i>dataset</i> at index <i>idx</i>.
+     *
+     * @param dataset An XYDataset.
+     * @param idx The axis index.
+     * @param visible Determines, if the dataset should be visible or not.
+     */
+    public void addAxisDataset(XYDataset dataset, int idx, boolean visible) {
+        if (dataset == null || idx < 0) {
+            return;
+        }
+
+        AxisDataset axisDataset = getAxisDataset(idx);
+
+        Bounds[] xyBounds = ChartHelper.getBounds(dataset);
+
+        if (xyBounds == null) {
+            logger.warn("Skip XYDataset for Axis (invalid ranges): " + idx);
+            return;
+        }
+
+        if (visible) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("Add new AxisDataset at index: " + idx);
+                logger.debug("X extent: " + xyBounds[0]);
+                logger.debug("Y extent: " + xyBounds[1]);
+            }
+
+            axisDataset.addDataset(dataset);
+        }
+
+        combineXBounds(xyBounds[0], 0);
+        combineYBounds(xyBounds[1], idx);
+    }
+
+
+    /**
+     * This method grants access to the AxisDatasets stored in <i>datasets</i>.
+     * If no AxisDataset exists for index <i>idx</i>, a new AxisDataset is
+     * created using <i>createAxisDataset()</i>.
+     *
+     * @param idx The index of the desired AxisDataset.
+     *
+     * @return an existing or new AxisDataset.
+     */
+    public AxisDataset getAxisDataset(int idx) {
+        AxisDataset axisDataset = datasets.get(idx);
+
+        if (axisDataset == null) {
+            axisDataset = createAxisDataset(idx);
+            datasets.put(idx, axisDataset);
+        }
+
+        return axisDataset;
+    }
+
+
+    /**
+     * Adjust some Stroke/Grid parameters for <i>plot</i>. The chart
+     * <i>Settings</i> are applied in this method.
+     *
+     * @param plot The XYPlot which is adapted.
+     */
+    protected void adjustPlot(XYPlot plot) {
+        Stroke gridStroke = new BasicStroke(
+            DEFAULT_GRID_LINE_WIDTH,
+            BasicStroke.CAP_BUTT,
+            BasicStroke.JOIN_MITER,
+            3.0f,
+            new float[] { 3.0f },
+            0.0f);
+
+        ChartSettings      cs = getChartSettings();
+        boolean isGridVisible = cs != null ? isGridVisible(cs) : true;
+
+        plot.setDomainGridlineStroke(gridStroke);
+        plot.setDomainGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setDomainGridlinesVisible(isGridVisible);
+
+        plot.setRangeGridlineStroke(gridStroke);
+        plot.setRangeGridlinePaint(DEFAULT_GRID_COLOR);
+        plot.setRangeGridlinesVisible(isGridVisible);
+
+        plot.setAxisOffset(new RectangleInsets(0d, 0d, 0d, 0d));
+    }
+
+
+    /**
+     * This helper mehtod is used to extract the current locale from instance
+     * vairable <i>context</i>.
+     *
+     * @return the current locale.
+     */
+    protected Locale getLocale() {
+        CallMeta           meta = context.getMeta();
+        PreferredLocale[] prefs = meta.getLanguages();
+
+        int len = prefs != null ? prefs.length : 0;
+
+        Locale[] locales = new Locale[len];
+
+        for (int i = 0; i < len; i++) {
+            locales[i] = prefs[i].getLocale();
+        }
+
+        return meta.getPreferredLocale(locales);
+    }
+
+
+    /**
+     * Look up \param key in i18n dictionary.
+     * @param key key for which to find i18nd version.
+     * @param def default, returned if lookup failed.
+     * @return value found in i18n dictionary, \param def if no value found.
+     */
+    public String msg(String key, String def) {
+        return Resources.getMsg(context.getMeta(), key, def);
+    }
+
+    /**
+     * Look up \param key in i18n dictionary.
+     * @param key key for which to find i18nd version.
+     * @return value found in i18n dictionary, key itself if failed.
+     */
+    public String msg(String key) {
+        return Resources.getMsg(context.getMeta(), key, key);
+    }
+
+    public String msg(String key, String def, Object[] args) {
+        return Resources.getMsg(context.getMeta(), key, def, args);
+    }
+
+
+    protected String getRiverName() {
+        D4EArtifact flys = (D4EArtifact) master;
+
+        River river = RiverUtils.getRiver(flys);
+        return (river != null) ? river.getName() : "";
+    }
+
+
+    protected double[] getRange() {
+        D4EArtifact flys = (D4EArtifact) master;
+
+        RangeAccess rangeAccess = new RangeAccess(flys);
+        return rangeAccess.getKmRange();
+    }
+
+
+    /**
+     * Returns the size of a chart export as array which has been specified by
+     * the incoming request document.
+     *
+     * @return the size of a chart as [width, height] or null if no width or
+     * height are given in the request document.
+     */
+    protected int[] getSize() {
+        int[] size = new int[2];
+
+        Element sizeEl = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_SIZE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (sizeEl != null) {
+            String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+            String w = sizeEl.getAttributeNS(uri, "width");
+            String h = sizeEl.getAttributeNS(uri, "height");
+
+            if (w.length() > 0 && h.length() > 0) {
+                try {
+                    size[0] = Integer.parseInt(w);
+                    size[1] = Integer.parseInt(h);
+                }
+                catch (NumberFormatException nfe) {
+                    logger.warn("Wrong values for chart width/height.");
+                }
+            }
+        }
+
+        return size[0] > 0 && size[1] > 0 ? size : null;
+    }
+
+
+    /**
+     * This method returns the format specified in the <i>request</i> document
+     * or <i>DEFAULT_CHART_FORMAT</i> if no format is specified in
+     * <i>request</i>.
+     *
+     * @return the format used to export this chart.
+     */
+    protected String getFormat() {
+        String format = (String) XMLUtils.xpath(
+            request,
+            XPATH_CHART_FORMAT,
+            XPathConstants.STRING,
+            ArtifactNamespaceContext.INSTANCE);
+
+        return format == null || format.length() == 0
+            ? DEFAULT_CHART_FORMAT
+            : format;
+    }
+
+
+    /**
+     * Returns the X-Axis range as String array from request document.
+     * If the (x|y)range elements are not found in request document, return
+     * null (i.e. not zoomed).
+     *
+     * @return a String array with [lower, upper], null if not in document.
+     */
+    protected String[] getDomainAxisRangeFromRequest() {
+        Element xrange = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_X_RANGE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (xrange == null) {
+            return null;
+        }
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String lower = xrange.getAttributeNS(uri, "from");
+        String upper = xrange.getAttributeNS(uri, "to");
+
+        return new String[] { lower, upper };
+    }
+
+
+    /** Returns null if the (x|y)range-element was not found in request document.
+     * This usally means that the axis are not manually zoomed, i.e. showing
+     * full data extent. */
+    protected String[] getValueAxisRangeFromRequest() {
+        Element yrange = (Element)XMLUtils.xpath(
+            request,
+            XPATH_CHART_Y_RANGE,
+            XPathConstants.NODE,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (yrange == null) {
+            return null;
+        }
+
+
+        String uri = ArtifactNamespaceContext.NAMESPACE_URI;
+
+        String lower = yrange.getAttributeNS(uri, "from");
+        String upper = yrange.getAttributeNS(uri, "to");
+
+        return new String[] { lower, upper };
+    }
+
+
+    /**
+     * Returns the default size of a chart export as array.
+     *
+     * @return the default size of a chart as [width, height].
+     */
+    protected int[] getDefaultSize() {
+        return new int[] { DEFAULT_CHART_WIDTH, DEFAULT_CHART_HEIGHT };
+    }
+
+
+    /**
+     * Add datasets stored in instance variable <i>datasets</i> to plot.
+     * <i>datasets</i> actually stores instances of AxisDataset, so each of this
+     * datasets is mapped to a specific axis as well.
+     *
+     * @param plot plot to add datasets to.
+     */
+    protected void addDatasets(XYPlot plot) {
+        logger.debug("addDatasets()");
+
+        // AxisDatasets are sorted, but some might be empty.
+        // Thus, generate numbering on the fly.
+        int axisIndex    = 0;
+        int datasetIndex = 0;
+
+        for (Map.Entry<Integer, AxisDataset> entry: datasets.entrySet()) {
+            if (!entry.getValue().isEmpty()) {
+                // Add axis and range information.
+                AxisDataset axisDataset = entry.getValue();
+                NumberAxis  axis        = createYAxis(entry.getKey());
+
+                plot.setRangeAxis(axisIndex, axis);
+
+                if (axis.getAutoRangeIncludesZero()) {
+                    axisDataset.setRange(
+                        Range.expandToInclude(axisDataset.getRange(), 0d));
+                }
+
+                setYBounds(axisIndex, expandPointRange(axisDataset.getRange()));
+
+                // Add contained datasets, mapping to axis.
+                for (XYDataset dataset: axisDataset.getDatasets()) {
+                    plot.setDataset(datasetIndex, dataset);
+                    plot.mapDatasetToRangeAxis(datasetIndex, axisIndex);
+
+                    applyThemes(plot, dataset,
+                        datasetIndex,
+                        axisDataset.isArea(dataset));
+
+                    datasetIndex++;
+                }
+
+                axisDataset.setPlotAxisIndex(axisIndex);
+                axisIndex++;
+            }
+        }
+    }
+
+
+    /**
+     * @param idx "index" of dataset/series (first dataset to be drawn has
+     *            index 0), correlates with renderer index.
+     * @param isArea true if the series describes an area and shall be rendered
+     *                as such.
+     */
+    protected void applyThemes(
+        XYPlot    plot,
+        XYDataset series,
+        int       idx,
+        boolean   isArea
+    ) {
+        if (isArea) {
+            applyAreaTheme(plot, (StyledAreaSeriesCollection) series, idx);
+        }
+        else {
+            applyLineTheme(plot, series, idx);
+        }
+    }
+
+
+    /**
+     * This method applies the themes defined in the series itself. Therefore,
+     * <i>StyledXYSeries.applyTheme()</i> is called, which modifies the renderer
+     * for the series.
+     *
+     * @param plot The plot.
+     * @param dataset The XYDataset which needs to support Series objects.
+     * @param idx The index of the renderer / dataset.
+     */
+    protected void applyLineTheme(XYPlot plot, XYDataset dataset, int idx) {
+        logger.debug("Apply LineTheme for dataset at index: " + idx);
+
+        LegendItemCollection lic  = new LegendItemCollection();
+        LegendItemCollection anno = plot.getFixedLegendItems();
+
+        Font legendFont = createLegendLabelFont();
+
+        XYLineAndShapeRenderer renderer = createRenderer(plot, idx);
+
+        for (int s = 0, num = dataset.getSeriesCount(); s < num; s++) {
+            Series series = getSeriesOf(dataset, s);
+
+            if (series instanceof StyledSeries) {
+                Style style = ((StyledSeries) series).getStyle();
+                style.applyTheme(renderer, s);
+            }
+
+            // special case: if there is just one single item, we need to enable
+            // points for this series, otherwise we would not see anything in
+            // the chart area.
+            if (series.getItemCount() == 1) {
+                renderer.setSeriesShapesVisible(s, true);
+            }
+
+            LegendItem legendItem = renderer.getLegendItem(idx, s);
+            if (legendItem.getLabel().endsWith(" ") ||
+                legendItem.getLabel().endsWith("interpol")) {
+                legendItem = null;
+            }
+
+            if (legendItem != null) {
+                legendItem.setLabelFont(legendFont);
+                lic.add(legendItem);
+            }
+            else {
+                logger.warn("Could not get LegentItem for renderer: "
+                    + idx + ", series-idx " + s);
+            }
+        }
+
+        if (anno != null) {
+            lic.addAll(anno);
+        }
+
+        plot.setFixedLegendItems(lic);
+
+        plot.setRenderer(idx, renderer);
+    }
+
+
+    /**
+     * @param plot The plot.
+     * @param area A StyledAreaSeriesCollection object.
+     * @param idx The index of the dataset.
+     */
+    protected void applyAreaTheme(
+        XYPlot                     plot,
+        StyledAreaSeriesCollection area,
+        int                        idx
+    ) {
+        LegendItemCollection lic  = new LegendItemCollection();
+        LegendItemCollection anno = plot.getFixedLegendItems();
+
+        Font legendFont = createLegendLabelFont();
+
+        logger.debug("Registering an 'area'renderer at idx: " + idx);
+
+        StableXYDifferenceRenderer dRenderer =
+            new StableXYDifferenceRenderer();
+
+        if (area.getMode() == StyledAreaSeriesCollection.FILL_MODE.UNDER) {
+            dRenderer.setPositivePaint(createTransparentPaint());
+        }
+
+        plot.setRenderer(idx, dRenderer);
+
+        area.applyTheme(dRenderer);
+
+        // i18n
+        dRenderer.setAreaLabelNumberFormat(Formatter.getFormatter(context.getMeta(), 2, 4));
+
+        dRenderer.setAreaLabelTemplate(Resources.getMsg(
+            context.getMeta(), "area.label.template", "Area=%sm2"));
+
+        LegendItem legendItem = dRenderer.getLegendItem(idx, 0);
+        if (legendItem != null) {
+            legendItem.setLabelFont(legendFont);
+            lic.add(legendItem);
+        }
+        else {
+            logger.warn("Could not get LegentItem for renderer: "
+                + idx + ", series-idx " + 0);
+        }
+
+        if (anno != null) {
+            lic.addAll(anno);
+        }
+
+        plot.setFixedLegendItems(lic);
+    }
+
+
+    /**
+     * Expands a given range if it collapses into one point.
+     *
+     * @param range Range to be expanded if upper == lower bound.
+     *
+     * @return Bounds of point plus 5 percent in each direction.
+     */
+    private Bounds expandPointRange(Range range) {
+        if (range == null) {
+            return null;
+        }
+        else if (range.getLowerBound() == range.getUpperBound()) {
+            Range expandedRange = ChartHelper.expandRange(range, 5d);
+            return new DoubleBounds(expandedRange.getLowerBound(), expandedRange.getUpperBound());
+        }
+
+        return new DoubleBounds(range.getLowerBound(), range.getUpperBound());
+    }
+
+
+    /**
+     * Creates a new instance of EnhancedLineAndShapeRenderer.
+     *
+     * @param plot The plot which is set for the new renderer.
+     * @param idx This value is not used in the current implementation.
+     *
+     * @return a new instance of EnhancedLineAndShapeRenderer.
+     */
+    protected XYLineAndShapeRenderer createRenderer(XYPlot plot, int idx) {
+        logger.debug("Create EnhancedLineAndShapeRenderer for idx: " + idx);
+
+        EnhancedLineAndShapeRenderer r =
+            new EnhancedLineAndShapeRenderer(true, false);
+
+        r.setPlot(plot);
+
+        return r;
+    }
+
+
+    /**
+     * Creates a new instance of <i>IdentifiableNumberAxis</i>.
+     *
+     * @param idx The index of the new axis.
+     * @param label The label of the new axis.
+     *
+     * @return an instance of IdentifiableNumberAxis.
+     */
+    protected NumberAxis createNumberAxis(int idx, String label) {
+        return new IdentifiableNumberAxis(axisIndexToName(idx), label);
+    }
+
+
+    /**
+     * Create Y (range) axis for given index.
+     * Shall be overriden by subclasses.
+     */
+    protected NumberAxis createYAxis(int index) {
+
+        Font labelFont = new Font(
+            DEFAULT_FONT_NAME,
+            Font.BOLD,
+            getYAxisFontSize(index));
+
+        String axisName = axisIndexToName(index);
+
+        IdentifiableNumberAxis axis = new IdentifiableNumberAxis(
+            axisName, getYAxisLabel(axisName));
+
+        axis.setAutoRangeIncludesZero(false);
+        axis.setLabelFont(labelFont);
+        axis.setTickLabelFont(labelFont);
+
+        return axis;
+    }
+
+
+    /**
+     * Creates a new LegendItem with <i>name</i> and font provided by
+     * <i>createLegendLabelFont()</i>.
+     *
+     * @param theme The theme of the chart line.
+     * @param name The displayed name of the item.
+     *
+     * @return a new LegendItem instance.
+     */
+    public LegendItem createLegendItem(ThemeDocument theme, String name) {
+        // OPTIMIZE Pass font, parsed Theme items.
+
+        Color color = theme.parseLineColorField();
+        if (color == null) {
+            color = Color.BLACK;
+        }
+
+        LegendItem legendItem  = new LegendItem(name, color);
+
+        legendItem.setLabelFont(createLegendLabelFont());
+        return legendItem;
+    }
+
+
+    /**
+     * Creates Font (Family and size) to use when creating Legend Items. The
+     * font size depends in the return value of <i>getLegendFontSize()</i>.
+     *
+     * @return a new Font instance with <i>DEFAULT_FONT_NAME</i>.
+     */
+    protected Font createLegendLabelFont() {
+        return new Font(
+            DEFAULT_FONT_NAME,
+            Font.PLAIN,
+            getLegendFontSize()
+        );
+    }
+
+
+    /**
+     * Create new legend entries, dependent on settings.
+     * @param plot The plot for which to modify the legend.
+     */
+    public void aggregateLegendEntries(XYPlot plot) {
+        int AGGR_THRESHOLD = 0;
+
+        if (getChartSettings() == null) {
+            return;
+        }
+        Integer threshold = getChartSettings().getLegendSection()
+            .getAggregationThreshold();
+
+        AGGR_THRESHOLD = (threshold != null) ? threshold.intValue() : 0;
+
+        LegendProcessor.aggregateLegendEntries(plot, AGGR_THRESHOLD);
+    }
+
+
+    /**
+     * Returns a transparently textured paint.
+     *
+     * @return a transparently textured paint.
+     */
+    protected static Paint createTransparentPaint() {
+        // TODO why not use a transparent color?
+        BufferedImage texture = new BufferedImage(
+            1, 1, BufferedImage.TYPE_4BYTE_ABGR);
+
+        return new TexturePaint(
+            texture, new Rectangle2D.Double(0d, 0d, 0d, 0d));
+    }
+
+
+    protected void preparePDFContext(CallContext context) {
+        int[] dimension = getExportDimension();
+
+        context.putContextValue("chart.width", dimension[0]);
+        context.putContextValue("chart.height", dimension[1]);
+        context.putContextValue("chart.marginLeft",   5f);
+        context.putContextValue("chart.marginRight",  5f);
+        context.putContextValue("chart.marginTop",    5f);
+        context.putContextValue("chart.marginBottom", 5f);
+        context.putContextValue(
+            "chart.page.format",
+            ChartExportHelper.DEFAULT_PAGE_SIZE);
+    }
+
+
+    protected void prepareSVGContext(CallContext context) {
+        int[] dimension = getExportDimension();
+
+        context.putContextValue("chart.width", dimension[0]);
+        context.putContextValue("chart.height", dimension[1]);
+        context.putContextValue(
+            "chart.encoding",
+            ChartExportHelper.DEFAULT_ENCODING);
+    }
+
+    /**
+     * Retuns the call context. May be null if init hasn't been called yet.
+     *
+     * @return the CallContext instance
+     */
+    public CallContext getCallContext() {
+        return context;
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -69,6 +69,10 @@
         this.generator = generator;
     }
 
+    public void setup(Object config) {
+        logger.debug("ChartInfoGenerator.setup");
+    }
+
 
     /**
      * Dispatches the operation to the instantiated generator.
@@ -77,10 +81,10 @@
      * @param out
      * @param context
      */
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
         this.out = out;
 
-        generator.init(request, out, context);
+        generator.init(outName, request, out, context);
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -19,8 +19,6 @@
 import java.text.DateFormat;
 import java.util.Locale;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
@@ -32,7 +30,6 @@
 
 import org.dive4elements.artifacts.common.utils.Config;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 
 import org.dive4elements.river.artifacts.D4EArtifact;
@@ -82,15 +79,10 @@
     protected boolean isCalculated;
     protected Date validSince;
 
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("ComputedDischargeCurveExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WQKms>();
+    public ComputedDischargeCurveExporter() {
+        data = new ArrayList<WQKms>();
     }
 
-
     @Override
     protected void addData(Object d) {
         if (d instanceof CalculationResult) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -70,7 +70,6 @@
 
     /** Trivial Constructor. */
     public CrossSectionGenerator() {
-        super();
     }
 
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramAttributes.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,374 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+import org.dive4elements.river.exports.process.Processor;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.D4EArtifact;
+
+import org.dive4elements.artifacts.common.utils.ElementConverter;
+
+public class DiagramAttributes
+implements   ElementConverter
+{
+    private static Logger log = Logger.getLogger(DiagramAttributes.class);
+
+    public class Instance {
+
+        private List<Processor> processors;
+
+        public Instance() {
+            processors = createProcessors();
+        }
+
+        private List<Processor> createProcessors() {
+            List<Processor> processors =
+                new ArrayList<Processor>(axesProcessors.size());
+            for (AxisProcessor ap: axesProcessors) {
+                Processor pr = ap.createProcessor();
+                if (pr != null) {
+                    processors.add(pr);
+                }
+            }
+            return processors;
+        }
+
+        public Processor getProcessorForAxisName(String axisName) {
+            for (Processor pr: processors) {
+                String aName = pr.getAxisName();
+                if (aName != null && axisName.equals(aName)) {
+                    return pr;
+                }
+            }
+            return null;
+        }
+
+        public List<Processor> getProcessors() {
+            return processors;
+        }
+
+        public Title getTitle() {
+            return DiagramAttributes.this.getTitle();
+        }
+
+        public Title getSubtitle() {
+            return DiagramAttributes.this.getSubtitle();
+        }
+
+        public Title getDomainAxisTitle() {
+            return DiagramAttributes.this.getDomainAxisTitle();
+        }
+
+        public int getAxisIndex(String axisName) {
+            return DiagramAttributes.this.getAxisIndex(axisName);
+        }
+
+        public String getAxisName(int index) {
+            return DiagramAttributes.this.getAxisName(index);
+        }
+
+        public List<AxisAttributes> getAxesAttributes() {
+            return DiagramAttributes.this.getAxesAttributes();
+        }
+    } // class Instance
+
+    public static class AxisAttributes {
+        private String  name;
+        private boolean isLeftAlign;
+        private boolean forceAlign;
+        private boolean includeZero;
+
+        public AxisAttributes() {
+        }
+
+        public AxisAttributes(
+            String  name,
+            boolean isLeftAlign,
+            boolean forceAlign,
+            boolean includeZero
+        ) {
+            this.name        = name;
+            this.isLeftAlign = isLeftAlign;
+            this.forceAlign  = forceAlign;
+            this.includeZero = includeZero;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public boolean isLeftAlign() {
+            return isLeftAlign;
+        }
+
+        public boolean forceAlign() {
+            return forceAlign;
+        }
+
+        public boolean includeZero() {
+            return includeZero;
+        }
+    } // class AxisAttributes
+
+    public static class AxisProcessor {
+
+        private Class<Processor> processorClass;
+        private String axisName;
+
+        public AxisProcessor(Class<Processor> processorClass, String axisName) {
+            this.processorClass = processorClass;
+            this.axisName = axisName;
+        }
+
+        public Processor createProcessor() {
+            try {
+                Processor pr = processorClass.newInstance();
+                pr.setAxisName(axisName);
+                return pr;
+            }
+            catch (InstantiationException ie) {
+                log.error(ie, ie);
+            }
+            catch (IllegalAccessException iae) {
+                log.error(iae, iae);
+            }
+            return null;
+        }
+
+    } // class AxisProcessor
+
+    public static class Argument {
+        private String expression;
+        private String type;
+
+        public Argument() {
+        }
+
+        public Argument(String expression, String type) {
+            this.expression = expression;
+            this.type = type;
+        }
+
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            if (expression.startsWith("artifact.")) {
+                String value = artifact.getDataAsString(
+                    expression.substring("artifact.".length()));
+                return convert(value);
+            }
+            if (expression.startsWith("context.")) {
+                return context.getContextValue(
+                    expression.substring("context.".length()));
+            }
+            return expression;
+        }
+
+        private Object convert(String value) {
+            if (value == null || type == null) {
+                return value;
+            }
+            if ("double".equals(type)) {
+                return Double.valueOf(value);
+            }
+            if ("int".equals(type)) {
+                return Integer.valueOf(value);
+            }
+            // TODO: more types
+            return value;
+        }
+    } // class Argument
+
+    public static class Title {
+
+        private String key;
+        private String def;
+        private List<Argument> arguments;
+
+        public Title() {
+            arguments = new ArrayList<Argument>(5);
+        }
+
+        public Title(String key) {
+            this(key, key);
+        }
+
+        public Title(String key, String def) {
+            this();
+            this.key = key;
+            this.def = def;
+        }
+
+        public String getKey() {
+            return key;
+        }
+
+        public void addArgument(Argument argument) {
+            arguments.add(argument);
+        }
+
+        public String evaluate(D4EArtifact artifact, CallContext context) {
+            if (key == null || key.isEmpty()) {
+                return def;
+            }
+            Object [] args = new Object[arguments.size()];
+            for (int i = 0; i < args.length; ++i) {
+                args[i] = arguments.get(i).evaluate(artifact, context);
+            }
+            return Resources.getMsg(context.getMeta(), key, def, args);
+        }
+    } // class Title
+
+    private List<AxisAttributes> axesAttrs;
+    private List<AxisProcessor>  axesProcessors;
+
+    private Title title;
+    private Title subtitle;
+    private Title domainAxisTitle;
+
+    public DiagramAttributes() {
+        axesAttrs      = new ArrayList<AxisAttributes>(5);
+        axesProcessors = new ArrayList<AxisProcessor>(5);
+    }
+
+    @Override
+    public Object convert(Element config) {
+        parseAxis(config);
+        parseProcessors(config);
+        parseTitle(config);
+        parseSubtitle(config);
+        parseDomainAxisTitle(config);
+        return this;
+    }
+
+    public List<AxisAttributes> getAxesAttributes() {
+        return axesAttrs;
+    }
+
+    private void parseAxis(Element config) {
+        NodeList axisNodes = config.getElementsByTagName("axis");
+
+        for (int i = 0, N = axisNodes.getLength(); i < N; ++i) {
+            Element axisElement = (Element)axisNodes.item(i);
+            String name = axisElement.getAttribute("name").trim();
+            String align = axisElement.getAttribute("align").trim();
+            String includeZero =
+                axisElement.getAttribute("include-zero").trim();
+            if (name.isEmpty()) {
+                continue;
+            }
+            boolean isleftAlign = false;
+            boolean forceAlign = false;
+            for (String part: align.split("[\\s,]")) {
+                part = part.trim();
+                     if ("left" .equals(part)) isleftAlign = true;
+                else if ("right".equals(part)) isleftAlign = false;
+                else if ("force".equals(part)) forceAlign  = true;
+            }
+
+            axesAttrs.add(new AxisAttributes(
+                name, isleftAlign, forceAlign,
+                includeZero.equals("true")));
+        }
+    }
+
+    public List<AxisProcessor> getAxesProcessors() {
+        return axesProcessors;
+    }
+
+    public Title getTitle() {
+        return title;
+    }
+
+    public Title getSubtitle() {
+        return subtitle;
+    }
+
+    public Title getDomainAxisTitle() {
+        return domainAxisTitle;
+    }
+
+    private void parseProcessors(Element config) {
+        NodeList processorNodes = config.getElementsByTagName("processor");
+
+        for (int i = 0, N = processorNodes.getLength(); i < N; ++i) {
+            Element processorElement = (Element)processorNodes.item(i);
+            String className = processorElement.getAttribute("class").trim();
+            String axisName = processorElement.getAttribute("axis").trim();
+            if (className.isEmpty() || axisName.isEmpty()) {
+                continue;
+            }
+
+            try {
+                Class<Processor> processorClass =
+                    (Class<Processor>)Class.forName(className);
+                axesProcessors.add(new AxisProcessor(processorClass, axisName));
+            }
+            catch (ClassNotFoundException cnfe) {
+                log.error(cnfe, cnfe);
+            }
+        }
+    }
+
+    private void parseTitle(Element config) {
+        title = extractTitle(config, "title");
+    }
+
+    private void parseSubtitle(Element config) {
+        subtitle = extractTitle(config, "subtitle");
+    }
+
+    private void parseDomainAxisTitle(Element config) {
+        domainAxisTitle = extractTitle(config, "domain-axis");
+    }
+
+    private static Title extractTitle(Element config, String tagName) {
+        NodeList titleNodes = config.getElementsByTagName(tagName);
+        if (titleNodes.getLength() < 1) {
+            return null;
+        }
+        Element titleElement = (Element)titleNodes.item(0);
+        String key = titleElement.getAttribute("key");
+        String def = titleElement.getAttribute("default");
+        Title title = new Title(key, def);
+        NodeList argumentNodes = titleElement.getElementsByTagName("arg");
+        for (int i = 0, N = argumentNodes.getLength(); i < N; ++i) {
+            Element argumentElement = (Element)argumentNodes.item(i);
+            String expression = argumentElement.getAttribute("expr");
+            String type = argumentElement.getAttribute("type");
+            title.addArgument(new Argument(expression, type));
+        }
+        return title;
+    }
+
+    public int getAxisIndex(String axisName) {
+        for (int i = axesAttrs.size()-1; i >= 0; --i) {
+            if (axesAttrs.get(i).getName().equals(axisName)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    public String getAxisName(int index) {
+        return index < 0 || index >= axesAttrs.size()
+            ? "" // null?
+            : axesAttrs.get(index).getName();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,1119 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import java.awt.Color;
+import java.awt.Font;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.river.artifacts.D4EArtifact;
+
+import org.dive4elements.river.exports.process.Processor;
+
+import org.dive4elements.river.jfree.AnnotationHelper;
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.jfree.chart.ChartFactory;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.LegendItem;
+
+import org.jfree.chart.annotations.XYAnnotation;
+import org.jfree.chart.annotations.XYImageAnnotation;
+
+import org.jfree.chart.axis.LogarithmicAxis;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.PlotOrientation;
+import org.jfree.chart.plot.XYPlot;
+
+import org.jfree.data.Range;
+
+import org.jfree.data.general.Series;
+
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+
+/**
+ * The main diagram creation class.
+ *
+ * This class is the glue between output processors and facets.
+ * The generator creates one diagram and calls the appropiate
+ * processors for the state and 
+ *
+ * With respect to datasets, ranges and axis, there are following requirements:
+ * <ul>
+ *   <li> First in, first drawn: "Early" datasets should be of lower Z-Oder
+ *        than later ones (only works per-axis). </li>
+ *   <li> Visible axis should initially show the range of all datasets that
+ *        show data for this axis (even invisible ones). Motivation: Once
+ *        a dataset (theme) has been activated, it should be on screen. </li>
+ *   <li> There should always be a Y-Axis on the "left". </li>
+ * </ul>
+ */
+public class DiagramGenerator extends ChartGenerator2 {
+
+    public static final int AXIS_SPACE = 5;
+
+    /** The logger that is used in this generator. */
+    private static Logger logger = Logger.getLogger(DiagramGenerator.class);
+
+    protected List<Marker> domainMarkers = new ArrayList<Marker>();
+
+    protected List<Marker> valueMarkers = new ArrayList<Marker>();
+
+    /** The max X range to include all X values of all series for each axis. */
+    protected Map<Integer, Bounds> xBounds;
+
+    /** The max Y range to include all Y values of all series for each axis. */
+    protected Map<Integer, Bounds> yBounds;
+
+    /** Whether or not the plot is inverted (left-right). */
+    private boolean inverted;
+
+    protected DiagramAttributes.Instance diagramAttributes;
+
+    public DiagramGenerator() {
+        super();
+
+        xBounds  = new HashMap<Integer, Bounds>();
+        yBounds  = new HashMap<Integer, Bounds>();
+    }
+
+    @Override
+    public void setup(Object config) {
+
+        if (!(config instanceof DiagramAttributes)) {
+            logger.error("invalid config type");
+            return;
+        }
+        DiagramAttributes da = (DiagramAttributes)config;
+        diagramAttributes = da.new Instance();
+    }
+
+    /**
+     * Generate the chart anew (including localized axis and all).
+     */
+    @Override
+    public JFreeChart generateChart() {
+        logger.debug("DiagramGenerator.generateChart");
+
+        JFreeChart chart = ChartFactory.createXYLineChart(
+            getChartTitle(),
+            "",
+            "",
+            null,
+            PlotOrientation.VERTICAL,
+            isLegendVisible(),
+            false,
+            false);
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+        ValueAxis axis = createXAxis(getXAxisLabel());
+        plot.setDomainAxis(axis);
+
+        chart.setBackgroundPaint(Color.WHITE);
+        plot.setBackgroundPaint(Color.WHITE);
+        addSubtitles(chart);
+        adjustPlot(plot);
+
+        //debugAxis(plot);
+
+        addDatasets(plot);
+
+        //debugDatasets(plot);
+
+        addMarkers(plot);
+
+        recoverEmptyPlot(plot);
+        preparePointRanges(plot);
+
+        //debugAxis(plot);
+
+        localizeAxes(plot);
+        adjustAxes(plot);
+        if (!(axis instanceof LogarithmicAxis)) {
+            // XXX:
+            // The auto zoom without a range tries
+            // to include 0 in a logarithmic axis
+            // which triggers a bug in jfreechart that causes
+            // the values to be drawn carthesian
+            autoZoom(plot);
+        }
+
+        //debugAxis(plot);
+
+        // These have to go after the autozoom.
+        AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
+                getChartSettings(), datasets);
+
+        // Add a logo (maybe).
+        addLogo(plot);
+
+        aggregateLegendEntries(plot);
+
+        return chart;
+    }
+
+    public String getOutName() {
+        return outName;
+    }
+
+    /**
+     * Return left most data points x value (on first axis).
+     */
+    protected double getLeftX() {
+        if (inverted) {
+            return (Double)getXBounds(0).getUpper();
+        }
+        return (Double)getXBounds(0).getLower();
+    }
+
+
+    /**
+     * Return right most data points x value (on first axis).
+     */
+    protected double getRightX() {
+        if (inverted) {
+            return (Double)getXBounds(0).getLower();
+        }
+        return (Double)getXBounds(0).getUpper();
+    }
+
+
+    /** Add a logo as background annotation to plot. */
+    protected void addLogo(XYPlot plot) {
+        String logo = showLogo();
+        if (logo  == null) {
+            logger.debug("No logo to show chosen");
+            return;
+        }
+
+        ImageIcon imageIcon = null;
+        if (logo.equals("none")) {
+            return;
+        }
+        /*
+         If you want to add images, remember to change code in these places:
+         flys-artifacts:
+         DiagramGenerator.java
+         Timeseries*Generator.java and
+         in the flys-client projects Chart*Propert*Editor.java.
+         Also, these images have to be put in
+         flys-artifacts/src/main/resources/images/
+         flys-client/src/main/webapp/images/
+         */
+        java.net.URL imageURL;
+        if (logo.equals("Intevation")) {
+            imageURL = DiagramGenerator.class.getResource("/images/intevation.png");
+        }
+        else { // TODO else if ...
+            imageURL = DiagramGenerator.class.getResource("/images/bfg_logo.gif");
+        }
+        imageIcon = new ImageIcon(imageURL);
+
+
+        double xPos = 0d, yPos = 0d;
+
+        String placeh = logoHPlace();
+        String placev = logoVPlace();
+
+        if (placev == null || placev.equals("none")) {
+            placev = "top";
+        }
+        if (placev.equals("top")) {
+            yPos = (Double)getYBounds(0).getUpper();
+        }
+        else if (placev.equals("bottom")) {
+            yPos = (Double)getYBounds(0).getLower();
+        }
+        else if (placev.equals("center")) {
+            yPos = ((Double)getYBounds(0).getUpper() + (Double)getYBounds(0).getLower())/2d;
+        }
+        else {
+            logger.debug("Unknown place-v value: " + placev);
+        }
+
+        if (placeh == null || placeh.equals("none")) {
+            placeh = "center";
+        }
+        if (placeh.equals("left")) {
+            xPos = getLeftX();
+        }
+        else if (placeh.equals("right")) {
+            xPos = getRightX();
+        }
+        else if (placeh.equals("center")) {
+            xPos = ((Double)getXBounds(0).getUpper() + (Double)getXBounds(0).getLower())/2d;
+        }
+        else {
+            logger.debug("Unknown place-h value: " + placeh);
+        }
+
+        logger.debug("logo position: " + xPos + "/" + yPos);
+
+        org.jfree.ui.RectangleAnchor anchor
+            = org.jfree.ui.RectangleAnchor.TOP;
+        if (placev.equals("top")) {
+            if (placeh.equals("left")) {
+                anchor = org.jfree.ui.RectangleAnchor.TOP_LEFT;
+            }
+            else if (placeh.equals("right")) {
+                anchor = org.jfree.ui.RectangleAnchor.TOP_RIGHT;
+            }
+            else if (placeh.equals("center")) {
+                anchor = org.jfree.ui.RectangleAnchor.TOP;
+            }
+        }
+        else if (placev.equals("bottom")) {
+            if (placeh.equals("left")) {
+                anchor = org.jfree.ui.RectangleAnchor.BOTTOM_LEFT;
+            }
+            else if (placeh.equals("right")) {
+                anchor = org.jfree.ui.RectangleAnchor.BOTTOM_RIGHT;
+            }
+            else if (placeh.equals("center")) {
+                anchor = org.jfree.ui.RectangleAnchor.BOTTOM;
+            }
+        }
+        else if (placev.equals("center")) {
+            if (placeh.equals("left")) {
+                anchor = org.jfree.ui.RectangleAnchor.LEFT;
+            }
+            else if (placeh.equals("right")) {
+                anchor = org.jfree.ui.RectangleAnchor.RIGHT;
+            }
+            else if (placeh.equals("center")) {
+                anchor = org.jfree.ui.RectangleAnchor.CENTER;
+            }
+        }
+
+        XYAnnotation xyannotation =
+            new XYImageAnnotation(xPos, yPos, imageIcon.getImage(), anchor);
+        plot.getRenderer().addAnnotation(xyannotation, org.jfree.ui.Layer.BACKGROUND);
+    }
+
+
+    protected NumberAxis createXAxis(String label) {
+        return new NumberAxis(label);
+    }
+
+
+    @Override
+    protected Series getSeriesOf(XYDataset dataset, int idx) {
+        return ((XYSeriesCollection) dataset).getSeries(idx);
+    }
+
+
+    @Override
+    protected AxisDataset createAxisDataset(int idx) {
+        logger.debug("Create new AxisDataset for index: " + idx);
+        return new AxisDataset(idx);
+    }
+
+
+    /**
+     * Put debug output about datasets.
+     */
+    public void debugDatasets(XYPlot plot) {
+        logger.debug("Number of datasets: " + plot.getDatasetCount());
+        for (int i = 0, P = plot.getDatasetCount(); i < P; i++) {
+            if (plot.getDataset(i) == null) {
+                logger.debug("Dataset #" + i + " is null");
+                continue;
+            }
+            logger.debug("Dataset #" + i + ":" + plot.getDataset(i));
+            XYSeriesCollection series = (XYSeriesCollection) plot.getDataset(i);
+            logger.debug("X-Extend of Dataset: " + series.getSeries(0).getMinX()
+                    + " " + series.getSeries(0).getMaxX());
+            logger.debug("Y-Extend of Dataset: " + series.getSeries(0).getMinY()
+                    + " " + series.getSeries(0).getMaxY());
+        }
+    }
+
+
+    /**
+     * Put debug output about axes.
+     */
+    public void debugAxis(XYPlot plot) {
+        logger.debug("...............");
+        for (int i = 0, P =  plot.getRangeAxisCount(); i < P; i++) {
+            if (plot.getRangeAxis(i) == null)
+                logger.debug("Range-Axis #" + i + " == null");
+            else {
+                logger.debug("Range-Axis " + i + " != null [" +
+                    plot.getRangeAxis(i).getRange().getLowerBound() +
+                    "  " + plot.getRangeAxis(i).getRange().getUpperBound() +
+                    "]");
+            }
+        }
+        for (int i = 0, P =  plot.getDomainAxisCount(); i < P; i++) {
+            if (plot.getDomainAxis(i) == null)
+                logger.debug("Domain-Axis #" + i + " == null");
+            else {
+                logger.debug("Domain-Axis " + i + " != null [" +
+                    plot.getDomainAxis(i).getRange().getLowerBound() +
+                    "  " + plot.getDomainAxis(i).getRange().getUpperBound() +
+                    "]");
+            }
+        }
+        logger.debug("...............");
+    }
+
+    /**
+     * Registers an area to be drawn.
+     * @param area Area to be drawn.
+     * @param axisName Name of the axis.
+     * @param visible Whether or not to be visible (important for range calculations).
+     */
+    public void addAreaSeries(StyledAreaSeriesCollection area, String axisName, boolean visible) {
+        addAreaSeries(area, diagramAttributes.getAxisIndex(axisName), visible);
+    }
+
+    /**
+     * Registers an area to be drawn.
+     * @param area Area to be drawn.
+     * @param index 'axis index'
+     * @param visible Whether or not to be visible (important for range calculations).
+     */
+    public void addAreaSeries(StyledAreaSeriesCollection area, int index, boolean visible) {
+        if (area == null) {
+            logger.warn("Cannot yet render above/under curve.");
+            return;
+        }
+
+        AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
+
+        if (visible) {
+            axisDataset.addArea(area);
+        }
+        else {
+            /* No range merging, for areas extending to infinity this
+             * causes problems. */
+        }
+    }
+
+    /**
+     * Add given series if visible, if not visible adjust ranges (such that
+     * all points in data would be plotted once visible).
+     * @param series   the data series to include in plot.
+     * @param index    index of the axis.
+     * @param visible  whether or not the data should be plotted.
+     */
+    public void addAxisSeries(XYSeries series, int index, boolean visible) {
+        if (series == null) {
+            return;
+        }
+
+        logger.debug("Y Range of XYSeries: " +
+            series.getMinY() + " | " + series.getMaxY());
+
+        addAxisDataset(new XYSeriesCollection(series), index, visible);
+    }
+
+    /**
+     * Add given series if visible, if not visible adjust ranges (such that
+     * all points in data would be plotted once visible).
+     * @param series   the data series to include in plot.
+     * @param axisName name of the axis.
+     * @param visible  whether or not the data should be plotted.
+     */
+    public void addAxisSeries(XYSeries series, String axisName, boolean visible) {
+        addAxisSeries(series, diagramAttributes.getAxisIndex(axisName), visible);
+    }
+
+
+    /**
+     * Add the given vertical marker to the chart.
+     */
+    public void addDomainMarker(Marker marker) {
+        addDomainMarker(marker, true);
+    }
+
+
+    /**
+     * Add the given vertical marker to the chart.<b>Note:</b> the marker is
+     * added to the chart only if it is not null and if <i>visible</i> is true.
+     * @param marker The marker that should be added to the chart.
+     * @param visible The visibility of the marker.
+     */
+    public void addDomainMarker(Marker marker, boolean visible) {
+        if (visible && marker != null) {
+            domainMarkers.add(marker);
+        }
+    }
+
+
+    /**
+     * Add the given vertical marker to the chart.
+     */
+    public void addValueMarker(Marker marker) {
+        addValueMarker(marker, true);
+    }
+
+
+    /**
+     * Add the given horizontal marker to the chart.<b>Note:</b> the marker is
+     * added to the chart only if it is not null and if <i>visible</i> is true.
+     * @param marker The marker that should be added to the chart.
+     * @param visible The visibility of the marker.
+     */
+    public void addValueMarker(Marker marker, boolean visible) {
+        if (visible && marker != null) {
+            valueMarkers.add(marker);
+        }
+    }
+
+
+    protected void addMarkers(XYPlot plot) {
+        for(Marker marker : domainMarkers) {
+            plot.addDomainMarker(marker);
+        }
+        for(Marker marker : valueMarkers) {
+            plot.addRangeMarker(marker);
+        }
+    }
+
+
+    /**
+     * Effect: extend range of x axis to include given limits.
+     *
+     * @param bounds the given ("minimal") bounds.
+     * @param index index of axis to be merged.
+     */
+    @Override
+    protected void combineXBounds(Bounds bounds, int index) {
+        if (!(bounds instanceof DoubleBounds)) {
+            logger.warn("Unsupported Bounds type: " + bounds.getClass());
+            return;
+        }
+
+        DoubleBounds dBounds = (DoubleBounds) bounds;
+
+        if (dBounds == null
+            || Double.isNaN((Double) dBounds.getLower())
+            || Double.isNaN((Double) dBounds.getUpper())) {
+            return;
+        }
+
+        Bounds old = getXBounds(index);
+
+        if (old != null) {
+            dBounds = (DoubleBounds) dBounds.combine(old);
+        }
+
+        setXBounds(index, dBounds);
+    }
+
+
+    @Override
+    protected void combineYBounds(Bounds bounds, int index) {
+        if (!(bounds instanceof DoubleBounds)) {
+            logger.warn("Unsupported Bounds type: " + bounds.getClass());
+            return;
+        }
+
+        DoubleBounds dBounds = (DoubleBounds) bounds;
+
+        if (dBounds == null
+            || Double.isNaN((Double) dBounds.getLower())
+            || Double.isNaN((Double) dBounds.getUpper())) {
+            return;
+        }
+
+        Bounds old = getYBounds(index);
+
+        if (old != null) {
+            dBounds = (DoubleBounds) dBounds.combine(old);
+        }
+
+        setYBounds(index, dBounds);
+    }
+
+
+    /**
+     * If no data is visible, draw at least empty axis.
+     */
+    private void recoverEmptyPlot(XYPlot plot) {
+        if (plot.getRangeAxis() == null) {
+            logger.debug("debug: No range axis");
+            plot.setRangeAxis(createYAxis(0));
+        }
+    }
+
+
+    /**
+     * Expands X axes if only a point is shown.
+     */
+    private void preparePointRanges(XYPlot plot) {
+        for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+
+            Integer key = Integer.valueOf(i);
+            Bounds  b   = getXBounds(key);
+
+
+            if (b != null && b.getLower().equals(b.getUpper())) {
+                logger.debug("Check whether to expand a x axis.i ("+b.getLower() + "-" + b.getUpper()+")");
+                setXBounds(key, ChartHelper.expandBounds(b, 5));
+            }
+        }
+    }
+
+
+    /**
+     * This method zooms the plot to the specified ranges in the attribute
+     * document or to the ranges specified by the min/max values in the
+     * datasets. <b>Note:</b> We determine the range manually if no zoom ranges
+     * are given, because JFreeCharts auto-zoom adds a margin to the left and
+     * right of the data area.
+     *
+     * @param plot The XYPlot.
+     */
+    protected void autoZoom(XYPlot plot) {
+        logger.debug("Zoom to specified ranges.");
+
+        Range xrange = getDomainAxisRange();
+        Range yrange = getValueAxisRange();
+
+        ValueAxis xAxis = plot.getDomainAxis();
+
+        Range fixedXRange = getRangeForAxisFromSettings("X");
+        if (fixedXRange != null) {
+            xAxis.setRange(fixedXRange);
+        }
+        else {
+            zoomX(plot, xAxis, getXBounds(0), xrange);
+        }
+
+        for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis yaxis = plot.getRangeAxis(i);
+
+            if (yaxis instanceof IdentifiableNumberAxis) {
+                IdentifiableNumberAxis idAxis = (IdentifiableNumberAxis) yaxis;
+
+                Range fixedRange = getRangeForAxisFromSettings(idAxis.getId());
+                if (fixedRange != null) {
+                    yaxis.setRange(fixedRange);
+                    continue;
+                }
+            }
+
+            if (yaxis == null) {
+                logger.debug("Zoom problem: no Y Axis for index: " + i);
+                continue;
+            }
+
+            logger.debug("Prepare zoom settings for y axis at index: " + i);
+            zoomY(plot, yaxis, getYBounds(Integer.valueOf(i)), yrange);
+        }
+    }
+
+
+    protected Range getDomainAxisRange() {
+        String[] ranges = getDomainAxisRangeFromRequest();
+
+        if (ranges == null || ranges.length < 2) {
+            logger.debug("No zoom range for domain axis specified.");
+            return null;
+        }
+
+        if (ranges[0].length() > 0 && ranges[1].length() > 0) {
+            try {
+                double from = Double.parseDouble(ranges[0]);
+                double to   = Double.parseDouble(ranges[1]);
+
+                if (from == 0 && to == 0) {
+                    logger.debug("No range specified. Lower and upper X == 0");
+                    return null;
+                }
+
+                if (from > to) {
+                    double tmp = to;
+                    to         = from;
+                    from       = tmp;
+                }
+
+                return new Range(from, to);
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Wrong values for domain axis range.");
+            }
+        }
+
+        return null;
+    }
+
+
+    protected Range getValueAxisRange() {
+        String[] ranges = getValueAxisRangeFromRequest();
+
+        if (ranges == null || ranges.length < 2) {
+            logger.debug("No range specified. Lower and upper Y == 0");
+            return null;
+        }
+
+        if (ranges[0].length() > 0 && ranges[1].length() > 0) {
+            try {
+                double from = Double.parseDouble(ranges[0]);
+                double to   = Double.parseDouble(ranges[1]);
+
+                if (from == 0 && to == 0) {
+                    logger.debug("No range specified. Lower and upper Y == 0");
+                    return null;
+                }
+
+                return from > to
+                       ? new Range(to, from)
+                       : new Range(from, to);
+            }
+            catch (NumberFormatException nfe) {
+                logger.warn("Wrong values for value axis range.");
+            }
+        }
+
+        return null;
+    }
+
+
+    protected boolean zoomX(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+        return zoom(plot, axis, bounds, x);
+    }
+
+
+    protected boolean zoomY(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+        return zoom(plot, axis, bounds, x);
+    }
+
+
+    /**
+     * Zooms the x axis to the range specified in the attribute document.
+     *
+     * @param plot  The XYPlot.
+     * @param axis  The axis the shoud be modified.
+     * @param bounds The whole range specified by a dataset.
+     * @param x     A user defined range (null permitted).
+     *
+     * @return true, if a zoom range was specified, otherwise false.
+     */
+    protected boolean zoom(XYPlot plot, ValueAxis axis, Bounds bounds, Range x) {
+
+        if (bounds == null) {
+            return false;
+        }
+
+        if (x != null) {
+            Bounds computed = calculateZoom(bounds, x);
+            computed.applyBounds(axis, AXIS_SPACE);
+
+            logger.debug("Zoom axis to: " + computed);
+
+            return true;
+        }
+
+        bounds.applyBounds(axis, AXIS_SPACE);
+        return false;
+    }
+
+    /**
+     * Calculates the start and end km for zoomed charts.
+     * @param bounds    The given total bounds (unzoomed).
+     * @param range     The range specifying the zoom.
+     *
+     * @return The start and end km for the zoomed chart.
+     */
+    protected Bounds calculateZoom(Bounds bounds, Range range) {
+        double min  = bounds.getLower().doubleValue();
+        double max  = bounds.getUpper().doubleValue();
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("Minimum is: " + min);
+            logger.debug("Maximum is: " + max);
+            logger.debug("Lower zoom is: " + range.getLowerBound());
+            logger.debug("Upper zoom is: " + range.getUpperBound());
+        }
+
+        double diff = max > min ? max - min : min - max;
+
+        DoubleBounds computed = new DoubleBounds(
+            min + range.getLowerBound() * diff,
+            min + range.getUpperBound() * diff);
+        return computed;
+    }
+
+    /**
+     * Extract the minimum and maximum values for x and y axes
+     * which are stored in <i>xRanges</i> and <i>yRanges</i>.
+     *
+     * @param index The index of the y-Axis.
+     *
+     * @return a Range[] as follows: [x-Range, y-Range].
+     */
+    @Override
+    public Range[] getRangesForAxis(int index) {
+        logger.debug("getRangesForAxis " + index);
+
+        Bounds rx = getXBounds(Integer.valueOf(0));
+        Bounds ry = getYBounds(Integer.valueOf(index));
+
+        if (rx == null) {
+            logger.warn("Range for x axis not set." +
+                        " Using default values: 0 - 1.");
+            rx = new DoubleBounds(0, 1);
+        }
+        if (ry == null) {
+            logger.warn("Range for y" + index +
+                        " axis not set. Using default values: 0 - 1.");
+            ry = new DoubleBounds(0, 1);
+        }
+
+        return new Range[] {
+            new Range(rx.getLower().doubleValue(), rx.getUpper().doubleValue()),
+            new Range(ry.getLower().doubleValue(), ry.getUpper().doubleValue())
+        };
+    }
+
+
+    /** Get X (usually horizontal) extent for given axis. */
+    @Override
+    public Bounds getXBounds(int axis) {
+        return xBounds.get(axis);
+    }
+
+
+    /** Set X (usually horizontal) extent for given axis. */
+    @Override
+    protected void setXBounds(int axis, Bounds bounds) {
+        if (bounds.getLower() == bounds.getUpper()) {
+            xBounds.put(axis, ChartHelper.expandBounds(bounds, 5d));
+        }
+        else {
+            xBounds.put(axis, bounds);
+        }
+    }
+
+
+    /** Get Y (usually vertical) extent for given axis. */
+    @Override
+    public Bounds getYBounds(int axis) {
+        return yBounds.get(axis);
+    }
+
+
+    /** Set Y (usually vertical) extent for given axis. */
+    @Override
+    protected void setYBounds(int axis, Bounds bounds) {
+        yBounds.put(axis, bounds);
+    }
+
+
+    /**
+     * Adjusts the axes of a plot. This method sets the <i>labelFont</i> of the
+     * X axis and applies the inversion if inverted is true.
+     *
+     * (Duplicate in TimeseriesChartGenerator)
+     *
+     * @param plot The XYPlot of the chart.
+     */
+    protected void adjustAxes(XYPlot plot) {
+        ValueAxis xaxis = plot.getDomainAxis();
+
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return;
+        }
+
+        Font labelFont = new Font(
+            DEFAULT_FONT_NAME,
+            Font.BOLD,
+            getXAxisLabelFontSize());
+
+        xaxis.setLabelFont(labelFont);
+        xaxis.setTickLabelFont(labelFont);
+
+        logger.debug("Adjusting xAxis. Inverted?: " + inverted);
+        if (inverted) {
+            xaxis.setInverted(true);
+        }
+    }
+
+
+    /**
+     * This method walks over all axes (domain and range) of <i>plot</i> and
+     * calls localizeDomainAxis() for domain axes or localizeRangeAxis() for
+     * range axes.
+     *
+     * @param plot The XYPlot.
+     */
+    private void localizeAxes(XYPlot plot) {
+        for (int i = 0, num = plot.getDomainAxisCount(); i < num; i++) {
+            ValueAxis axis = plot.getDomainAxis(i);
+
+            if (axis != null) {
+                localizeDomainAxis(axis);
+            }
+            else {
+                logger.warn("Domain axis at " + i + " is null.");
+            }
+        }
+
+        for (int i = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis axis = plot.getRangeAxis(i);
+
+            if (axis != null) {
+                localizeRangeAxis(axis);
+            }
+            else {
+                logger.warn("Range axis at " + i + " is null.");
+            }
+        }
+    }
+
+
+    /**
+     * Overrides the NumberFormat with the NumberFormat for the current locale
+     * that is provided by getLocale().
+     *
+     * @param domainAxis The domain axis that needs localization.
+     */
+    protected void localizeDomainAxis(ValueAxis domainAxis) {
+        NumberFormat nf = NumberFormat.getInstance(getLocale());
+        ((NumberAxis) domainAxis).setNumberFormatOverride(nf);
+    }
+
+
+    /**
+     * Overrides the NumberFormat with the NumberFormat for the current locale
+     * that is provided by getLocale().
+     *
+     * @param rangeAxis The domain axis that needs localization.
+     */
+    protected void localizeRangeAxis(ValueAxis rangeAxis) {
+        NumberFormat nf = NumberFormat.getInstance(getLocale());
+        ((NumberAxis) rangeAxis).setNumberFormatOverride(nf);
+    }
+
+
+    /**
+     * Create a hash from a legenditem.
+     * This hash can then be used to merge legend items labels.
+     * @return hash for given legenditem to identify mergeables.
+     */
+    public static String legendItemHash(LegendItem li) {
+        // TODO Do proper implementation. Ensure that only mergable sets are created.
+        // getFillPaint()
+        // getFillPaintTransformer()
+        // getLabel()
+        // getLine()
+        // getLinePaint()
+        // getLineStroke()
+        // getOutlinePaint()
+        // getOutlineStroke()
+        // Shape getShape()
+        // String getToolTipText()
+        // String getURLText()
+        // boolean isLineVisible()
+        // boolean isShapeFilled()
+        // boolean isShapeOutlineVisible()
+        // boolean isShapeVisible()
+        String hash = li.getLinePaint().toString();
+        String label = li.getLabel();
+        if (label.startsWith("W (") || label.startsWith("W(")) {
+            hash += "-W-";
+        }
+        else if (label.startsWith("Q(") || label.startsWith("Q (")) {
+            hash += "-Q-";
+        }
+
+        // WQ.java holds example of using regex Matcher/Pattern.
+
+        return hash;
+    }
+
+    /** True if x axis has been inverted. */
+    public boolean isInverted() {
+        return inverted;
+    }
+
+
+    /** Set to true if x axis should be inverted.
+     * This can not be set to false afterwards. */
+    public void setInverted(boolean value) {
+        /* One request to invert dominates. */
+        if (!inverted) {
+            inverted = value;
+        }
+    }
+
+    @Override
+    public String getDefaultChartTitle() {
+        DiagramAttributes.Title dTitle = diagramAttributes.getTitle();
+        if (dTitle == null) {
+            return "Title not configured in conf.xml";
+        }
+
+        return dTitle.evaluate((D4EArtifact)getMaster(), context);
+    }
+
+    @Override
+    public String getDefaultChartSubtitle() {
+        DiagramAttributes.Title dTitle = diagramAttributes.getSubtitle();
+        if (dTitle == null) {
+            return "Subtitle not configured in conf.xml";
+        }
+
+        return dTitle.evaluate((D4EArtifact)getMaster(), context);
+    }
+
+    /**
+     * Get internationalized label for the x axis.
+     */
+    @Override
+    protected String getDefaultXAxisLabel() {
+        DiagramAttributes.Title dTitle = diagramAttributes.getDomainAxisTitle();
+        if (dTitle == null) {
+            return "Domain Axis Title not configured in conf.xml";
+        }
+
+        return dTitle.evaluate((D4EArtifact)getMaster(), context);
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(String axisName) {
+        Processor pr = diagramAttributes.getProcessorForAxisName(axisName);
+        return pr == null ? "" : pr.getAxisLabel(this);
+    }
+
+
+    /**
+     * Creates a list of Section for the chart's Y axes.
+     *
+     * @return a list of Y axis sections.
+     */
+    protected List<AxisSection> buildYAxisSections() {
+        List<AxisSection> axisSections = new ArrayList<AxisSection>();
+
+        List<DiagramAttributes.AxisAttributes> axesAttrs = diagramAttributes.getAxesAttributes();
+
+        for (int i = 0, n = axesAttrs.size(); i < n; i++) {
+            AxisSection ySection = new AxisSection();
+            String axisName = diagramAttributes.getAxisName(i);
+            ySection.setIdentifier(axisName);
+            ySection.setLabel(getYAxisLabel(axisName));
+            ySection.setFontSize(14);
+            ySection.setFixed(false);
+
+            // XXX We are able to find better default ranges that [0,0], the
+            // only problem is, that we do NOT have a better range than [0,0]
+            // for each axis, because the initial chart will not have a dataset
+            // for each axis set!
+            ySection.setUpperRange(0d);
+            ySection.setLowerRange(0d);
+
+            axisSections.add(ySection);
+        }
+
+        return axisSections;
+    }
+
+    /**
+     * Returns the Y-Axis label of a chart at position <i>pos</i>.
+     *
+     * @return the Y-Axis label of a chart at position <i>0</i>.
+     */
+    protected String getYAxisLabel(String axisName) {
+        ChartSettings chartSettings = getChartSettings();
+        if (chartSettings == null) {
+            return getDefaultYAxisLabel(axisName);
+        }
+        AxisSection as = chartSettings.getAxisSection(axisName);
+        if (as != null) {
+            String label = as.getLabel();
+            if (label != null) {
+                return label;
+            }
+        }
+
+        return getDefaultYAxisLabel(axisName);
+    }
+
+    protected String axisIndexToName(int index) {
+        return diagramAttributes.getAxisName(index);
+    }
+
+    /** Add the acutal data to the diagram according to the processors.
+     * For every outable facets, this function is
+     * called and handles the data accordingly. */
+    @Override
+    public void doOut(
+        ArtifactAndFacet bundle,
+        ThemeDocument    theme,
+        boolean          visible
+    ) {
+        String facetName = bundle.getFacetName();
+        Facet facet = bundle.getFacet();
+
+        /* A conservative security check */
+        if (facetName == null || facet == null) {
+            /* Can't happen,.. */
+            logger.error("doOut called with null facet.");
+            return;
+        }
+
+        logger.debug("DoOut for facet: " + facetName);
+
+        boolean found = false;
+        List<Processor> prL = diagramAttributes.getProcessors();
+        for (Processor pr: prL) {
+            if (pr.canHandle(facetName)) {
+                found = true;
+                pr.doOut(this, bundle, theme, visible);
+            }
+        }
+        if (!found) {
+            logger.warn("No processor found for: " + facetName);
+            if (logger.isDebugEnabled()) {
+                logger.debug("Configured processors for this diagram are:");
+                for (Processor pr: prL) {
+                    logger.debug(pr.getClass().getName());
+                }
+            }
+        }
+    }
+
+    @Override
+    protected NumberAxis createYAxis(int index) {
+        NumberAxis axis = super.createYAxis(index);
+
+        if (diagramAttributes.getAxesAttributes().get(index).includeZero()) {
+            axis.setAutoRangeIncludesZero(true);
+        }
+        return axis;
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,129 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.exports;
-
-import org.apache.log4j.Logger;
-
-import org.jfree.data.xy.XYSeries;
-
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Facet;
-
-import org.dive4elements.river.artifacts.model.FacetTypes;
-import org.dive4elements.river.artifacts.model.WQCKms;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.exports.process.QOutProcessor;
-import org.dive4elements.river.exports.process.WOutProcessor;
-
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.themes.ThemeDocument;
-
-
-
-/**
- * An OutGenerator that generates discharge longitudinal section curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DischargeLongitudinalSectionGenerator
-extends      LongitudinalSectionGenerator
-implements   FacetTypes
-{
-    private static Logger logger =
-        Logger.getLogger(DischargeLongitudinalSectionGenerator.class);
-
-
-    public DischargeLongitudinalSectionGenerator() {
-        super();
-    }
-
-
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactFacet,
-        ThemeDocument    attr,
-        boolean          visible
-    ) {
-        logger.debug("DischargeLongitudinalSectionGenerator.doOut");
-
-        String name = artifactFacet.getFacetName();
-
-        if (name == null) {
-            return;
-        }
-
-        Facet facet = artifactFacet.getFacet();
-
-        if (name.equals(DISCHARGE_LONGITUDINAL_C)) {
-            doCorrectedWOut(
-                (WQCKms) artifactFacet.getData(context),
-                facet,
-                attr,
-                visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations((RiverAnnotation) artifactFacet.getData(context),
-                 artifactFacet, attr, visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactFacet.getData(context),
-                artifactFacet,
-                attr, visible, YAXIS.W.idx);
-        }
-        else {
-            Processor processor = new WOutProcessor();
-            Processor qProcessor = new QOutProcessor();
-            if (processor.canHandle(name)) {
-                processor.doOut(this, artifactFacet, attr, visible, YAXIS.W.idx);
-            }
-            else if (qProcessor.canHandle(name)) {
-                qProcessor.doOut(this, artifactFacet, attr, visible, YAXIS.Q.idx);
-            }
-            else {
-                logger.warn("Unknown facet name: " + name);
-            }
-        }
-    }
-
-
-    /**
-     * Adds a new series for the corrected W curve.
-     *
-     * @param wqckms The object that contains the corrected W values.
-     * @param theme The theme that contains styling information.
-     */
-    protected void doCorrectedWOut(
-        WQCKms        wqckms,
-        Facet         facet,
-        ThemeDocument theme,
-        boolean       visible
-    ) {
-        logger.debug("DischargeLongitudinalSectionGenerator.doCorrectedWOut");
-
-        int size = wqckms.size();
-
-        if (size > 0) {
-            XYSeries series = new StyledXYSeries(
-                facet.getDescription(),
-                theme);
-
-            for (int i = 0; i < size; i++) {
-                series.add(wqckms.getKm(i), wqckms.getC(i));
-            }
-
-            addAxisSeries(series, YAXIS.W.idx, visible);
-        }
-
-        if (wqckms.guessWaterIncreasing()) {
-            setInverted(true);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionInfoGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific discharge
- * longitudinal section curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class DischargeLongitudinalSectionInfoGenerator
-extends      ChartInfoGenerator
-{
-    public DischargeLongitudinalSectionInfoGenerator() {
-        super(new DischargeLongitudinalSectionGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -18,8 +18,6 @@
 import java.text.DateFormat;
 import java.util.Locale;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
@@ -29,7 +27,6 @@
 import net.sf.jasperreports.engine.JasperPrint;
 import net.sf.jasperreports.engine.JRException;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.artifacts.common.utils.Config;
 
@@ -73,16 +70,10 @@
     /** The storage that contains all WQKms objects for the different facets. */
     protected List<WQDay> data;
 
-
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("DurationCurveExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WQDay>();
+    public DurationCurveExporter() {
+        data = new ArrayList<WQDay>();
     }
 
-
     @Override
     protected void addData(Object d) {
         if (d instanceof CalculationResult) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -13,14 +13,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.FlowVelocityData;
@@ -58,13 +54,10 @@
 
     protected List<FlowVelocityData[]> data;
 
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
+    public FlowVelocityExporter() {
         data = new ArrayList<FlowVelocityData[]>();
     }
 
-
     @Override
     protected void addData(Object d) {
         if (d instanceof CalculationResult) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -18,8 +18,6 @@
 import java.util.HashMap;
 import java.util.Locale;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
@@ -31,7 +29,6 @@
 
 import org.dive4elements.artifacts.common.utils.Config;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 
 import org.dive4elements.river.artifacts.D4EArtifact;
@@ -77,14 +74,10 @@
 
     protected List<WQTimerange[]> data;
 
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
-
+    public HistoricalDischargeCurveExporter() {
         data = new ArrayList<WQTimerange[]>();
     }
 
-
     @Override
     protected void addData(Object d) {
         logger.debug("Add data of class: " + d.getClass());
--- a/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java	Tue Sep 24 12:29:44 2013 +0200
@@ -48,12 +48,10 @@
 
     protected ChartGenerator generator;
 
-
     public InfoGeneratorHelper(ChartGenerator generator) {
         this.generator = generator;
     }
 
-
     /**
      * Triggers the creation of the chart info document.
      *
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper2.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,409 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import java.awt.geom.AffineTransform;
+import java.awt.geom.NoninvertibleTransformException;
+import java.awt.geom.Rectangle2D;
+
+import java.util.Date;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+import org.apache.log4j.Logger;
+
+import org.jfree.chart.ChartRenderingInfo;
+import org.jfree.chart.JFreeChart;
+import org.jfree.chart.axis.DateAxis;
+import org.jfree.chart.axis.NumberAxis;
+import org.jfree.chart.axis.ValueAxis;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.data.Range;
+import org.jfree.data.xy.XYDataset;
+
+import org.dive4elements.artifacts.common.ArtifactNamespaceContext;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
+
+import org.dive4elements.river.jfree.Bounds;
+
+
+/**
+ * This class helps generating chart info documents.
+ */
+public class InfoGeneratorHelper2 {
+
+    /** Private logging instance. */
+    private static final Logger logger =
+        Logger.getLogger(InfoGeneratorHelper2.class);
+
+    protected ChartGenerator2 generator;
+
+    public InfoGeneratorHelper2(ChartGenerator2 generator) {
+        this.generator = generator;
+    }
+
+    /**
+     * Triggers the creation of the chart info document.
+     *
+     * @param chart The JFreeChart chart.
+     * @param info An info object that has been created while chart creation.
+     *
+     * @return the info document.
+     */
+    public Document createInfoDocument(
+        JFreeChart         chart,
+        ChartRenderingInfo info)
+    {
+        logger.debug("InfoGeneratorHelper.createInfoDocument");
+
+        Document doc = XMLUtils.newDocument();
+
+        ElementCreator cr = new ElementCreator(
+            doc,
+            ArtifactNamespaceContext.NAMESPACE_URI,
+            ArtifactNamespaceContext.NAMESPACE_PREFIX);
+
+        Element chartinfo = cr.create("chartinfo");
+
+        chartinfo.appendChild(createAxesElements(cr, chart));
+        chartinfo.appendChild(createTransformationElements(cr, chart, info));
+
+        doc.appendChild(chartinfo);
+
+        return doc;
+    }
+
+
+    /**
+     * This method create a axes element that contains all domain and range
+     * axes of the given chart.
+     *
+     * @param cr The ElementCreator.
+     * @param chart The chart that provides range information of its axes.
+     *
+     * @return an element with axes information.
+     */
+    protected Element createAxesElements(
+        ElementCreator     cr,
+        JFreeChart         chart)
+    {
+        logger.debug("InfoGeneratorHelper.createRangeElements");
+
+        Element axes = cr.create("axes");
+
+        XYPlot plot = (XYPlot) chart.getPlot();
+
+        int dAxisCount = plot.getDomainAxisCount();
+        for (int i = 0; i < dAxisCount; i++) {
+            ValueAxis axis = plot.getDomainAxis(i);
+            XYDataset data = plot.getDataset(i);
+
+            if (axis != null) {
+                Element e = createAxisElement(cr, axis, data, "domain", i);
+                axes.appendChild(e);
+            }
+        }
+
+        int rAxisCount = plot.getRangeAxisCount();
+        for (int i = 0; i < rAxisCount; i++) {
+            ValueAxis axis = plot.getRangeAxis(i);
+            XYDataset data = plot.getDataset(i);
+
+            if (axis == null || data == null) {
+                logger.warn("Axis or dataset is empty at pos: " + i);
+                continue;
+            }
+
+            Element e = createAxisElement(cr, axis, data, "range", i);
+            axes.appendChild(e);
+        }
+
+        return axes;
+    }
+
+
+    /**
+     * This method create a axis element for a given <i>axis</i> and
+     * <i>type</i>. Type can be one of 'domain' or 'range'.
+     *
+     * @param cr The ElementCreator
+     * @param axis The axis that provides range information.
+     * @param dataset The dataset for min/max determination.
+     * @param type The axis type ('domain' or 'range').
+     * @param pos The position in the chart.
+     *
+     * @return An element that contains range information of a given axis.
+     */
+    protected Element createAxisElement(
+        ElementCreator cr,
+        ValueAxis      axis,
+        XYDataset      dataset,
+        String         type,
+        int            pos)
+    {
+        logger.debug("createAxisElement " + pos);
+        logger.debug("Axis is from type: " + axis.getClass());
+
+        Element e = cr.create(type);
+        cr.addAttr(e, "pos",  String.valueOf(pos), true);
+
+        if (axis instanceof DateAxis) {
+            prepareDateAxisElement(
+                e, cr, (DateAxis) axis, dataset, type, pos);
+        }
+        else {
+            prepareNumberAxisElement(
+                e, cr, (NumberAxis) axis, dataset, type, pos);
+        }
+
+        return e;
+    }
+
+
+    protected Element prepareNumberAxisElement(
+        Element        e,
+        ElementCreator cr,
+        NumberAxis     axis,
+        XYDataset      dataset,
+        String         type,
+        int            pos
+    ) {
+        Range range = axis.getRange();
+
+        cr.addAttr(e, "from", String.valueOf(range.getLowerBound()), true);
+        cr.addAttr(e, "to",   String.valueOf(range.getUpperBound()), true);
+        cr.addAttr(e, "axistype", "number", true);
+
+        Range[] rs = generator.getRangesForAxis(pos);
+        Range   r  = null;
+
+        if (type.equals("range")) {
+            r = rs[1];
+        }
+        else {
+            r = rs[0];
+        }
+
+        cr.addAttr(e, "min", String.valueOf(r.getLowerBound()), true);
+        cr.addAttr(e, "max", String.valueOf(r.getUpperBound()), true);
+
+        return e;
+    }
+
+
+    protected Element prepareDateAxisElement(
+        Element        e,
+        ElementCreator cr,
+        DateAxis       axis,
+        XYDataset      dataset,
+        String         type,
+        int            pos
+    ) {
+        Date from = axis.getMinimumDate();
+        Date to   = axis.getMaximumDate();
+
+        Bounds bounds = null;
+        if (type.equals("range")) {
+            bounds = generator.getYBounds(pos);
+        }
+        else {
+            bounds = generator.getXBounds(pos);
+        }
+
+        cr.addAttr(e, "axistype", "date", true);
+        cr.addAttr(e, "from", String.valueOf(from.getTime()), true);
+        cr.addAttr(e, "to", String.valueOf(to.getTime()), true);
+
+        cr.addAttr(e, "min", bounds.getLower().toString(), true);
+        cr.addAttr(e, "max", bounds.getUpper().toString(), true);
+
+        return e;
+    }
+
+
+    /**
+     * This method appends the values of a transformation matrix to transform
+     * image pixel coordinates into chart coordinates.
+     *
+     * @param cr The ElementCreator.
+     * @param chart The chart object.
+     * @param info The ChartRenderingInfo that is filled while chart creation.
+     *
+     * @return an element that contains one or more transformation matrix.
+     */
+    protected Element createTransformationElements(
+        ElementCreator     cr,
+        JFreeChart         chart,
+        ChartRenderingInfo info)
+    {
+        logger.debug("InfoGeneratorHelper.createTransformationElements");
+
+        Element tf = cr.create("transformation-matrix");
+
+        Rectangle2D dataArea = info.getPlotInfo().getDataArea();
+
+        XYPlot    plot  = (XYPlot) chart.getPlot();
+        ValueAxis xAxis = plot.getDomainAxis();
+
+        if (xAxis == null) {
+            logger.error("There is no x axis in the chart!");
+            return null;
+        }
+
+        for (int i  = 0, num = plot.getRangeAxisCount(); i < num; i++) {
+            ValueAxis yAxis = plot.getRangeAxis(i);
+
+            if (yAxis == null) {
+                logger.warn("No y axis at pos " + i + " existing.");
+                continue;
+            }
+
+            Element matrix = createTransformationElement(
+                cr, xAxis, yAxis, dataArea, i);
+
+            tf.appendChild(matrix);
+        }
+
+        return tf;
+    }
+
+
+    /**
+     * Creates an element that contains values used to transform coordinates
+     * of a coordinate system A into a coordinate system B.
+     *
+     * @param cr The ElementCreator.
+     * @param xAxis The x axis of the target coordinate system.
+     * @param yAxis The y axis of the target coordinate system.
+     * @param dataArea The pixel coordinates of the chart image.
+     * @param pos The dataset position.
+     *
+     * @return an element that contains transformation matrix values.
+     */
+    protected Element createTransformationElement(
+        ElementCreator cr,
+        ValueAxis      xAxis,
+        ValueAxis      yAxis,
+        Rectangle2D    dataArea,
+        int            pos)
+    {
+        double[] tm = createTransformationMatrix(dataArea, xAxis, yAxis);
+
+        Element matrix = cr.create("matrix");
+
+        cr.addAttr(matrix, "pos", String.valueOf(pos), true);
+        cr.addAttr(matrix, "sx", String.valueOf(tm[0]), true);
+        cr.addAttr(matrix, "sy", String.valueOf(tm[1]), true);
+        cr.addAttr(matrix, "tx", String.valueOf(tm[2]), true);
+        cr.addAttr(matrix, "ty", String.valueOf(tm[3]), true);
+
+        if (xAxis instanceof DateAxis) {
+            cr.addAttr(matrix, "xtype", "date", true);
+        }
+        else {
+            cr.addAttr(matrix, "xtype", "number", true);
+        }
+
+        if (yAxis instanceof DateAxis) {
+            cr.addAttr(matrix, "ytype", "date", true);
+        }
+        else {
+            cr.addAttr(matrix, "ytype", "number", true);
+        }
+
+        return matrix;
+    }
+
+
+    /**
+     * This method determines a transformation matrix to transform pixel
+     * coordinates of the chart image into chart coordinates.
+     *
+     * @param dataArea The rectangle that contains the data points of the chart.
+     * @param xAxis The x axis.
+     * @param yAxis The y axis.
+     *
+     * @return a double array as follows: [sx, sy, tx, ty].
+     */
+    protected static double[] createTransformationMatrix(
+        Rectangle2D dataArea,
+        ValueAxis   xAxis,
+        ValueAxis   yAxis)
+    {
+        logger.debug("InfoGeneratorHelper.createTransformationMatrix");
+
+        double offsetX = dataArea.getX();
+        double width   = dataArea.getWidth();
+        double offsetY = dataArea.getY();
+        double height  = dataArea.getHeight();
+
+        Range xRange = getRangeFromAxis(xAxis);
+        Range yRange = getRangeFromAxis(yAxis);
+
+        double lowerX  = xRange.getLowerBound();
+        double upperX  = xRange.getUpperBound();
+        double lowerY  = yRange.getLowerBound();
+        double upperY  = yRange.getUpperBound();
+
+        if (xAxis.isInverted()) {
+            logger.info("X-Axis is inverted!");
+
+            double tmp = upperX;
+            upperX = lowerX;
+            lowerX = tmp;
+        }
+
+        double dMoveX = upperX - lowerX;
+        double fMoveX = width * lowerX;
+        double dMoveY = lowerY - upperY;
+        double fMoveY = height * upperY;
+
+        AffineTransform t1 = AffineTransform.getTranslateInstance(
+                offsetX - ( fMoveX / dMoveX ),
+                offsetY - ( fMoveY / dMoveY ) );
+
+        AffineTransform t2 = AffineTransform.getScaleInstance(
+                width / (upperX - lowerX),
+                height / (lowerY - upperY));
+
+        t1.concatenate(t2);
+
+        try {
+            t1.invert();
+
+            double[] c = new double[6];
+            t1.getMatrix(c);
+
+            return new double[] { c[0], c[3], c[4], c[5] };
+        }
+        catch (NoninvertibleTransformException e) {
+            // do nothing
+            logger.warn("Matrix is not invertible.");
+        }
+
+        return new double[] { 1d, 1d, 0d, 0d };
+    }
+
+
+    protected static Range getRangeFromAxis(ValueAxis axis) {
+        if  (axis instanceof DateAxis) {
+            DateAxis dAxis = (DateAxis) axis;
+            Date     min   = dAxis.getMinimumDate();
+            Date     max   = dAxis.getMaximumDate();
+
+            return new Range(min.getTime(), max.getTime());
+        }
+        else {
+            return axis.getRange();
+        }
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -23,12 +23,11 @@
 import org.dive4elements.river.exports.process.BedheightProcessor;
 import org.dive4elements.river.exports.process.QOutProcessor;
 import org.dive4elements.river.exports.process.WOutProcessor;
+import org.dive4elements.river.exports.process.AnnotationProcessor;
 
-import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 import org.dive4elements.river.utils.RiverUtils;
 import org.apache.log4j.Logger;
 import org.jfree.chart.axis.NumberAxis;
@@ -340,11 +339,12 @@
             return;
         }
 
-        WOutProcessor wProcessor = new WOutProcessor();
-        QOutProcessor qProcessor = new QOutProcessor();
+        Processor wProcessor = new WOutProcessor();
+        Processor qProcessor = new QOutProcessor();
         Processor bedp = new BedheightProcessor();
         Processor bdyProcessor = new BedDiffYearProcessor();
         Processor bdhyProcessor = new BedDiffHeightYearProcessor();
+        Processor annotationProcessor = new AnnotationProcessor();
 
         if (wProcessor.canHandle(name)) {
             wProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
@@ -361,12 +361,8 @@
         else if (bdhyProcessor.canHandle(name)) {
            bdhyProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
         }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
+        else if (annotationProcessor.canHandle(name)) {
+            annotationProcessor.doOut(this, artifactAndFacet, attr, visible, 0);
         }
         else if (name.equals(W_DIFFERENCES)) {
             doWDifferencesOut(
@@ -429,36 +425,6 @@
 
 
     /**
-     * This method determines - taking JFreeCharts auto x value ordering into
-     * account - if the x axis need to be inverted. Waterlines in these charts
-     * should decrease.
-     *
-     * @param wkms The data object that stores the x and y values used for this
-     * chart.
-     */
-    public boolean needInvertAxis(WKms wkms) {
-        boolean wsUp = wkms.guessWaterIncreasing();
-        boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms());
-        boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp);
-
-        int size = wkms.size();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("(Wkms)Values  : " + size);
-            if (size > 0) {
-                logger.debug("Start km: " + wkms.getKm(0));
-                logger.debug("End   km: " + wkms.getKm(size-1));
-            }
-            logger.debug("wsUp: " + wsUp);
-            logger.debug("kmUp: " + kmUp);
-            logger.debug("inv:  " + inv);
-        }
-
-        return inv;
-    }
-
-
-    /**
      * Get name of series (displayed in legend).
      * @return name of the series.
      */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,42 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports;
+
+import org.apache.log4j.Logger;
+import org.jfree.chart.axis.NumberAxis;
+
+
+public class LongitudinalSectionGenerator2 extends DiagramGenerator
+{
+    public static final String I18N_CHART_SHORT_SUBTITLE =
+        "chart.longitudinal.section.shortsubtitle";
+
+    public static final String I18N_CHART_LOCATION_SUBTITLE =
+        "chart.longitudinal.section.locsubtitle";
+
+    private static Logger logger =
+        Logger.getLogger(LongitudinalSectionGenerator.class);
+
+    @Override
+    public String getDefaultChartSubtitle() {
+        double[] dist = getRange();
+
+        Object[] args = null;
+        if (dist == null || dist.length != 2 ||
+                Double.isNaN(dist[0]) || Double.isNaN(dist[1])) {
+            args = new Object[] {getRiverName()};
+            return msg(I18N_CHART_SHORT_SUBTITLE, "", args);
+        } else if (Math.abs(dist[0] - dist[1]) < 1E-5) {
+            args = new Object[] {getRiverName(), dist[1]};
+            return msg(I18N_CHART_LOCATION_SUBTITLE, "", args);
+        }
+        return super.getDefaultChartSubtitle();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionInfoGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * longitudinal section curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class LongitudinalSectionInfoGenerator
-extends      ChartInfoGenerator
-{
-    public LongitudinalSectionInfoGenerator() {
-        super(new LongitudinalSectionGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -65,12 +65,18 @@
 
     protected String srid;
 
-
+    protected String outName;
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void setup(Object config) {
+        logger.debug("MapGenerator.setup");
+    }
+
+    @Override
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
         logger.debug("MapGenerator.init");
 
+        this.outName  = outName;
         this.request  = request;
         this.out      = out;
         this.context  = context;
--- a/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -13,14 +13,10 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.MiddleBedHeightData;
@@ -64,13 +60,10 @@
 
     protected List<MiddleBedHeightData[]> data;
 
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
+    public MiddleBedHeightExporter() {
         data = new ArrayList<MiddleBedHeightData[]>();
     }
 
-
     @Override
     protected void addData(Object d) {
         if (d instanceof CalculationResult) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -29,15 +29,21 @@
 public interface OutGenerator {
 
     /**
+     * Pre-initialize generator from configuration.
+     */
+    void setup(Object config);
+
+    /**
      * Initializes the OutGenerator with meta information which are necessary
      * for the output generation.
      *
+     * @param outName The name of the out to serve.
      * @param request The incomding request document.
      * @param out     The output stream.
      * @param context The CallContext that provides further information and
      * objects used for the output generation.
      */
-    void init(Document request, OutputStream out, CallContext context);
+    void init(String outName, Document request, OutputStream out, CallContext context);
 
     /**
      * This method is used to tell the OutGenerator which artifact is the master
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -10,7 +10,6 @@
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 
 import org.dive4elements.artifacts.common.utils.Config;
@@ -46,8 +45,6 @@
 
 import org.apache.log4j.Logger;
 
-import org.w3c.dom.Document;
-
 /**
  * (CSV)Exporter for Reference Curves.
  */
@@ -91,17 +88,10 @@
 
     protected boolean endAtGauge = false;
 
-
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("ReferenceCurveExporter.init");
-
-        super.init(request, out, context);
-
+    public ReferenceCurveExporter() {
         this.data = new ArrayList<WWQQ[]>();
     }
 
-
     /**
      * Genereate data in csv format.
      */
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -36,13 +36,20 @@
     protected Document     result;
     protected OutputStream out;
     protected CallContext  context;
+    protected String       outName;
 
     public ReportGenerator() {
     }
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void setup(Object config) {
+        logger.debug("ReportGenerator.setup");
+    }
+
+    @Override
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
         logger.debug("init");
+        this.outName = outName;
         this.out     = out;
         this.context = context;
         result       = null;
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -29,9 +29,16 @@
     private D4EArtifactCollection collection;
     private String facet;
     private File dir;
+    private String outName;
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void setup(Object config) {
+        logger.debug("ShapeExporter.setup");
+    }
+
+    @Override
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        this.outName = outName;
         this.request = request;
         this.out = out;
         this.context = context;
--- a/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -16,6 +16,7 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledTimeSeries;
 import org.dive4elements.river.jfree.TimeBounds;
+import org.dive4elements.river.jfree.AxisDataset;
 import org.dive4elements.river.themes.ThemeDocument;
 
 import java.awt.Color;
@@ -60,126 +61,6 @@
 public abstract class TimeseriesChartGenerator
 extends               ChartGenerator {
 
-    /**
-     * Inner class TimeseriesAxisDataset stores TimeSeriesCollection.
-     */
-    public class TimeseriesAxisDataset implements AxisDataset {
-
-        protected int axisSymbol;
-
-        protected List<TimeSeriesCollection> datasets;
-
-        protected Range range;
-
-        protected int plotAxisIndex;
-
-        public TimeseriesAxisDataset(int axisSymbol) {
-            this.axisSymbol = axisSymbol;
-            this.datasets   = new ArrayList<TimeSeriesCollection>();
-        }
-
-
-        @Override
-        public void addDataset(XYDataset dataset) {
-            if (!(dataset instanceof TimeSeriesCollection)) {
-                logger.warn("Skip non TimeSeriesCollection dataset.");
-                return;
-            }
-
-            TimeSeriesCollection tsc = (TimeSeriesCollection) dataset;
-
-            datasets.add(tsc);
-            mergeRanges(tsc);
-        }
-
-
-        @Override
-        public XYDataset[] getDatasets() {
-            return datasets.toArray(new XYDataset[datasets.size()]);
-        }
-
-
-        @Override
-        public boolean isEmpty() {
-            return datasets.isEmpty();
-        }
-
-
-        @Override
-        public void setRange(Range range) {
-            this.range = range;
-        }
-
-
-        @Override
-        public Range getRange() {
-            return range;
-        }
-
-
-        @Override
-        public void setPlotAxisIndex(int plotAxisIndex) {
-            this.plotAxisIndex = plotAxisIndex;
-        }
-
-
-        @Override
-        public int getPlotAxisIndex() {
-            return plotAxisIndex;
-        }
-
-
-        @Override
-        public boolean isArea(XYDataset dataset) {
-            logger.warn("This AxisDataset doesn't support Areas yet!");
-            return false;
-        }
-
-
-        protected void mergeRanges(TimeSeriesCollection dataset) {
-            logger.debug("Range before merging: " + range);
-            Range subRange = null;
-
-            // Determine min/max of range axis.
-            for (int i = 0; i < dataset.getSeriesCount(); i++) {
-                if (dataset.getSeries(i).getItemCount() == 0) {
-                    continue;
-                }
-                double min = Double.MAX_VALUE;
-                double max = -Double.MAX_VALUE;
-                TimeSeries series = dataset.getSeries(i);
-                for (int j = 0; j < series.getItemCount(); j++) {
-                    double tmp = series.getValue(j).doubleValue();
-                    min = tmp < min ? tmp : min;
-                    max = tmp > max ? tmp : max;
-                }
-                if (subRange != null) {
-                    subRange = new Range(
-                        min < subRange.getLowerBound() ?
-                            min : subRange.getLowerBound(),
-                        max > subRange.getUpperBound() ?
-                            max : subRange.getUpperBound());
-                }
-                else {
-                    subRange = new Range(min, max);
-                }
-            }
-
-            // Avoid merging NaNs, as they take min/max place forever.
-            if (subRange == null ||
-                Double.isNaN(subRange.getLowerBound()) ||
-                Double.isNaN(subRange.getUpperBound())) {
-                return;
-            }
-            if (range == null) {
-                range = subRange;
-                return;
-            }
-            range = Range.combine(range, subRange);
-        }
-
-    } // end of TimeseriesAxisDataset class
-
     protected List<Marker> domainMarker;
 
     protected List<Marker> valueMarker;
@@ -389,14 +270,14 @@
 
 
     /**
-     * This method creates new instances of TimeseriesAxisDataset.
+     * This method creates new instances of AxisDataset.
      *
-     * @param idx The symbol for the new TimeseriesAxisDataset.
+     * @param idx The symbol for the new AxisDataset.
      */
     @Override
     protected AxisDataset createAxisDataset(int idx) {
         logger.debug("Create a new AxisDataset for index: " + idx);
-        return new TimeseriesAxisDataset(idx);
+        return new AxisDataset(idx);
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,169 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.exports;
-
-import org.apache.log4j.Logger;
-
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Facet;
-
-import org.dive4elements.river.artifacts.model.FacetTypes;
-import org.dive4elements.river.artifacts.model.WKms;
-
-import org.dive4elements.river.exports.process.BedDiffYearProcessor;
-import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
-import org.dive4elements.river.exports.process.BedheightProcessor;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.themes.ThemeDocument;
-
-import org.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.XYPlot;
-
-
-/**
- * An OutGenerator that generates w differences curves.
- */
-public class WDifferencesCurveGenerator
-extends      LongitudinalSectionGenerator
-implements   FacetTypes
-{
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(WDifferencesCurveGenerator.class);
-
-    public enum YAXIS {
-        W(0),
-        D(1),
-        Q(2);
-        protected int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-    /** Key for internationalized title of WDiff charts. */
-    public final static String I18N_WDIFF_TITLE = "chart.w_differences.title";
-
-    /** Default for internationalized title (when no translation found). */
-    public final static String I18N_WDIFF_TITLE_DEFAULT = "Differences";
-
-    public final static String I18N_WDIFF_SUBTITLE =
-        "chart.w_differences.subtitle";
-
-
-    @Override
-    protected YAxisWalker getYAxisWalker() {
-        return new YAxisWalker() {
-            @Override
-            public int length() {
-                return YAXIS.values().length;
-            }
-
-            @Override
-            public String getId(int idx) {
-                YAXIS[] yaxes = YAXIS.values();
-                return yaxes[idx].toString();
-            }
-        };
-    }
-
-
-    /**
-     * Get internationalized title for chart.
-     * @return internationalized Chart title.
-     */
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_WDIFF_TITLE, I18N_WDIFF_TITLE_DEFAULT);
-    }
-
-
-    @Override
-    protected String getDefaultChartSubtitle() {
-        return getRiverName();
-    }
-
-
-    /**
-     * Gets key to look up internationalized String for the charts subtitle.
-     * @return key to look up translated subtitle.
-     */
-    @Override
-    protected String getChartSubtitleKey() {
-        return I18N_WDIFF_SUBTITLE;
-    }
-
-
-    /** Handle additional facets (beddifferences). */
-    @Override
-    public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) {
-        super.doOut(bundle, attr, visible);
-
-        String name = bundle.getFacetName();
-        logger.debug("doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        Facet facet = bundle.getFacet();
-
-        if (facet == null) {
-            return;
-        }
-
-        Processor bedp = new BedheightProcessor();
-        Processor bdyProcessor = new BedDiffYearProcessor();
-        Processor bdhyProcessor = new BedDiffHeightYearProcessor();
-
-        if (bedp.canHandle(name)) {
-           bedp.doOut(this, bundle, attr, visible, YAXIS.W.idx);
-        }
-        else if (bdyProcessor.canHandle(name)) {
-           bdyProcessor.doOut(this, bundle, attr, visible, YAXIS.W.idx);
-        }
-        else if (bdhyProcessor.canHandle(name)) {
-           bdhyProcessor.doOut(this, bundle, attr, visible, YAXIS.W.idx);
-        }
-        else {
-            logger.warn("WDifferencesCurveGenerator.doOut: unknown facet type " + name);
-        }
-    }
-
-
-    /**
-     * Sets the zero base line visible.
-     */
-    @Override
-    public JFreeChart generateChart() {
-        JFreeChart chart = super.generateChart();
-        if (chart != null && chart.getPlot() != null) {
-            XYPlot plot = (XYPlot) chart.getPlot();
-            plot.setRangeZeroBaselineVisible(true);
-        }
-        return chart;
-    }
-
-
-    /**
-     * Get name of series (displayed in legend).
-     * @return name of the series.
-     */
-    protected String getSeriesName(WKms wqkms, String mode) {
-        String name   = wqkms.getName();
-        String prefix = (name != null && name.indexOf(mode) >= 0)
-                      ? null
-                      : mode;
-
-        return (prefix != null && prefix.length() > 0)
-                ? prefix + "(" + name +")"
-                : name;
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveInfoGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,23 +0,0 @@
-/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
- * Software engineering by Intevation GmbH
- *
- * This file is Free Software under the GNU AGPL (>=v3)
- * and comes with ABSOLUTELY NO WARRANTY! Check out the
- * documentation coming with Dive4Elements River for details.
- */
-
-package org.dive4elements.river.exports;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific
- * w differences.
- */
-public class WDifferencesCurveInfoGenerator
-extends      ChartInfoGenerator
-{
-    public WDifferencesCurveInfoGenerator() {
-        super(new WDifferencesCurveGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -19,8 +19,6 @@
 import java.text.DateFormat;
 import java.util.Locale;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
@@ -30,7 +28,6 @@
 import net.sf.jasperreports.engine.JasperPrint;
 import net.sf.jasperreports.engine.JRException;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.artifacts.common.utils.Config;
 
@@ -68,16 +65,10 @@
     /** The storage that contains all WKms objects for the different facets. */
     protected List<WKms[]> data;
 
-
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("WDifferencesExporter.init");
-
-        super.init(request, out, context);
-
-        this.data = new ArrayList<WKms[]>();
+    public WDifferencesExporter() {
+        data = new ArrayList<WKms[]>();
     }
 
-
     /**
      * Genereate data in csv format.
      */
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -21,8 +21,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
 import au.com.bytecode.opencsv.CSVWriter;
@@ -35,7 +33,6 @@
 import net.sf.jasperreports.engine.JRException;
 
 import org.dive4elements.artifacts.Artifact;
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.artifacts.common.utils.Config;
 
@@ -145,15 +142,10 @@
     /** The storage that contains official fixings if available.*/
     protected List<WQKms> officalFixings;
 
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("WaterlevelExporter.init");
-
-        super.init(request, out, context);
-
+    public WaterlevelExporter() {
         data = new ArrayList<WQKms[]>();
     }
 
-
     @Override
     public void generate()
     throws IOException
--- a/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -46,6 +46,8 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.jfree.AxisDataset;
+import org.dive4elements.river.jfree.AnnotationHelper;
 import org.dive4elements.river.themes.ThemeDocument;
 
 
@@ -66,115 +68,6 @@
  */
 public abstract class XYChartGenerator extends ChartGenerator {
 
-    public static class XYAxisDataset implements AxisDataset {
-        /** Symbolic integer, but also coding the priority (0 goes first). */
-        protected int axisSymbol;
-
-        /** List of assigned datasets (in order). */
-        protected List<XYDataset> datasets;
-
-        /** Range to use to include all given datasets. */
-        protected Range range;
-
-        /** Index of axis in plot. */
-        protected int plotAxisIndex;
-
-        /** Create AxisDataset. */
-        public XYAxisDataset(int symb) {
-            this.axisSymbol = symb;
-            datasets        = new ArrayList<XYDataset>();
-        }
-
-        /** Merge (or create given range with range so far (if any). */
-        private void mergeRanges(Range subRange) {
-            // Avoid merging NaNs, as they take min/max place forever.
-            if (subRange == null ||
-                Double.isNaN(subRange.getLowerBound()) ||
-                Double.isNaN(subRange.getUpperBound())) {
-                return;
-            }
-            if (range == null) {
-                range = subRange;
-                return;
-            }
-            range = Range.combine(range, subRange);
-        }
-
-
-        /** Add a dataset to internal list for this axis. */
-        @Override
-        public void addDataset(XYDataset dataset) {
-            datasets.add(dataset);
-            includeYRange(((XYSeriesCollection) dataset).getSeries(0));
-        }
-
-        /** Add a dataset, include its range. */
-        public void addDataset(XYSeries series) {
-            addDataset(new XYSeriesCollection(series));
-        }
-
-
-        /** Set Range for this axis. */
-        @Override
-        public void setRange(Range range) {
-            this.range = range;
-        }
-
-
-        /** Get Range for this axis. */
-        @Override
-        public Range getRange() {
-            return range;
-        }
-
-
-        /** Get Array of Datasets. */
-        @Override
-        public XYDataset[] getDatasets() {
-            return datasets.toArray(new XYDataset[datasets.size()]);
-        }
-
-
-        /** Add a Dataset that describes an area. */
-        public void addArea(StyledAreaSeriesCollection series) {
-            this.datasets.add(series);
-            List<?> allSeries = series.getSeries();
-            /* We do not include the bounds/ranges, if the area includes
-             * points at "infinity"/BIG_DOUBLE_VALUE, the charts extents are
-             * expanded to include these very small/big value.
-             * This is especially used when showing "area above axis". */
-        }
-
-        /** True if to be rendered as area. */
-        @Override
-        public boolean isArea(XYDataset series) {
-            return (series instanceof StyledAreaSeriesCollection);
-        }
-
-        /** Adjust range to include given dataset. */
-        public void includeYRange(XYSeries dataset) {
-            mergeRanges(new Range(dataset.getMinY(), dataset.getMaxY()));
-        }
-
-        /** True if no datasets given. */
-        @Override
-        public boolean isEmpty() {
-            return this.datasets.isEmpty();
-        }
-
-        /** Set the 'real' axis index that this axis is mapped to. */
-        @Override
-        public void setPlotAxisIndex(int axisIndex) {
-            this.plotAxisIndex = axisIndex;
-        }
-
-        /** Get the 'real' axis index that this axis is mapped to. */
-        @Override
-        public int getPlotAxisIndex() {
-            return this.plotAxisIndex;
-        }
-    } // class AxisDataset
-
     /** Enumerator over existing axes. */
     @Override
     protected abstract YAxisWalker getYAxisWalker();
@@ -258,7 +151,8 @@
         //debugAxis(plot);
 
         // These have to go after the autozoom.
-        addAnnotationsToRenderer(plot);
+        AnnotationHelper.addAnnotationsToRenderer(annotations, plot,
+                getChartSettings(), datasets);
 
         // Add a logo (maybe).
         addLogo(plot);
@@ -415,8 +309,8 @@
 
     @Override
     protected AxisDataset createAxisDataset(int idx) {
-        logger.debug("Create new XYAxisDataset for index: " + idx);
-        return new XYAxisDataset(idx);
+        logger.debug("Create new AxisDataset for index: " + idx);
+        return new AxisDataset(idx);
     }
 
 
@@ -481,7 +375,7 @@
             return;
         }
 
-        XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index);
+        AxisDataset axisDataset = (AxisDataset) getAxisDataset(index);
 
         if (visible) {
             axisDataset.addArea(area);
@@ -510,13 +404,6 @@
 
         addAxisDataset(new XYSeriesCollection(series), index, visible);
 
-        XYAxisDataset axisDataset = (XYAxisDataset) getAxisDataset(index);
-
-        if (!visible) {
-            // Do this also when not visible to have axis scaled by default such
-            // that every data-point could be seen (except for annotations).
-            axisDataset.includeYRange(series);
-        }
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -10,7 +10,6 @@
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 
 import org.dive4elements.river.artifacts.model.CalculationResult;
@@ -39,8 +38,6 @@
 
 import org.apache.log4j.Logger;
 
-import org.w3c.dom.Document;
-
 /** Exports fixation analysis deltaw(t) computation results to csv. */
 public class DeltaWtExporter
 extends      AbstractExporter
@@ -101,10 +98,7 @@
 
     protected List<KMIndex<QWD[]>> referenceEvents;
 
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        log.debug("DeltaWtExporter.init");
-        super.init(request, out, context);
+    public DeltaWtExporter() {
         analysisPeriods = new ArrayList<KMIndex<AnalysisPeriod []>>();
         referenceEvents = new ArrayList<KMIndex<QWD[]>>();
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -33,8 +33,8 @@
         "/art:action/art:attributes/art:currentKm/@art:km";
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        super.init(request, out, context);
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        super.init(outName, request, out, context);
 
         Double currentKm = getCurrentKmFromRequest(request);
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -462,8 +462,8 @@
 
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        super.init(request, out, context);
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        super.init(outName, request, out, context);
 
         Double currentKm = FixChartGenerator.getCurrentKmFromRequest(request);
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -15,7 +15,6 @@
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.FacetTypes;
-import org.dive4elements.river.artifacts.model.WKms;
 import org.dive4elements.river.artifacts.model.WQKms;
 import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriod;
 import org.dive4elements.river.artifacts.model.fixings.QWD;
@@ -28,7 +27,6 @@
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 import org.dive4elements.river.utils.KMIndex;
 import org.dive4elements.river.utils.RiverUtils;
 import org.jfree.chart.plot.Marker;
@@ -147,38 +145,7 @@
 
         addAxisSeries(series, YAXIS.Q.idx, visible);
 
-        if (needInvertAxis(wqkms)) {
-            setInverted(true);
-        }
-    }
-
-    /**
-     * This method determines - taking JFreeCharts auto x value ordering into
-     * account - if the x axis need to be inverted. Waterlines in these charts
-     * should decrease.
-     *
-     * @param wkms The data object that stores the x and y values used for this
-     * chart.
-     */
-    public boolean needInvertAxis(WKms wkms) {
-        boolean wsUp = wkms.guessWaterIncreasing();
-        boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms());
-        boolean inv = (wsUp && kmUp) || (!wsUp && !kmUp);
-
-        int size = wkms.size();
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("(Wkms)Values  : " + size);
-            if (size > 0) {
-                logger.debug("Start km: " + wkms.getKm(0));
-                logger.debug("End   km: " + wkms.getKm(size-1));
-            }
-            logger.debug("wsUp: " + wsUp);
-            logger.debug("kmUp: " + kmUp);
-            logger.debug("inv:  " + inv);
-        }
-
-        return inv;
+        setInverted(wqkms.guessRTLData());
     }
 
     @SuppressWarnings("unchecked")
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -10,8 +10,6 @@
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.Parameters;
 
@@ -31,8 +29,6 @@
 
 import org.apache.log4j.Logger;
 
-import org.w3c.dom.Document;
-
 public class ParametersExporter
 extends      AbstractExporter
 {
@@ -40,10 +36,7 @@
 
     protected List<Parameters> parametersList;
 
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        log.debug("ParametersExporter.init");
-        super.init(request, out, context);
+    public ParametersExporter() {
         parametersList = new ArrayList<Parameters>();
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -28,7 +28,6 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 import org.dive4elements.river.utils.RiverUtils;
 
 
@@ -257,7 +256,7 @@
         StyledSeriesBuilder.addPoints(series, wkms);
 
         addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+        if (wkms.guessWaterIncreasing()) {
             setInverted(true);
         }
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -27,7 +27,6 @@
 import net.sf.jasperreports.engine.JasperPrint;
 import net.sf.jasperreports.engine.JRException;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.access.BedDifferencesAccess;
@@ -40,11 +39,8 @@
 import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.RiverUtils;
 
-import org.w3c.dom.Document;
-
 import au.com.bytecode.opencsv.CSVWriter;
 
-
 public class BedDifferenceExporter
 extends AbstractExporter
 {
@@ -70,10 +66,7 @@
 
     private BedDifferencesResult[] results;
 
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("init");
-        super.init(request, out, context);
+    public BedDifferenceExporter() {
         results = new BedDifferencesResult[0];
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -28,7 +28,6 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 import org.dive4elements.river.utils.RiverUtils;
 
 
@@ -234,7 +233,7 @@
         StyledSeriesBuilder.addPoints(series, wkms);
 
         addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+        if (wkms.guessWaterIncreasing()) {
             setInverted(true);
         }
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -19,10 +19,8 @@
 import java.util.List;
 
 import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
 
 import au.com.bytecode.opencsv.CSVWriter;
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult;
 import org.dive4elements.river.artifacts.model.minfo.BedParametersResult;
@@ -56,10 +54,7 @@
 
     private BedQualityResult[] results;
 
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        logger.debug("BedQualityExporter.init");
-        super.init(request, out, context);
+    public BedQualityExporter() {
         results = new BedQualityResult[0];
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -17,12 +17,8 @@
 
 import java.text.NumberFormat;
 
-import org.w3c.dom.Document;
-
 import org.apache.log4j.Logger;
 
-import org.dive4elements.artifacts.CallContext;
-
 import org.dive4elements.river.artifacts.model.CalculationResult;
 import org.dive4elements.river.artifacts.model.minfo.SedimentLoad;
 import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFraction;
@@ -78,17 +74,9 @@
 
     /** Empty constructor. */
     public SedimentLoadExporter() {
-    }
-
-    /** Trivial init. */
-    @Override
-    public void init(Document request, OutputStream out, CallContext context) {
-        super.init(request, out, context);
-        logger.debug("init");
         results = new SedimentLoadResult[0];
     }
 
-
     /** Process all stored data and write csv. */
     @Override
     protected void writeCSVData(CSVWriter writer) throws IOException {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -33,7 +33,6 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 
 
 /** Generator for Longitudinal Sections of SedimentLoad-Calculations. */
@@ -355,7 +354,7 @@
         StyledSeriesBuilder.addPoints(series, wkms);
 
         addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+        if (wkms.guessWaterIncreasing()) {
             setInverted(true);
         }
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,84 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports.process;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.themes.ThemeDocument;
+
+/**
+ * Add data to chart/generator.
+ *
+ */
+public class AnnotationProcessor extends DefaultProcessor {
+
+    /** Private logger. */
+    private static final Logger logger =
+            Logger.getLogger(AnnotationProcessor.class);
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        if (!visible) {
+            // Nothing to do
+            return;
+        }
+        CallContext context = generator.getCallContext();
+        if (!(bundle.getData(context) instanceof RiverAnnotation)) {
+            // Just a bit defensive should not happen
+            logger.error("Incompatible facet in doOut");
+            return;
+        }
+        RiverAnnotation ra = (RiverAnnotation)bundle.getData(context);
+        ra.setTheme(theme);
+        ra.setLabel(bundle.getFacetDescription());
+        generator.addAnnotations(ra);
+    }
+
+    @Override
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible,
+            int              index)
+    {
+        if (!visible) {
+            // Nothing to do
+            return;
+        }
+        CallContext context = generator.getCallContext();
+        if (!(bundle.getData(context) instanceof RiverAnnotation)) {
+            // Just a bit defensive should not happen
+            logger.error("Incompatible facet in doOut");
+            return;
+        }
+        RiverAnnotation ra = (RiverAnnotation)bundle.getData(context);
+        ra.setTheme(theme);
+        ra.setLabel(bundle.getFacetDescription());
+        generator.addAnnotations(ra);
+    }
+
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+        return facetType.equals(FacetTypes.LONGITUDINAL_ANNOTATION);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,154 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+
+package org.dive4elements.river.exports.process;
+
+import org.apache.log4j.Logger;
+
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.geom.Lines;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.AreaFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.jfree.data.xy.XYSeries;
+
+public class AreaProcessor extends DefaultProcessor {
+
+    private static final Logger logger = Logger.getLogger(ManualPointsProcessor.class);
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        String seriesName = bundle.getFacetDescription();
+        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
+
+        logger.debug("Area Processor processing: " + seriesName);
+
+        AreaFacet.Data data = (AreaFacet.Data) bundle.getData(context);
+
+        XYSeries up   = null;
+        XYSeries down = null;
+
+        if (data.getUpperData() != null) {
+            up = new StyledXYSeries(seriesName, false, theme);
+            if (data.getUpperData() instanceof WQKms) {
+                if (FacetTypes.IS.Q(data.getRootFacetName())) {
+                    StyledSeriesBuilder.addPointsKmQ(up, (WQKms) data.getUpperData());
+                }
+                else {
+                    StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData());
+                }
+            }
+            else if (data.getUpperData() instanceof double[][]) {
+                StyledSeriesBuilder.addPoints(up, (double [][]) data.getUpperData(), false);
+            }
+            else if (data.getUpperData() instanceof WKms) {
+                StyledSeriesBuilder.addPoints(up, (WKms) data.getUpperData());
+            }
+            else if (data.getUpperData() instanceof Lines.LineData) {
+                StyledSeriesBuilder.addPoints(up, ((Lines.LineData) data.getUpperData()).points, false);
+            }
+            else {
+                logger.error("Do not know how to deal with (up) area info from: "
+                        + data.getUpperData());
+            }
+        }
+
+        // TODO Depending on style, the area (e.g. 20m^2) should be added as annotation.
+
+        if (data.getLowerData() != null) {
+            // TODO: Sort this out: when the two series have the same name,
+            // the renderer (or anything in between) will not work correctly.
+            down = new StyledXYSeries(seriesName + " ", false, theme);
+            if (data.getLowerData() instanceof WQKms) {
+                if (FacetTypes.IS.Q(data.getRootFacetName())) {
+                    StyledSeriesBuilder.addPointsKmQ(down, (WQKms) data.getLowerData());
+                }
+                else {
+                    StyledSeriesBuilder.addPoints(down, (WQKms) data.getLowerData());
+                }
+            }
+            else if (data.getLowerData() instanceof double[][]) {
+                StyledSeriesBuilder.addPoints(down, (double[][]) data.getLowerData(), false);
+            }
+            else if (data.getLowerData() instanceof WKms) {
+                StyledSeriesBuilder.addPoints(down, (WKms) data.getLowerData());
+            }
+            else if (data.getLowerData() instanceof Lines.LineData) {
+                StyledSeriesBuilder.addPoints(down, ((Lines.LineData) data.getLowerData()).points, false);
+            }
+            else {
+                logger.error("Do not know how to deal with (down) area info from: "
+                        + data.getLowerData());
+            }
+        }
+
+        if (up == null && down != null) {
+            area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE);
+            down.setKey(seriesName);
+            area.addSeries(down);
+            area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(down));
+        }
+        else if (up != null && down == null) {
+            area.setMode(StyledAreaSeriesCollection.FILL_MODE.UNDER);
+            area.addSeries(up);
+            area.addSeries(StyledSeriesBuilder.createGroundAtInfinity(up));
+        }
+        else if (up != null && down != null) {
+            if (data.doPaintBetween()) {
+                area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN);
+            }
+            else {
+                area.setMode(StyledAreaSeriesCollection.FILL_MODE.ABOVE);
+            }
+            area.addSeries(up);
+            area.addSeries(down);
+        }
+
+        /* Decide axis name based on facet name */
+        generator.addAreaSeries(area,
+                axisNameForFacet(data.getRootFacetName()), visible);
+    }
+
+    /** Look up the axis identifier for a given facet type. */
+    private String axisNameForFacet(String facetName) {
+        if (FacetTypes.IS.W(facetName)) {
+            return "W";
+        }
+        else if (FacetTypes.IS.Q(facetName)) {
+            return "Q";
+        }
+        else {
+            logger.warn("Could not find axis for facet " + facetName);
+            return "W";
+        }
+    }
+
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+        return FacetTypes.IS.AREA(facetType);
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -16,12 +16,14 @@
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult;
 import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.exports.XYChartGenerator;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
 
 
-public class BedDiffHeightYearProcessor implements Processor, FacetTypes {
+public class BedDiffHeightYearProcessor
+extends DefaultProcessor implements FacetTypes {
 
     private final static Logger logger =
             Logger.getLogger(BedDiffHeightYearProcessor.class);
@@ -30,21 +32,43 @@
 
     @Override
     public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        Object data = bundle.getData(context);
+        if (data instanceof BedDiffYearResult) {
+            BedDiffYearResult bData = (BedDiffYearResult) data;
+            XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+            StyledSeriesBuilder.addPoints(series, bData.getHeightPerYearData(), false, GAP_TOLERANCE);
+
+            generator.addAxisSeries(series, axisName, visible);
+            return;
+        }
+        // Should not happen if canHandle is correct
+        logger.error("Can't process " + data.getClass().getName() + " objects");
+    }
+
+    @Override
+    public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aandf,
+            ArtifactAndFacet bundle,
             ThemeDocument theme,
             boolean visible,
             int index
     ) {
         CallContext context = generator.getCallContext();
-        Object data = aandf.getData(context);
+        Object data = bundle.getData(context);
         if (data instanceof BedDiffYearResult) {
-            doBedDifferenceYearOut(
-               generator,
-               (BedDiffYearResult) data,
-               aandf, theme, visible, index);
+            BedDiffYearResult bData = (BedDiffYearResult) data;
+            XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+            StyledSeriesBuilder.addPoints(series, bData.getHeightPerYearData(), false, GAP_TOLERANCE);
+
+            generator.addAxisSeries(series, index, visible);
             return;
         }
+        // Should not happen if canHandle is correct
         logger.error("Can't process " + data.getClass().getName() + " objects");
     }
 
@@ -58,12 +82,12 @@
 
     protected void doBedDifferenceYearOut(XYChartGenerator generator,
         BedDiffYearResult data,
-        ArtifactAndFacet aandf,
+        ArtifactAndFacet bundle,
         ThemeDocument theme,
         boolean visible,
         int axidx) {
 
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
         StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), false, GAP_TOLERANCE);
 
         generator.addAxisSeries(series, axidx, visible);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -17,11 +17,13 @@
 import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult;
 import org.dive4elements.river.exports.StyledSeriesBuilder;
 import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
 
 
-public class BedDiffYearProcessor implements Processor, FacetTypes {
+public class BedDiffYearProcessor
+extends DefaultProcessor implements FacetTypes {
 
     private final static Logger logger =
             Logger.getLogger(BedDiffYearProcessor.class);
@@ -30,23 +32,65 @@
 
     @Override
     public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        Object data = bundle.getData(context);
+        if (data instanceof BedDiffYearResult) {
+            String facetType = bundle.getFacetName();
+            BedDiffYearResult bData = (BedDiffYearResult) data;
+
+            double[][] points;
+            if (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType)) {
+                points = bData.getHeights1Data();
+            } else {
+                points = bData.getHeights2Data();
+            }
+            XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+            StyledSeriesBuilder.addPointsFactorY(series,
+                points,
+                false,
+                GAP_TOLERANCE,
+                100d);
+
+            generator.addAxisSeries(series, axisName, visible);
+
+            return;
+        }
+        logger.error("Can't process " + data.getClass().getName() + " objects");
+    }
+
+    @Override
+    public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aandf,
+            ArtifactAndFacet bundle,
             ThemeDocument theme,
             boolean visible,
             int axidx
     ) {
         CallContext context = generator.getCallContext();
-        Object data = aandf.getData(context);
+        Object data = bundle.getData(context);
         if (data instanceof BedDiffYearResult) {
-            String facetType = aandf.getFacetName();
-            int index = 
-               (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType)
-               || BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED.equals(facetType))?0:1;
-            doBedDifferenceHeightsOut(
-               generator,
-               (BedDiffYearResult) data,
-               aandf, theme, visible, index, axidx);
+            String facetType = bundle.getFacetName();
+            BedDiffYearResult bData = (BedDiffYearResult) data;
+
+            double[][] points;
+            if (BED_DIFFERENCE_YEAR_HEIGHT1.equals(facetType)) {
+                points = bData.getHeights1Data();
+            } else {
+                points = bData.getHeights2Data();
+            }
+            XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+            StyledSeriesBuilder.addPointsFactorY(series,
+                points,
+                false,
+                GAP_TOLERANCE,
+                100d);
+
+            generator.addAxisSeries(series, axidx, visible);
+
             return;
         }
         logger.error("Can't process " + data.getClass().getName() + " objects");
@@ -60,33 +104,5 @@
             || BED_DIFFERENCE_YEAR_HEIGHT1_FILTERED.equals(facetType)
             || BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED.equals(facetType);
     }
-
-    private void doBedDifferenceHeightsOut(
-        XYChartGenerator generator,
-        BedDiffYearResult data,
-        ArtifactAndFacet bundle,
-        ThemeDocument attr,
-        boolean visible,
-        int idx,
-        int axidx) {
-         logger.debug("doBedDifferenceHeightsOut()");
-
-        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
-        double[][] points;
-        if (idx == 0) {
-            points = data.getHeights1Data();
-        }
-        else {
-            points = data.getHeights2Data();
-        }
-
-        StyledSeriesBuilder.addPointsFactorY(series,
-            points,
-            false,
-            GAP_TOLERANCE,
-            100d);
-
-        generator.addAxisSeries(series, axidx, visible);
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -17,11 +17,12 @@
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.minfo.BedHeightSingle;
 import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.model.BedHeightSingleValue;
 import org.dive4elements.river.themes.ThemeDocument;
 
-public class BedheightProcessor implements Processor {
+public class BedheightProcessor extends DefaultProcessor {
 
     private final static Logger logger =
             Logger.getLogger(BedheightProcessor.class);
@@ -30,34 +31,66 @@
 
     @Override
     public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        Object data = bundle.getData(context);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        if (data instanceof BedHeightSingle) {
+            BedHeightSingle bData = (BedHeightSingle)data;
+            double[] width = bData.getMorphWidths();
+            double[] stations = bData.getStations().toNativeArray();
+
+            logger.debug("doBedheightSingleOut");
+
+            for (int i = 0; i < width.length; i++) {
+                series.add(stations[i], width[i], false);
+            }
+        } else if (data instanceof List<?>) {
+            List<BedHeightSingleValue> bData = (List<BedHeightSingleValue>)data;
+
+            logger.debug("doBedheightSingleValueOut");
+
+            for(BedHeightSingleValue bvalue: bData) {
+                series.add(bvalue.getStation(), bvalue.getSoundingWidth());
+            }
+        }
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aandf,
+            ArtifactAndFacet bundle,
             ThemeDocument theme,
             boolean visible,
             int index
     ) {
         CallContext context = generator.getCallContext();
-        Object data = aandf.getData(context);
+        Object data = bundle.getData(context);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
         if (data instanceof BedHeightSingle) {
-            doBedheightSingleOut(generator, aandf, theme, index, visible,
-                    (BedHeightSingle)data);
-        }
-        else if (data instanceof List<?>) {
-            doBedheightSingeValuesOut(generator, aandf, theme, index, visible,
-                    (List<BedHeightSingleValue>)data);
-        }
-        logger.error("Can't process " + data.getClass().getName() + " objects");
-    }
+            BedHeightSingle bData = (BedHeightSingle)data;
+            double[] width = bData.getMorphWidths();
+            double[] stations = bData.getStations().toNativeArray();
 
-    private void doBedheightSingeValuesOut(XYChartGenerator generator,
-            ArtifactAndFacet aandf, ThemeDocument theme, int index, boolean visible,
-            List<BedHeightSingleValue> data) {
-        logger.debug("doBedheightSingeValuesOut");
+            logger.debug("doBedheightSingleOut");
 
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(),
-                theme);
-        for(BedHeightSingleValue bvalue: data) {
-            series.add(bvalue.getStation(), bvalue.getSoundingWidth());
+            for (int i = 0; i < width.length; i++) {
+                series.add(stations[i], width[i], false);
+            }
+        } else if (data instanceof List<?>) {
+            List<BedHeightSingleValue> bData = (List<BedHeightSingleValue>)data;
+
+            logger.debug("doBedheightSingleValueOut");
+
+            for(BedHeightSingleValue bvalue: bData) {
+                series.add(bvalue.getStation(), bvalue.getSoundingWidth());
+            }
         }
         generator.addAxisSeries(series, index, visible);
     }
@@ -66,22 +99,4 @@
     public boolean canHandle(String facettype) {
         return BEDHEIGHT.equals(facettype);
     }
-
-    public void doBedheightSingleOut(XYChartGenerator generator,
-            ArtifactAndFacet aandf, ThemeDocument theme, int index, boolean visible,
-            BedHeightSingle data) {
-        double[] width = data.getMorphWidths();
-        double[] stations = data.getStations().toNativeArray();
-
-        logger.debug("doBedheightSingleOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(),
-                theme);
-
-        for (int i = 0; i < width.length; i++) {
-            series.add(stations[i], width[i], false);
-        }
-
-        generator.addAxisSeries(series, index, visible);
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DefaultProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,75 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports.process;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.themes.ThemeDocument;
+
+/** Dummy implementation for the Processor interface.
+ */
+public class DefaultProcessor implements Processor {
+
+    protected String axisName;
+
+    public void setAxisName(String axisName) {
+        this.axisName = axisName;
+    }
+
+    public String getAxisName() {
+        return axisName;
+    }
+
+    /**
+     * Processes data to generate e.g. a chart.
+     *
+     * @param generator XYChartGenerator to add output on.
+     * @param bundle       The artifact and facet
+     * @param theme      The theme that contains styling information.
+     * @param visible       The visibility of the curve.
+     * @param index        The index of the curve
+     */
+    @Override
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible,
+            int              index) {
+        return;
+    }
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        return;
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return "Please overwrite me in the implementation";
+    }
+
+
+    /**
+     * Returns true if the Processor class is able to generate output for a facet type
+     *
+     * @param facettype Name of the facet type
+     * @return true if the facettype can be processed
+     */
+    @Override
+    public boolean canHandle(String facettype)
+    {
+        return false;
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DischargeProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -31,7 +31,7 @@
 
 /** Helper for data handling in discharge diagrams. */
 public class DischargeProcessor
-implements   Processor, FacetTypes {
+extends DefaultProcessor implements FacetTypes {
 
     private final static Logger logger =
             Logger.getLogger(DischargeProcessor.class);
@@ -58,32 +58,32 @@
     @Override
     public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aandf,
+            ArtifactAndFacet bundle,
             ThemeDocument theme,
             boolean visible,
             int axisIndex
     ) {
         CallContext context = generator.getCallContext();
-        Object data = aandf.getData(context);
+        Object data = bundle.getData(context);
         if (false && data instanceof WQKms) {
             doWQKmsPointOut(
-                generator, (WQKms) data, aandf, theme, visible, axisIndex);
+                generator, (WQKms) data, bundle, theme, visible, axisIndex);
             return;
         }
         else if (data instanceof RiverAnnotation) {
             doRiverAnnotationOut(
-                generator, (RiverAnnotation) data, aandf, theme, visible);
+                generator, (RiverAnnotation) data, bundle, theme, visible);
             return;
         }
         else if (data instanceof double[][]) {
             doMarksOut(
-                generator, (double[][]) data, aandf, theme, visible);
+                generator, (double[][]) data, bundle, theme, visible);
             return;
         }
         else {
             logger.error("Can't process "
                 + data.getClass().getName() + " objects of facet "
-                + aandf.getFacetName());
+                + bundle.getFacetName());
         }
     }
 
@@ -109,13 +109,13 @@
     /** Handle WQKms data by finding w/q values at given km. */
     protected void doWQKmsPointOut(XYChartGenerator generator,
         WQKms wqkms,
-        ArtifactAndFacet aandf,
+        ArtifactAndFacet bundle,
         ThemeDocument theme,
         boolean visible,
         int axidx
     ) {
         logger.debug("doWQKmsPointOut");
-        String title = aandf.getFacetDescription();
+        String title = bundle.getFacetDescription();
         XYSeries series = new StyledXYSeries(
             title,
             theme);
@@ -147,7 +147,7 @@
 
     protected void doRiverAnnotationOut(XYChartGenerator generator,
         RiverAnnotation annotations,
-        ArtifactAndFacet aandf,
+        ArtifactAndFacet bundle,
         ThemeDocument theme,
         boolean visible
     ) {
@@ -163,7 +163,7 @@
         dGenerator.translateRiverAnnotation(annotations);
         dGenerator.doAnnotations(
             annotations,
-            aandf, theme, visible);
+            bundle, theme, visible);
     }
 
 
@@ -173,7 +173,7 @@
      */
     protected void doMarksOut(XYChartGenerator generator,
         double[][] data,
-        ArtifactAndFacet aandf,
+        ArtifactAndFacet bundle,
         ThemeDocument theme,
         boolean visible
     ) {
@@ -184,7 +184,7 @@
         }
 
         // TODO subtract gauge null point if at gauge.
-        String title = aandf.getFacetDescription();
+        String title = bundle.getFacetDescription();
         List<StickyAxisAnnotation> yMarks = new ArrayList<StickyAxisAnnotation>();
 
         for (double yPos: data[1]) {
@@ -195,7 +195,7 @@
         }
 
         generator.doAnnotations(new RiverAnnotation(title, yMarks),
-            aandf, theme, visible);
+            bundle, theme, visible);
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -22,22 +22,22 @@
 import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.KMIndex;
 
-public class KMIndexProcessor implements Processor {
+public class KMIndexProcessor extends DefaultProcessor {
 
     private static final Logger logger = Logger.getLogger(KMIndexProcessor.class);
 
     @Override
-    public void doOut(XYChartGenerator generator, ArtifactAndFacet aandf,
+    public void doOut(XYChartGenerator generator, ArtifactAndFacet bundle,
             ThemeDocument theme, boolean visible, int index) {
-        String facettype = aandf.getFacetName();
+        String facettype = bundle.getFacetName();
         if (facettype.contains(FacetTypes.FIX_SECTOR_AVERAGE_LS)) {
-            doSectorAverageOut(generator, aandf, theme, visible, index);
+            doSectorAverageOut(generator, bundle, theme, visible, index);
         }
         else if (facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)) {
-            doReferenceEventsOut(generator, aandf, theme, visible, index);
+            doReferenceEventsOut(generator, bundle, theme, visible, index);
         }
         else if (facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)) {
-            doAnalysisEventsOut(generator, aandf, theme, visible, index);
+            doAnalysisEventsOut(generator, bundle, theme, visible, index);
         }
 
     }
@@ -57,23 +57,23 @@
         return false;
     }
 
-    private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet aaf,
+    private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet bundle,
             ThemeDocument doc, boolean visible, int idx) {
-        logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex());
+        logger.debug("doSectorAverageOut" + bundle.getFacet().getIndex());
 
         CallContext context = generator.getCallContext();
-        int index = aaf.getFacet().getIndex();
+        int index = bundle.getFacet().getIndex();
         int sectorNdx = index & 3;
 
         @SuppressWarnings("unchecked")
         KMIndex<AnalysisPeriod> kms =
-                (KMIndex<AnalysisPeriod>)aaf.getData(context);
+                (KMIndex<AnalysisPeriod>)bundle.getData(context);
 
         if(kms == null) {
             return;
         }
 
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), doc);
 
         for (KMIndex.Entry<AnalysisPeriod> entry: kms) {
             double km = entry.getKm();
@@ -90,14 +90,14 @@
     }
 
     private void doReferenceEventsOut(XYChartGenerator generator,
-            ArtifactAndFacet aaf, ThemeDocument doc, boolean visible, int idx) {
+            ArtifactAndFacet bundle, ThemeDocument doc, boolean visible, int idx) {
         logger.debug("doReferenceEventOut");
 
         CallContext context = generator.getCallContext();
 
         @SuppressWarnings("unchecked")
         KMIndex<QWD> kms =
-                (KMIndex<QWD>)aaf.getData(context);
+                (KMIndex<QWD>)bundle.getData(context);
 
         if(kms == null) {
             return;
@@ -105,7 +105,7 @@
 
         XYSeriesCollection col = new XYSeriesCollection();
 
-        StyledXYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false,
+        StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false,
                 doc);
 
         for (KMIndex.Entry<QWD> entry: kms) {
@@ -120,14 +120,14 @@
     }
 
     private void doAnalysisEventsOut(XYChartGenerator generator,
-            ArtifactAndFacet aaf, ThemeDocument doc, boolean visible, int idx) {
+            ArtifactAndFacet bundle, ThemeDocument doc, boolean visible, int idx) {
         logger.debug("doAnalysisEventsOut");
 
         CallContext context = generator.getCallContext();
 
         @SuppressWarnings("unchecked")
         KMIndex<QWD> kms =
-                (KMIndex<QWD>)aaf.getData(context);
+                (KMIndex<QWD>)bundle.getData(context);
 
         if(kms == null) {
             return;
@@ -135,7 +135,7 @@
 
         XYSeriesCollection col = new XYSeriesCollection();
 
-        StyledXYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, doc);
+        StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false, doc);
 
         for (KMIndex.Entry<QWD> entry: kms) {
             double km = entry.getKm();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/ManualPointsProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,87 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+
+package org.dive4elements.river.exports.process;
+
+import org.apache.log4j.Logger;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+public class ManualPointsProcessor extends DefaultProcessor {
+
+    private static final Logger logger = Logger.getLogger(ManualPointsProcessor.class);
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        String seriesName = bundle.getFacetDescription();
+        XYSeries series = new StyledXYSeries(seriesName, theme);
+        String jsonData = (String) bundle.getData(context);
+
+        // Add text annotations for single points.
+        List<XYTextAnnotation> xy = new ArrayList<XYTextAnnotation>();
+
+        try {
+            JSONArray points = new JSONArray(jsonData);
+            for (int i = 0, P = points.length(); i < P; i++) {
+                JSONArray array = points.getJSONArray(i);
+                double x    = array.getDouble(0);
+                double y    = array.getDouble(1);
+                String name = array.getString(2);
+                boolean act = array.getBoolean(3);
+                if (!act) {
+                    continue;
+                }
+                //logger.debug(" x " + x + " y " + y );
+                series.add(x, y, false);
+                xy.add(new CollisionFreeXYTextAnnotation(name, x, y));
+            }
+        }
+        catch(JSONException e){
+            logger.error("Could not decode json.");
+        }
+
+        RiverAnnotation annotation = new RiverAnnotation(null, null, null, theme);
+        annotation.setTextAnnotations(xy);
+
+        if (visible) {
+            generator.addAnnotations(annotation);
+        }
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+        return FacetTypes.IS.MANUALPOINTS(facetType);
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -11,6 +11,7 @@
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.river.exports.XYChartGenerator;
 import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.exports.DiagramGenerator;
 
 /**
  * A processor is intended to generate an output e.g. curve in a chart diagramm from
@@ -19,19 +20,55 @@
  * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
  */
 public interface Processor {
+    /**
+     * Set the axis for this processor.
+     * This should be done before doOut is called for the first time.
+     *
+     * @param axisName The name of the Axis this processor should use.
+     */
+    public void setAxisName(String axisName);
+
+    /**
+     * Get the axis for this processor.
+     *
+     * @return The name of the axis that is used.
+     */
+    public String getAxisName();
+
+    /**
+     * Get the axis label for this processor.
+     *
+     * @return The label of the axis.
+     */
+    public String getAxisLabel(DiagramGenerator generator);
 
     /**
      * Processes data to generate e.g. a chart.
      *
      * @param generator XYChartGenerator to add output on.
-     * @param aandf       The artifact and facet
-     * @param theme      The theme that contains styling information.
-     * @param visible       The visibility of the curve.
-     * @param index        The index of the curve
+     * @param bundle     The artifact and facet
+     * @param theme     The theme that contains styling information.
+     * @param visible   The visibility of the curve.
      */
     public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible);
+
+    /**
+     * Processes data to generate e.g. a chart.
+     *
+     * @param generator DiagramGenerator to add output on.
+     * @param bundle     The artifact and facet
+     * @param theme     The theme that contains styling information.
+     * @param visible   The visibility of the curve.
+     * @param index     The index of the curve
+     */
+    @Deprecated
+    public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aandf,
+            ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible,
             int              index);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -14,21 +14,26 @@
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.model.FacetTypes;
-import org.dive4elements.river.artifacts.model.WKms;
 import org.dive4elements.river.artifacts.model.WQKms;
 
 import org.dive4elements.river.exports.StyledSeriesBuilder;
 import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
 
 /**
  * Add data to chart/generator.
  *
  * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
  */
-public class QOutProcessor implements Processor {
+public class QOutProcessor extends DefaultProcessor {
+
+    public static final String I18N_LONGITUDINAL_LABEL =
+        "chart.longitudinal.section.yaxis.second.label";
+
+    public static final String
+        I18N_LONGITUDINAL_LABEL_DEFAULT = "Q [m\u00b3/s]";
 
     /** Private logger. */
     private static final Logger logger =
@@ -36,16 +41,48 @@
 
     @Override
     public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        WQKms wqkms = (WQKms) bundle.getData(context);
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
+
+        generator.addAxisSeries(series, axisName, visible);
+
+        /* Check if the diagram should be inverted*/
+        generator.setInverted(wqkms.guessRTLData());
+    }
+
+    @Override
+    public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aaf,
+            ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible,
             int              index)
     {
         CallContext context = generator.getCallContext();
-        WQKms wqkms = (WQKms) aaf.getData(context);
+        WQKms wqkms = (WQKms) bundle.getData(context);
 
-        doQOut(generator, wqkms, aaf, theme, visible, index);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
+
+        generator.addAxisSeries(series, index, visible);
+
+        /* Check if the diagram should be inverted*/
+        generator.setInverted(wqkms.guessRTLData());
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(I18N_LONGITUDINAL_LABEL,
+                I18N_LONGITUDINAL_LABEL_DEFAULT);
     }
 
     /**
@@ -59,73 +96,10 @@
 
         if (facetType.equals(FacetTypes.STATIC_WQKMS_Q)
             || facetType.equals(FacetTypes.LONGITUDINAL_Q)
-            || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_Q)) {
+            || facetType.startsWith(FacetTypes.DISCHARGE_LONGITUDINAL_Q)) {
             return true;
         }
         return false;
     }
-
-
-    /**
-     * Process the output for Q facets in a longitudinal section curve.
-     *
-     * @param generator Generator to use.
-     * @param wqkms An array of WQKms values.
-     * @param aandf The facet and artifact. This facet does NOT support any data objects. Use
-     * D4EArtifact.getNativeFacet() instead to retrieve a Facet which supports
-     * data.
-     * @param theme The theme that contains styling information.
-     * @param visible The visibility of the curve.
-     * @param index Axis index to add data to.
-     */
-    protected void doQOut(
-        XYChartGenerator generator,
-        WQKms    wqkms,
-        ArtifactAndFacet aaf,
-        ThemeDocument theme,
-        boolean  visible,
-        int index
-    ) {
-        logger.debug("QProcessor.doOut");
-
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
-
-        generator.addAxisSeries(series, index, visible);
-
-        invertAxis(generator, wqkms);
-    }
-
-    /**
-     * This method determines - taking JFreeCharts auto x value ordering into
-     * account - if the x axis need to be inverted. Waterlines in these charts
-     * should decrease.
-     *
-     * @param generator the generator to invert the axis or not.
-     * @param wkms The data object that stores the x and y values used for this
-     * chart.
-     */
-    public void invertAxis(XYChartGenerator generator, WKms wkms) {
-        boolean wsUp = wkms.guessWaterIncreasing();
-        boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms());
-        int size = wkms.size();
-        boolean inv = ((wsUp && kmUp) || (!wsUp && !kmUp)) && size > 1;
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("(Wkms)Values  : " + size);
-            if (size > 0) {
-                logger.debug("Start km: " + wkms.getKm(0));
-                logger.debug("End   km: " + wkms.getKm(size-1));
-            }
-            logger.debug("wsUp: " + wsUp);
-            logger.debug("kmUp: " + kmUp);
-            if (size == 1) {
-                logger.debug("InvertAxis not inverting because we have just one km");
-        }
-            logger.debug("inv:  " + inv);
-        }
-        generator.setInverted(inv);
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WDiffProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,39 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.exports.process;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.exports.DiagramGenerator;
+
+public class WDiffProcessor extends WOutProcessor {
+
+    public final static String I18N_WDIFF_YAXIS_LABEL =
+        "chart.w_differences.yaxis.label";
+
+    public final static String I18N_WDIFF_YAXIS_LABEL_DEFAULT = "m";
+
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+        return facetType.equals(FacetTypes.W_DIFFERENCES);
+    }
+
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(I18N_WDIFF_YAXIS_LABEL,
+                I18N_WDIFF_YAXIS_LABEL_DEFAULT);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Tue Sep 24 12:29:44 2013 +0200
@@ -13,40 +13,102 @@
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.FacetTypes;
 import org.dive4elements.river.artifacts.model.WKms;
+import org.dive4elements.river.artifacts.model.WQCKms;
 import org.dive4elements.river.exports.StyledSeriesBuilder;
 import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.exports.DiagramGenerator;
 import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.themes.ThemeDocument;
-import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.utils.RiverUtils;
 
 /**
  * Add data to chart/generator.
  *
  * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
  */
-public class WOutProcessor implements Processor {
+public class WOutProcessor extends DefaultProcessor {
 
     /** Private logger. */
     private static final Logger logger =
             Logger.getLogger(WOutProcessor.class);
 
+    public static final String I18N_AXIS_LABEL =
+        "chart.longitudinal.section.yaxis.label";
+
+    public static final String I18N_AXIS_LABEL_DEFAULT  = "W [NN + m]";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        Object data = bundle.getData(context);
+        WKms wkms = (WKms) data;
+
+        logger.debug("Processing facet: " + bundle.getFacetName());
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+
+        if (bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_C)) {
+            // Add corrected values
+            WQCKms wqckms = (WQCKms) data;
+            int size = wqckms.size();
+            for (int i = 0; i < size; i++) {
+                series.add(wqckms.getKm(i), wqckms.getC(i), false);
+            }
+        } else {
+            StyledSeriesBuilder.addPoints(series, wkms);
+        }
+        generator.addAxisSeries(series, axisName, visible);
+
+        // If a "band around the curve shall be drawn, add according area.
+        double bandWidth = theme.parseBandWidth();
+        if (bandWidth > 0 ) {
+            XYSeries seriesDown = new StyledXYSeries(
+                "band " + bundle.getFacetDescription(), false, theme);
+            XYSeries seriesUp = new StyledXYSeries(
+                bundle.getFacetDescription()+"+/-"+bandWidth, false, theme);
+            StyledSeriesBuilder.addUpperBand(seriesUp, wkms, bandWidth);
+            StyledSeriesBuilder.addLowerBand(seriesDown, wkms, bandWidth);
+
+            StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(theme);
+            area.addSeries(seriesUp);
+            area.addSeries(seriesDown);
+            area.setMode(StyledAreaSeriesCollection.FILL_MODE.BETWEEN);
+            generator.addAreaSeries(area, axisName, visible);
+        }
+
+        if (bundle.getFacetName().equals(FacetTypes.LONGITUDINAL_W) ||
+            bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) ||
+            bundle.getFacetName().equals(FacetTypes.STATIC_WQKMS_W) ||
+            bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_C)) {
+            /* Only use W values to check if the diagram should be inverted
+             * see flys/issue1290 for details */
+            logger.debug("Check for RTL data: "+ wkms.guessRTLData());
+            generator.setInverted(wkms.guessRTLData());
+        }
+    }
+
     @Override
     public void doOut(
             XYChartGenerator generator,
-            ArtifactAndFacet aaf,
+            ArtifactAndFacet bundle,
             ThemeDocument    theme,
             boolean          visible,
             int              index)
     {
         CallContext context = generator.getCallContext();
-        WKms wkms = (WKms) aaf.getData(context);
+        WKms wkms = (WKms) bundle.getData(context);
 
         logger.debug("doOut");
 
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
 
         StyledSeriesBuilder.addPoints(series, wkms);
         generator.addAxisSeries(series, index, visible);
@@ -55,9 +117,9 @@
         double bandWidth = theme.parseBandWidth();
         if (bandWidth > 0 ) {
             XYSeries seriesDown = new StyledXYSeries(
-                "band " + aaf.getFacetDescription(), false, theme);
+                "band " + bundle.getFacetDescription(), false, theme);
             XYSeries seriesUp = new StyledXYSeries(
-                aaf.getFacetDescription()+"+/-"+bandWidth, false, theme);
+                bundle.getFacetDescription()+"+/-"+bandWidth, false, theme);
             StyledSeriesBuilder.addUpperBand(seriesUp, wkms, bandWidth);
             StyledSeriesBuilder.addLowerBand(seriesDown, wkms, bandWidth);
 
@@ -68,15 +130,27 @@
             generator.addAreaSeries(area, index, visible);
         }
 
-        if (aaf.getFacetName().equals(FacetTypes.LONGITUDINAL_W) ||
-            aaf.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) ||
-            aaf.getFacetName().equals(FacetTypes.STATIC_WQKMS_W)) {
+        if (bundle.getFacetName().equals(FacetTypes.LONGITUDINAL_W) ||
+            bundle.getFacetName().equals(FacetTypes.DISCHARGE_LONGITUDINAL_W) ||
+            bundle.getFacetName().equals(FacetTypes.STATIC_WQKMS_W)) {
             /* Only use W values to check if the diagram should be inverted
              * see flys/issue1290 for details */
-            invertAxis(generator, wkms);
+            generator.setInverted(wkms.guessRTLData());
         }
     }
 
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        D4EArtifact flys = (D4EArtifact) generator.getMaster();
+
+        String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
+
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT,
+                new Object[] { unit });
+    }
+
     /**
      * Returns true if facettype is longitutinal_section.w .
      */
@@ -91,41 +165,12 @@
                 || facetType.equals(FacetTypes.HEIGHTMARKS_POINTS)
                 || facetType.equals(FacetTypes.STATIC_WQKMS)
                 || facetType.equals(FacetTypes.STATIC_WQKMS_W)
-                || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W))
+                || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_W)
+                || facetType.equals(FacetTypes.DISCHARGE_LONGITUDINAL_C))
         {
             return true;
         }
         return false;
     }
-
-    /**
-     * This method determines - taking JFreeCharts auto x value ordering into
-     * account - if the x axis need to be inverted. Waterlines in these charts
-     * should decrease.
-     *
-     * @param wkms The data object that stores the x and y values used for this
-     * chart.
-     */
-    public void invertAxis(XYChartGenerator generator, WKms wkms) {
-        boolean wsUp = wkms.guessWaterIncreasing();
-        boolean kmUp = DataUtil.guessWaterIncreasing(wkms.allKms());
-        int size = wkms.size();
-        boolean inv = ((wsUp && kmUp) || (!wsUp && !kmUp)) && size > 1;
-
-        if (logger.isDebugEnabled()) {
-            logger.debug("(Wkms)Values  : " + size);
-            if (size > 0) {
-                logger.debug("Start km: " + wkms.getKm(0));
-                logger.debug("End   km: " + wkms.getKm(size-1));
-            }
-            logger.debug("wsUp: " + wsUp);
-            logger.debug("kmUp: " + kmUp);
-            if (size == 1) {
-                logger.debug("InvertAxis not inverting because we have just one km");
-        }
-            logger.debug("inv:  " + inv);
-        }
-        generator.setInverted(inv);
-    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java	Tue Sep 24 12:29:44 2013 +0200
@@ -62,6 +62,13 @@
 
     protected List<JFreeChart> charts;
 
+    protected String outName;
+
+    @Override
+    public void setup(Object config) {
+        logger.debug("SQOverviewGenerator.setup");
+    }
+
     /**
      * Produce output.
      * @param artifactAndFacet current facet and artifact.
@@ -75,6 +82,8 @@
     ) {
         logger.debug("doOut()");
 
+        // TODO: Why not using outName for this.
+
         String name = artifactAndFacet.getData(context).toString();
         if(name != null) {
             logger.debug("name: " + name);
@@ -94,7 +103,7 @@
 
             try {
                 Document cAttr = getAttribute(context, collectionAttribute, name);
-                g.init(request, out, context);
+                g.init(name, request, out, context);
 
                 helper.doOut(g, name, name, cAttr, context);
                 JFreeChart chart = g.generateChart();
@@ -111,7 +120,8 @@
     }
 
     @Override
-    public void init(Document request, OutputStream out, CallContext context) {
+    public void init(String outName, Document request, OutputStream out, CallContext context) {
+        this.outName = outName;
         this.request = request;
         this.out = out;
         this.context = context;
@@ -153,6 +163,7 @@
             if (i > 1) {
                 vertPos = (size[1] / 3) * (i / 2);
             }
+            // TODO: Dispose Graphics object!
             result.createGraphics().drawImage(img, horPos, vertPos, null);
         }
         ImageIO.write(result, "png", out);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java	Tue Sep 24 12:29:44 2013 +0200
@@ -18,8 +18,6 @@
 import java.text.DateFormat;
 import java.text.NumberFormat;
 
-import org.w3c.dom.Document;
-
 import net.sf.jasperreports.engine.JasperExportManager;
 import net.sf.jasperreports.engine.JasperFillManager;
 import net.sf.jasperreports.engine.JasperPrint;
@@ -27,7 +25,6 @@
 
 import au.com.bytecode.opencsv.CSVWriter;
 
-import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.CallMeta;
 
 import org.dive4elements.river.artifacts.model.CalculationResult;
@@ -117,13 +114,10 @@
 
     protected List<SQResult []> data;
 
-
-    public void init(Document request, OutputStream out, CallContext cc) {
-        super.init(request, out, cc);
+    public SQRelationExporter() {
         data = new ArrayList<SQResult []>();
     }
 
-
     @Override
     protected void addData(Object d) {
         if (d instanceof CalculationResult) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,365 @@
+/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * This file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUTELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+package org.dive4elements.river.jfree;
+
+import org.dive4elements.river.themes.ThemeDocument;
+
+import java.util.List;
+import java.util.Map;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Font;
+
+import org.jfree.ui.TextAnchor;
+import org.jfree.chart.plot.XYPlot;
+import org.jfree.chart.LegendItem;
+import org.jfree.chart.LegendItemCollection;
+import org.jfree.chart.annotations.XYTextAnnotation;
+import org.jfree.chart.annotations.XYLineAnnotation;
+
+import org.dive4elements.river.themes.LineStyle;
+import org.dive4elements.river.themes.TextStyle;
+import org.dive4elements.river.exports.ChartSettings;
+import org.dive4elements.river.exports.LegendSection;
+import org.dive4elements.river.exports.ChartArea;
+
+import org.apache.log4j.Logger;
+
+/** Annotation helper class, handles plotting of annotations. */
+public class AnnotationHelper {
+    private static final Logger logger = Logger.getLogger(AnnotationHelper.class);
+
+    protected static float ANNOTATIONS_AXIS_OFFSET = 0.02f;
+
+    /* arr this would be better in chartsettings */
+    public static final int    DEFAULT_FONT_SIZE       = 12;
+    public static final String DEFAULT_FONT_NAME       = "Tahoma";
+
+    /**
+     * Add annotations (Sticky, Text and hyk zones) to a plot.
+     * @param annotations Annotations to add
+     * @param plot Plot to add annotations to.
+     * @param settings ChartSettings object for settings.
+     */
+    public static void addAnnotationsToRenderer(List<RiverAnnotation> annotations,
+            XYPlot plot, ChartSettings settings, Map<Integer, AxisDataset> datasets) {
+        logger.debug("addAnnotationsToRenderer");
+
+        if (annotations == null || annotations.isEmpty()) {
+            logger.debug("addAnnotationsToRenderer: no annotations.");
+            return;
+        }
+
+        // OPTMIMIZE: Pre-calculate positions
+        ChartArea area = new ChartArea(
+            plot.getDomainAxis(0).getRange(),
+            plot.getRangeAxis().getRange());
+
+        // Walk over all Annotation sets.
+        for (RiverAnnotation fa: annotations) {
+
+            // Access text styling, if any.
+            ThemeDocument theme = fa.getTheme();
+            TextStyle textStyle = null;
+            LineStyle lineStyle = null;
+
+            // Get Themeing information and add legend item.
+            if (theme != null) {
+                textStyle = theme.parseComplexTextStyle();
+                lineStyle = theme.parseComplexLineStyle();
+                if (fa.getLabel() != null) {
+                    // Legend handling, maybe misplaced?
+                    LegendItemCollection lic = new LegendItemCollection();
+                    LegendItemCollection old = plot.getFixedLegendItems();
+
+                    Color color = theme.parseLineColorField();
+                    if (color == null) {
+                        color = Color.BLACK;
+                    }
+
+                    LegendItem newItem = new LegendItem(fa.getLabel(), color);
+                    LegendSection ls = (settings != null ?
+                            settings.getLegendSection() : null);
+                    newItem.setLabelFont (new Font(
+                        DEFAULT_FONT_NAME,
+                        Font.PLAIN,
+                        ls != null ? ls.getFontSize() : null)
+                    );
+
+                    lic.add(newItem);
+                    // (Re-)Add prior legend entries.
+                    if (old != null) {
+                        old.addAll(lic);
+                    }
+                    else {
+                        old = lic;
+                    }
+                    plot.setFixedLegendItems(old);
+                }
+            }
+
+            // The 'Sticky' Annotations (at axis, with line and text).
+            for (StickyAxisAnnotation sta: fa.getAxisTextAnnotations()) {
+                addStickyAnnotation(
+                    sta, plot, area, lineStyle, textStyle, theme,
+                    datasets.get(new Integer(sta.getAxisSymbol())));
+            }
+
+            // Other Text Annotations (e.g. labels of (manual) points).
+            for (XYTextAnnotation ta: fa.getTextAnnotations()) {
+                // Style the text.
+                if (textStyle != null) {
+                    textStyle.apply(ta);
+                }
+                ta.setY(area.above(0.05d, ta.getY()));
+                plot.getRenderer().addAnnotation(ta, org.jfree.ui.Layer.FOREGROUND);
+            }
+        }
+    }
+
+    /**
+     * Add a text and a line annotation.
+     * @param area convenience to determine positions in plot.
+     * @param theme (optional) theme document
+     */
+    public static void addStickyAnnotation(
+        StickyAxisAnnotation annotation,
+        XYPlot plot,
+        ChartArea area,
+        LineStyle lineStyle,
+        TextStyle textStyle,
+        ThemeDocument theme,
+        AxisDataset dataset
+    ) {
+        // OPTIMIZE pre-calculate area-related values
+        final float TEXT_OFF = 0.03f;
+
+        XYLineAnnotation lineAnnotation = null;
+        XYTextAnnotation textAnnotation = null;
+
+        int rendererIndex = 0;
+
+        if (annotation.atX()) {
+            textAnnotation = new CollisionFreeXYTextAnnotation(
+                annotation.getText(), annotation.getPos(), area.ofGround(TEXT_OFF));
+            // OPTIMIZE externalize the calculation involving PI.
+            //textAnnotation.setRotationAngle(270f*Math.PI/180f);
+            lineAnnotation = createGroundStickAnnotation(
+                area, annotation.getPos(), lineStyle);
+            textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
+            textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
+        }
+        else {
+            // Do the more complicated case where we stick to the Y-Axis.
+            // There is one nasty case (duration curves, where annotations
+            // might stick to the second y-axis).
+            if (dataset == null) {
+                logger.warn("Annotation should stick to unfindable y-axis: "
+                    + annotation.getAxisSymbol());
+                rendererIndex = 0;
+            }
+            else {
+                rendererIndex = dataset.getPlotAxisIndex();
+            }
+
+            // Stick to the "right" (opposed to left) Y-Axis.
+            if (rendererIndex != 0) {
+                // OPTIMIZE: Pass a different area to this function,
+                //           do the adding to renderer outside (let this
+                //           function return the annotations).
+                //           Note that this path is travelled rarely.
+                ChartArea area2 = new ChartArea(plot.getDomainAxis(), plot.getRangeAxis(rendererIndex));
+                textAnnotation = new CollisionFreeXYTextAnnotation(
+                    annotation.getText(), area2.ofRight(TEXT_OFF), annotation.getPos());
+                textAnnotation.setRotationAnchor(TextAnchor.CENTER_RIGHT);
+                textAnnotation.setTextAnchor(TextAnchor.CENTER_RIGHT);
+                lineAnnotation = createRightStickAnnotation(
+                    area2, annotation.getPos(), lineStyle);
+                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
+                    // New line annotation to hit curve.
+                    if (theme.parseShowVerticalLine()) {
+                        XYLineAnnotation hitLineAnnotation =
+                            createStickyLineAnnotation(
+                                StickyAxisAnnotation.SimpleAxis.X_AXIS,
+                                annotation.getHitPoint(), annotation.getPos(),// annotation.getHitPoint(),
+                                area2, lineStyle);
+                        plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
+                            org.jfree.ui.Layer.BACKGROUND);
+                    }
+                    if (theme.parseShowHorizontalLine()) {
+                        XYLineAnnotation lineBackAnnotation =
+                            createStickyLineAnnotation(
+                                StickyAxisAnnotation.SimpleAxis.Y_AXIS2,
+                                annotation.getPos(), annotation.getHitPoint(),
+                                area2, lineStyle);
+                        plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation,
+                            org.jfree.ui.Layer.BACKGROUND);
+                    }
+                }
+            }
+            else { // Stick to the left y-axis.
+                textAnnotation = new CollisionFreeXYTextAnnotation(
+                    annotation.getText(), area.ofLeft(TEXT_OFF), annotation.getPos());
+                textAnnotation.setRotationAnchor(TextAnchor.CENTER_LEFT);
+                textAnnotation.setTextAnchor(TextAnchor.CENTER_LEFT);
+                lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle);
+                if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
+                    // New line annotation to hit curve.
+                    if (theme.parseShowHorizontalLine()) {
+                        XYLineAnnotation hitLineAnnotation =
+                            createStickyLineAnnotation(
+                                StickyAxisAnnotation.SimpleAxis.Y_AXIS,
+                                annotation.getPos(), annotation.getHitPoint(),
+                                area, lineStyle);
+                        plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
+                            org.jfree.ui.Layer.BACKGROUND);
+                    }
+                    if (theme.parseShowVerticalLine()) {
+                        XYLineAnnotation lineBackAnnotation =
+                            createStickyLineAnnotation(
+                                StickyAxisAnnotation.SimpleAxis.X_AXIS,
+                                annotation.getHitPoint(), annotation.getPos(),
+                                area, lineStyle);
+                        plot.getRenderer(rendererIndex).addAnnotation(lineBackAnnotation,
+                            org.jfree.ui.Layer.BACKGROUND);
+                    }
+                }
+            }
+        }
+
+        // Style the text.
+        if (textStyle != null) {
+            textStyle.apply(textAnnotation);
+        }
+
+        // Add the Annotations to renderer.
+        plot.getRenderer(rendererIndex).addAnnotation(textAnnotation,
+            org.jfree.ui.Layer.FOREGROUND);
+        plot.getRenderer(rendererIndex).addAnnotation(lineAnnotation,
+            org.jfree.ui.Layer.FOREGROUND);
+    }
+
+   /**
+     * Create annotation that sticks to "ground" (X) axis.
+     * @param area helper to calculate coordinates
+     * @param pos one-dimensional position (distance from axis)
+     * @param lineStyle the line style to use for the line.
+     */
+    public static XYLineAnnotation createGroundStickAnnotation(
+        ChartArea area, float pos, LineStyle lineStyle
+    ) {
+        // Style the line.
+        if (lineStyle != null) {
+            return new XYLineAnnotation(
+                pos, area.atGround(),
+                pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET),
+                new BasicStroke(lineStyle.getWidth()),lineStyle.getColor());
+        }
+        else {
+            return new XYLineAnnotation(
+                pos, area.atGround(),
+                pos, area.ofGround(ANNOTATIONS_AXIS_OFFSET));
+        }
+    }
+
+
+    /**
+     * Create annotation that sticks to the second Y axis ("right").
+     * @param area helper to calculate coordinates
+     * @param pos one-dimensional position (distance from axis)
+     * @param lineStyle the line style to use for the line.
+     */
+    public static XYLineAnnotation createRightStickAnnotation(
+        ChartArea area, float pos, LineStyle lineStyle
+    ) {
+        // Style the line.
+        if (lineStyle != null) {
+            return new XYLineAnnotation(
+                area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos,
+                area.atRight(), pos,
+                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
+        }
+        else {
+            return new XYLineAnnotation(
+                area.atRight(), pos,
+                area.ofRight(ANNOTATIONS_AXIS_OFFSET), pos);
+        }
+    }
+    /**
+     * Create annotation that sticks to the first Y axis ("left").
+     * @param area helper to calculate coordinates
+     * @param pos one-dimensional position (distance from axis)
+     * @param lineStyle the line style to use for the line.
+     */
+    public static XYLineAnnotation createLeftStickAnnotation(
+        ChartArea area, float pos, LineStyle lineStyle
+    ) {
+        // Style the line.
+        if (lineStyle != null) {
+            return new XYLineAnnotation(
+                area.atLeft(), pos,
+                area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos,
+                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
+        }
+        else {
+            return new XYLineAnnotation(
+                area.atLeft(), pos,
+                area.ofLeft(ANNOTATIONS_AXIS_OFFSET), pos);
+        }
+    }
+
+
+    /**
+     * Create a line from a axis to a given point.
+     * @param axis   The "simple" axis.
+     * @param fromD1 from-location in first dimension.
+     * @param toD2   to-location in second dimension.
+     * @param area   helper to calculate offsets.
+     * @param lineStyle optional line style.
+     */
+    public static XYLineAnnotation createStickyLineAnnotation(
+        StickyAxisAnnotation.SimpleAxis axis, float fromD1, float toD2,
+        ChartArea area, LineStyle lineStyle
+    ) {
+        double anchorX1 = 0d, anchorX2 = 0d, anchorY1 = 0d, anchorY2 = 0d;
+        switch(axis) {
+            case X_AXIS:
+                anchorX1 = fromD1;
+                anchorX2 = fromD1;
+                anchorY1 = area.atGround();
+                anchorY2 = toD2;
+                break;
+            case Y_AXIS:
+                anchorX1 = area.atLeft();
+                anchorX2 = toD2;
+                anchorY1 = fromD1;
+                anchorY2 = fromD1;
+                break;
+            case Y_AXIS2:
+                anchorX1 = area.atRight();
+                anchorX2 = toD2;
+                anchorY1 = fromD1;
+                anchorY2 = fromD1;
+                break;
+        }
+        // Style the line.
+        if (lineStyle != null) {
+            return new XYLineAnnotation(
+                anchorX1, anchorY1,
+                anchorX2, anchorY2,
+                new BasicStroke(lineStyle.getWidth()), lineStyle.getColor());
+        }
+        else {
+            return new XYLineAnnotation(
+                anchorX1, anchorY1,
+                anchorX2, anchorY2);
+        }
+    }
+
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AxisDataset.java	Tue Sep 24 12:29:44 2013 +0200
@@ -0,0 +1,127 @@
+/* Copyright (C) 2013 by Bundesanstalt für Gewässerkunde
+ * Software engineering by Intevation GmbH
+ *
+ * XYDatasethis file is Free Software under the GNU AGPL (>=v3)
+ * and comes with ABSOLUXYDatasetELY NO WARRANTY! Check out the
+ * documentation coming with Dive4Elements River for details.
+ */
+
+package org.dive4elements.river.jfree;
+
+import java.util.List;
+import java.util.ArrayList;
+
+import org.jfree.data.Range;
+import org.jfree.data.RangeInfo;
+import org.jfree.data.xy.XYDataset;
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+import org.jfree.data.time.TimeSeriesCollection;
+
+import org.apache.log4j.Logger;
+
+/**
+ * Axis datasets.
+ */
+public class AxisDataset
+{
+    private static Logger logger = Logger.getLogger(AxisDataset.class);
+
+    /** Symbolic integer, but also coding the priority (0 goes first). */
+    protected int axisSymbol;
+
+    /** List of assigned datasets (in order). */
+    protected List<XYDataset> datasets;
+
+    /** Range to use to include all given datasets. */
+    protected Range range;
+
+    /** Index of axis in plot. */
+    protected int plotAxisIndex;
+
+    protected boolean rangeDirty;
+
+    /** Create AxisDataset. */
+    public AxisDataset(int symb) {
+        axisSymbol = symb;
+        datasets   = new ArrayList<XYDataset>();
+    }
+
+    /** Add a dataset to internal list for this axis. */
+    public void addDataset(XYDataset dataset) {
+        datasets.add(dataset);
+        rangeDirty = true;
+    }
+
+    /** Add a dataset. */
+    public void addDataset(XYSeries series) {
+        addDataset(new XYSeriesCollection(series));
+    }
+
+    public void setRange(Range val) {
+        range = val;
+    }
+
+    /** Get Range for the range axis of this dataset. */
+    public Range getRange() {
+        if (range != null && !rangeDirty) {
+            return range;
+        }
+        /* Calculate the min / max of all series */
+        for (XYDataset dataset: datasets) {
+            Range newRange = null;
+            if (dataset instanceof StyledAreaSeriesCollection) {
+                /* We do not include areas in the range calculation because
+                 * they are used with very large / small values to draw areas
+                 * with axis boundaries */
+                continue;
+            } else if (dataset instanceof RangeInfo) {
+                /* The usual case for most series */
+                newRange = ((RangeInfo) dataset).getRangeBounds(false);
+            } else if (dataset instanceof TimeSeriesCollection) {
+                /* Lalala <3 Jfreechart's class hirarchy */
+                newRange = ((TimeSeriesCollection) dataset).getRangeBounds(false);
+            }
+
+            /* Now we only expand as we also only add new data */
+            if (range == null) {
+                range = newRange;
+            } else {
+                range = Range.combine(range, newRange);
+            }
+        }
+        rangeDirty = false;
+        return range;
+    }
+
+    /** Get Array of Datasets. */
+    public XYDataset[] getDatasets() {
+        return datasets.toArray(new XYDataset[datasets.size()]);
+    }
+
+    /** True if to be rendered as area. */
+    public boolean isArea(XYDataset series) {
+        return (series instanceof StyledAreaSeriesCollection);
+    }
+
+    /** True if no datasets given. */
+    public boolean isEmpty() {
+        return datasets.isEmpty();
+    }
+
+    /** Set the 'real' axis index that this axis is mapped to. */
+    public void setPlotAxisIndex(int axisIndex) {
+        plotAxisIndex = axisIndex;
+    }
+
+    /** Get the 'real' axis index that this axis is mapped to. */
+    public int getPlotAxisIndex() {
+        return plotAxisIndex;
+    }
+
+    /** Add a Dataset that describes an area. */
+    public void addArea(StyledAreaSeriesCollection series) {
+        addDataset(series);
+    }
+
+}
--- a/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java	Tue Sep 24 12:29:44 2013 +0200
@@ -14,11 +14,20 @@
 
 public class DataUtil
 {
-    public static boolean guessWaterIncreasing(TDoubleArrayList data) {
-        return guessWaterIncreasing(data, 0.05f);
+    public static boolean guessDataIncreasing(TDoubleArrayList data) {
+        return guessDataIncreasing(data, 0.05f);
     }
 
-    public static boolean guessWaterIncreasing(TDoubleArrayList data, float factor) {
+    /** Guess if data1 and data2 both grow in the same direction */
+    public static boolean guessSameDirectionData(TDoubleArrayList data1,
+            TDoubleArrayList data2) {
+        boolean d1dir = DataUtil.guessDataIncreasing(data1, 0.05f);
+        boolean d2dir = DataUtil.guessDataIncreasing(data2, 0.05f);
+        int size = data1.size();
+        return ((d1dir && d2dir) || (!d1dir && !d2dir)) && size > 1;
+    }
+
+    public static boolean guessDataIncreasing(TDoubleArrayList data, float factor) {
         int N = data.size();
         if (N < 2) return false;
 
--- a/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java	Tue Sep 24 12:29:44 2013 +0200
@@ -21,8 +21,7 @@
     private A a;
     private B b;
 
-    @SuppressWarnings("unused")
-    private Pair() {
+    public Pair() {
     }
 
     public Pair(A a, B b) {
--- a/artifacts/src/main/resources/messages.properties	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/resources/messages.properties	Tue Sep 24 12:29:44 2013 +0200
@@ -152,6 +152,7 @@
 chart.longitudinal.section.title = W-Longitudinal Section
 chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
 chart.longitudinal.section.shortsubtitle = {0}
+chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###}
 chart.longitudinal.section.xaxis.label = {0}-km
 chart.longitudinal.section.yaxis.label = W [{0}]
 chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
@@ -197,7 +198,7 @@
 chart.reference.curve.y.axis.in.m = Target Station(s) [NN + m] 
 
 chart.w_differences.title = Differences
-chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.subtitle = {0}
 chart.w_differences.yaxis.label = m
 chart.w_differences.yaxis.second.label = W [NN + m]
 
--- a/artifacts/src/main/resources/messages_de.properties	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Tue Sep 24 12:29:44 2013 +0200
@@ -158,6 +158,7 @@
 chart.longitudinal.section.title = W-L\u00e4ngsschnitt
 chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
 chart.longitudinal.section.shortsubtitle = {0}
+chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###}
 chart.longitudinal.section.xaxis.label = {0}-km
 chart.longitudinal.section.yaxis.label = W [{0}]
 chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
@@ -207,7 +208,7 @@
 chart.normalized.reference.curve.title = Reduzierte Bezugslinie
 
 chart.w_differences.title = Differenzen
-chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.subtitle = {0}
 chart.w_differences.yaxis.label = m
 chart.w_differences.yaxis.second.label = W [NN + m]
 
--- a/artifacts/src/main/resources/messages_de_DE.properties	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/resources/messages_de_DE.properties	Tue Sep 24 12:29:44 2013 +0200
@@ -157,6 +157,7 @@
 chart.longitudinal.section.title = W-L\u00e4ngsschnitt
 chart.longitudinal.section.subtitle = Bereich: {0}-km {1,number,#.###} - {2,number,#.###}
 chart.longitudinal.section.shortsubtitle = {0}
+chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###}
 chart.longitudinal.section.xaxis.label = {0}-km
 chart.longitudinal.section.yaxis.label = W [{0}]
 chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
@@ -205,7 +206,7 @@
 chart.normalized.reference.curve.title = Reduzierte Bezugslinie
 
 chart.w_differences.title = Differenzen
-chart.w_differences.subtitle = Strecke: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.subtitle = {0}
 chart.w_differences.yaxis.label = m
 chart.w_differences.yaxis.second.label = W [NN + m]
 
--- a/artifacts/src/main/resources/messages_en.properties	Mon Sep 23 11:37:13 2013 +0200
+++ b/artifacts/src/main/resources/messages_en.properties	Tue Sep 24 12:29:44 2013 +0200
@@ -157,6 +157,7 @@
 chart.longitudinal.section.title = W-Longitudinal Section
 chart.longitudinal.section.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
 chart.longitudinal.section.shortsubtitle = {0}
+chart.longitudinal.section.locsubtitle = {0} km {1,number,#.###}
 chart.longitudinal.section.xaxis.label = {0}-km
 chart.longitudinal.section.yaxis.label = W [{0}]
 chart.longitudinal.section.yaxis.second.label = Q [m\u00b3/s]
@@ -210,7 +211,7 @@
 chart.normalized.reference.curve.title = Reduced Reference Curve
 
 chart.w_differences.title = Differences
-chart.w_differences.subtitle = Range: {0}-km {1,number,#.###} - {2,number,#.###}
+chart.w_differences.subtitle = {0}
 chart.w_differences.yaxis.label = m
 chart.w_differences.yaxis.second.label = W [NN + m]
 

http://dive4elements.wald.intevation.org