changeset 7193:7fabae60428b double-precision

Merged changes from default into double-precision branch.
author Tom Gottfried <tom@intevation.de>
date Fri, 27 Sep 2013 17:36:50 +0200
parents 28748bb1b676 (current diff) 5be5bf32bead (diff)
children 5358a5497b2b
files 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/MiddleBedHeightGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightInfoGenerator.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/fixings/FixLongitudinalSectionGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffYearInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceYearGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSInfoGenerator.java artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java artifacts/src/main/java/org/dive4elements/river/themes/ThemeAccess.java artifacts/src/main/java/org/dive4elements/river/utils/ThemeUtil.java backend/src/main/java/org/dive4elements/river/importer/ImportRiver.java backend/src/main/java/org/dive4elements/river/importer/ImportWst.java backend/src/main/java/org/dive4elements/river/importer/ImportWstColumn.java
diffstat 261 files changed, 11830 insertions(+), 5192 deletions(-) [+]
line wrap: on
line diff
--- a/.hgtags	Fri Sep 13 18:29:01 2013 +0200
+++ b/.hgtags	Fri Sep 27 17:36:50 2013 +0200
@@ -66,3 +66,6 @@
 0000000000000000000000000000000000000000 3.0.12
 da197a9236fde564d45379c0826510c69a5709ce 3.0.12
 71da3d4ffb4a46a2f8de7e6a9e1e4a32657802aa 3.0.13
+84b1e87e86692db4202c5d68e0c521185ef0f9d2 3.0.14
+a18a4376479383d7b6233bfa2d27737c8686c90a 3.0.15
+12248d9eb326472adc2a8824989dc4a52197f7cc 3.0.16
--- a/artifacts/doc/conf/artifacts/chart.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/artifacts/chart.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -80,6 +80,7 @@
                 <outputmode name="longitudinal_section" description="output.discharge_longitudinal_section" mime-type="image/png" type="chart">
                     <facets>
                         <facet name="empty.facet" decription= "Empty"/>
+                        <facet name="w_differences" decription= "W Differences"/>
                         <facet name="discharge_longitudinal_section.w"/>
                         <facet name="discharge_longitudinal_section.q"/>
                         <facet name="discharge_longitudinal_section.c"/>
--- a/artifacts/doc/conf/artifacts/gaugedischargecurve.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/artifacts/gaugedischargecurve.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -18,6 +18,8 @@
                         <facet name="other.wkms"  description="Point-like data like fixations"/>
                         <facet name="other.wkms.interpol" description="Height over km, like flood protections."/>
                         <facet name="computed_discharge_curve.manualpoints" description="Manuelle Punkte"/>
+                        <facet name="other.w.interpol" description="Interpolated (likely single) W Values"/>
+                        <facet name="other.wqkms.w" description="facet.other.wqkms"/>
                     </facets>
                 </outputmode>
                 <outputmode name="computed_dischargecurve_at_export" description="output.computed_dischargecurve_at_export" mime-type="text/plain" type="export">
--- a/artifacts/doc/conf/artifacts/winfo.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/artifacts/winfo.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -326,6 +326,8 @@
                         <facet name="computed_discharge_curve.mainvalues.w" description="facet.computed_discharge_curve.mainvalues.w"/>
                         <facet name="discharge_curve.curve" description="facet.discharge_curve.curve"/>
                         <facet name="heightmarks_points" description="facet.other.wqkms"/>
+                        <facet name="other.wqkms.w" description="facet.other.wqkms"/>
+                        <facet name="other.wqkms.q" description="facet.other.wqkms"/>
                         <facet name="other.wqkms" description="facet.other.wqkms"/>
                         <facet name="other.wq"    description="Point-like data like fixations"/>
                         <facet name="other.wkms"  description="Point-like data like fixations"/>
@@ -382,6 +384,8 @@
                 </outputmode>
                 <outputmode name="waterlevel_export" description="output.waterlevel_export" mime-type="text/plain" type="export">
                   <facets>
+                    <!-- include other.wqkms.w to be able to include official lines (issue1384) -->
+                    <facet name="other.wqkms.w" description="W-Type of data" />
                     <facet name="csv" description="facet.waterlevel_export.csv" />
                     <facet name="wst" description="facet.waterlevel_export.wst" />
                     <facet name="pdf" description="facet.waterlevel_export.pdf" />
@@ -638,6 +642,7 @@
                 </outputmode>
                 <outputmode name="historical_discharge_wq" description="output.historical_discharge_wq.description" mime-type="image/png" type="chart">
                     <facets>
+                        <facet name="discharge_curve.curve"/>
                         <facet name="historical_discharge.wq.q"/>
                         <facet name="historical_discharge.wq.w"/>
                         <facet name="historical_discharge.wq.curve"/>
--- a/artifacts/doc/conf/conf.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/conf.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -7,6 +7,8 @@
     <!ENTITY rest-server SYSTEM "rest-server.xml">
     <!ENTITY floodmap    SYSTEM "floodmap.xml">
     <!ENTITY rivermap	 SYSTEM "rivermap.xml">
+    <!ENTITY generators  SYSTEM "generators.xml">
+    <!ENTITY longitudinal-defaults SYSTEM "longitudinal-diagram-defaults.xml">
 ]>
 <artifact-database>
     <export-secret>YOUR_SECRET</export-secret>
@@ -284,104 +286,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>
--- a/artifacts/doc/conf/floodmap.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/floodmap.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -14,127 +14,106 @@
 
     <river name="Saar">
         <srid value="31467"/>
-        <dgm-srid value="31466"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Saar"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Mosel">
         <srid value="31467"/>
-        <dgm-srid value="31466"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Mosel"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Elbe">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Elbe"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Donau">
         <srid value="31467"/>
-        <dgm-srid value="25833"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Donau"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Donau (Regensburg Nordarm)">
         <srid value="31467"/>
-        <dgm-srid value="25833"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Donau (Regensburg Nordarm)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Donau (Schleusenkanäle)">
         <srid value="31467"/>
-        <dgm-srid value="25833"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Donau (Schleusenkanäle)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Fulda (Winter)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Fulda (Winter)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Fulda (Sommer)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Fulda (Sommer)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Lahn">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Lahn"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Main">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Main"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Main (Wehrarm Limbach)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Main (Wehrarm Limbach)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Main (Wehrarm Volkach)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Main (Wehrarm Volkach)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Neckar">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Neckar"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Neckar (über Wehrarme)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Neckar (über Wehrarme)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Rhein">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Rhein"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Saale">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Saale"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Saale-Thüringen">
         <srid value="31467"/>
-        <dgm-srid value="31468"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Saale-Thüringen"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Saar (Wiltinger Bogen)">
         <srid value="31467"/>
-        <dgm-srid value="31466"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Saar (Wiltinger Bogen)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Werra (Winter)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Werra (Winter)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Werra (Sommer)">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Werra (Sommer)"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
     <river name="Weser">
         <srid value="31467"/>
-        <dgm-srid value="31467"/>
         <river-wms url="http://example.com/cgi-bin/river-wms" layers="Weser"/>
         <background-wms url="http://osm.intevation.de/mapcache/?" layers="OSM-WMS-Dienst"/>
     </river>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/doc/conf/generators.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,158 @@
+<?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">
+        <title key="chart.longitudinal.section.title" default="W-Längsschnitt"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/>
+    </output-generator>
+    <output-generator
+        names="w_differences,w_differences_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.w_differences.title" default="Differences"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="W"/>
+        <subtitle key="chart.w_differences.subtitle" default="-">
+            <arg expr="artifact.river"/>
+        </subtitle>
+    </output-generator>
+    <output-generator
+        names="fix_longitudinal_section_curve,fix_longitudinal_section_curve_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.fixings.longitudinalsection.title" default="Fixierungsanalyse"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="deltaW"/>
+    </output-generator>
+    <output-generator
+        names="bedheight_middle,bedheight_middle_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.bedheight_middle.section.title" default="Mittlere Sohlhöhe"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor" axis="BedHeight"/>
+    </output-generator>
+    <output-generator
+        names="bed_difference_height_year,bed_difference_height_year_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.beddifference.height.title" default="Sohlenhöhen Differenz"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="diffBed/Y"/>
+    </output-generator>
+    <output-generator names="bed_difference_year,bed_difference_year_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.beddifference.year.title" default="Sohlenhöhen Differenz"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="diffBed"/>
+    </output-generator>
+    <output-generator names="bed_longitudinal_section,bed_longitudinal_section_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.bedquality.title" default="Sohlen Längsschnitt"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="diffBed"/>
+    </output-generator>
+    <output-generator
+        names="sedimentload_ls,sedimentload_ls_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.sedimentload.ls.title" default="Sedimentfracht"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="SedimentLoad"/>
+    </output-generator>
+    <output-generator
+        names="flow_velocity,flow_velocity_chartinfo"
+        class="org.dive4elements.river.exports.LongitudinalSectionGenerator2"
+        converter="org.dive4elements.river.exports.DiagramAttributes">
+        <title key="chart.flow_velocity.section.title" default="Geschwindigkeit- und Schubspannung"/>
+        &longitudinal-defaults;
+        <processor class="org.dive4elements.river.exports.process.ManualPointsProcessor"
+            axis="Velocity"/>
+    </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_export" class="org.dive4elements.river.exports.FlowVelocityExporter"/>
+    <output-generator names="bedheight_middle_export" class="org.dive4elements.river.exports.MiddleBedHeightExporter"/>
+    <output-generator names="bed_quality_export" class="org.dive4elements.river.exports.minfo.BedQualityExporter"/>
+    <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="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_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_export" class="org.dive4elements.river.exports.minfo.SedimentLoadExporter"/>
+    <!-- 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>
+
Binary file artifacts/doc/conf/jasper/sqrelation.jasper has changed
Binary file artifacts/doc/conf/jasper/sqrelation_en.jasper has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/doc/conf/longitudinal-diagram-defaults.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<longitudinal-defaults>
+    <axis name="W"/>
+    <axis name="diffW"/>
+    <axis name="deltaW"/>
+    <axis name="diffBed"/>
+    <axis name="diffBed/Y"/>
+    <axis name="BedHeight"/>
+    <axis name="SoundingWidth"/>
+    <axis name="Width"/>
+    <axis name="Diameter"/>
+    <axis name="Density"/>
+    <axis name="Porosity"/>
+    <axis name="SedimentLoad"/>
+    <axis name="Velocity"/>
+    <axis name="Tau"/>
+    <axis name="Q" include-zero="true"/>
+    <domain-axis key="chart.longitudinal.section.xaxis.label" default="Fluss-Km"
+            inverted="org.dive4elements.river.exports.IsKmUpEvaluator()">
+        <arg expr="artifact.river"/>
+    </domain-axis>
+    <!-- Default longitudinal section Processors -->
+    <processor class="org.dive4elements.river.exports.process.DeltaWProcessor" axis="deltaW"/>
+    <processor class="org.dive4elements.river.exports.process.AnnotationProcessor" axis="none"/>
+    <processor class="org.dive4elements.river.exports.process.AreaProcessor" axis="none"/>
+    <processor class="org.dive4elements.river.exports.process.WDiffProcessor" axis="diffW"/>
+    <processor class="org.dive4elements.river.exports.process.WOutProcessor" axis="W"/>
+    <processor class="org.dive4elements.river.exports.process.QOutProcessor" axis="Q"/>
+    <processor class="org.dive4elements.river.exports.process.BedHeightSoundingProcessor" axis="SoundingWidth"/>
+    <processor class="org.dive4elements.river.exports.process.BedWidthProcessor" axis="Width"/>
+    <processor class="org.dive4elements.river.exports.process.BedDiffYearProcessor" axis="diffBed"/>
+    <processor class="org.dive4elements.river.exports.process.BedDiffHeightYearProcessor" axis="diffBed/Y"/>
+    <processor class="org.dive4elements.river.exports.process.MiddleBedHeightProcessor" axis="BedHeight"/>
+    <processor class="org.dive4elements.river.exports.process.BedQualityDiameterProcessor" axis="Diameter"/>
+    <processor class="org.dive4elements.river.exports.process.BedQualityPorosityProcessor" axis="Porosity"/>
+    <processor class="org.dive4elements.river.exports.process.BedQualityDensityProcessor" axis="Density"/>
+    <processor class="org.dive4elements.river.exports.process.SedimentLoadProcessor" axis="SedimentLoad"/>
+    <processor class="org.dive4elements.river.exports.process.FlowVelocityProcessor" axis="Velocity"/>
+    <processor class="org.dive4elements.river.exports.process.ShearStressProcessor" axis="Tau"/>
+</longitudinal-defaults>
--- a/artifacts/doc/conf/mapserver/barrier_lines_class.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/barrier_lines_class.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -1,6 +1,6 @@
 CLASS
     NAME "Damm"
-    EXPRESSION ("[TYP]"="Damm")
+    EXPRESSION /damm/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#008000"
@@ -8,7 +8,7 @@
 END
 CLASS
     NAME "Rohr 1"
-    EXPRESSION ("[TYP]"="Rohr 1")
+    EXPRESSION /rohr.1/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#800080"
@@ -16,7 +16,7 @@
 END
 CLASS
     NAME "Rohr 2"
-    EXPRESSION ("[TYP]"="Rohr 2")
+    EXPRESSION /rohr.2/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#808080"
@@ -24,7 +24,7 @@
 END
 CLASS
     NAME "Graben"
-    EXPRESSION ("[TYP]"="Graben")
+    EXPRESSION /graben/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#800000"
@@ -32,7 +32,7 @@
 END
 CLASS
     NAME "Ringdeich"
-    EXPRESSION ("[TYP]"="Ringdeich")
+    EXPRESSION /ringdeich/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#800000"
--- a/artifacts/doc/conf/mapserver/barrier_polygons_class.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/barrier_polygons_class.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -1,6 +1,6 @@
 CLASS
     NAME "Ringdeich"
-    EXPRESSION ("[TYP]"="Ringdeich")
+    EXPRESSION /ringdeich/i
     STYLE
         SIZE 5
         OUTLINECOLOR "#FF8000"
--- a/artifacts/doc/conf/mapserver/mapfile.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/mapfile.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -19,23 +19,26 @@
     WEB
       METADATA
         "wms_title"             "FLYS Web Map Service"
-        "wms_onlineresource"    "$MAPSERVERURL"
+        "wms_onlineresource"    "${MAPSERVERURL}user-wms"
         "wms_encoding"          "UTF-8"
         "wms_accessconstraints" "none"
         "wms_fees"              "none"
         "wms_addresstype"       "postal"
-        "wms_address"           "Any Street"
-        "wms_city"              "Any City"
-        "wms_stateorprovince"   "Any state"
-        "wms_postcode"          "My Postalcode"
-        "wms_country"           "Any Country"
-        "wms_contactperson"     "Any Person"
-        "wms_contactorganization" "Any Orga"
-        "wms_contactelectronicmailaddress" "any-email@example.com"
-        "wms_contactvoicetelephone" "Any's telephone number"
+        "wms_address"           ""
+        "wms_city"              ""
+        "wms_stateorprovince"   ""
+        "wms_postcode"          ""
+        "wms_country"           ""
+        "wms_contactperson"     ""
+        "wms_contactorganization" ""
+        "wms_contactelectronicmailaddress" ""
+        "wms_contactvoicetelephone" ""
         "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467"
-        "wms_feature_info_mime_type" "text/html"
+        "wms_getmap_formatlist" "image/png,image/png; mode=24bit,image/jpeg"
         "ows_enable_request"   "*"
+        "ows_sld_enabled"  "false"
+        "ows_title"             "FLYS Web Map Service"
+        "ows_extent"            "3233232 5303455 3421524 5585825"
       END
     END
 
--- a/artifacts/doc/conf/mapserver/river-mapfile.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/river-mapfile.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -19,7 +19,7 @@
     WEB
       METADATA
         "wms_title"             "FLYS Rivers Web Map Service"
-        #"wms_onlineresource"    "http://localhost:7777/river-wms" # "$MAPSERVERURL"
+        "wms_onlineresource"    "${MAPSERVERURL}river-wms"
         "wms_encoding"          "UTF-8"
         "wms_accessconstraints" "none"
         "wms_fees"              "none"
@@ -34,7 +34,6 @@
         "wms_contactelectronicmailaddress" "any-email@example.com"
         "wms_contactvoicetelephone" "Any's telephone number"
         "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467"
-        "wms_feature_info_mime_type" "text/html"
         "ows_enable_request"   "*"
       END
     END
--- a/artifacts/doc/conf/mapserver/shapefile_layer.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/shapefile_layer.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -21,6 +21,8 @@
             "wms_group_title" "$LAYER.getGroupTitle()"
         #end
     END
+    
+    CLASSITEM "TYP"
 
     #if ( !$LAYER.getStyle() )
         #if ( $LAYER.getGroupTitle() )
--- a/artifacts/doc/conf/mapserver/wsplgen_layer.vm	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/mapserver/wsplgen_layer.vm	Fri Sep 27 17:36:50 2013 +0200
@@ -19,6 +19,7 @@
         "gml_include_items" "all"
         "ows_enable_request" "GetFeatureInfo"
         "wms_feature_info_mime_type" "gml"
+        "wms_srs" "EPSG:4326 EPSG:31466 EPSG:31467"
         #if ( $LAYER.getGroupTitle() )
             "wms_group_title" "$LAYER.getGroupTitle()"
         #end
--- a/artifacts/doc/conf/meta-data.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/meta-data.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -141,6 +141,7 @@
                   </dc:when>
                   <dc:when test="$out = 'longitudinal_section'">
                     <dc:call-macro name="longitudinal-section-prototype"/>
+                    <dc:call-macro name="bedheight_differences"/>
                   </dc:when>
                   <dc:when test="$out = 'w_differences'">
                     <dc:call-macro name="longitudinal-section-prototype"/>
@@ -166,7 +167,7 @@
                     <dc:call-macro name="discharge_table_gauge"/>
                     <dc:call-macro name="basedata_2_fixations_wqkms"/>
                     <dc:call-macro name="basedata_5_flood-protections"/>
-                    <dc:call-macro name="basedata_0"/>
+                    <dc:call-macro name="basedata_0_wq"/>
                     <dc:call-macro name="basedata_1_additionals"/>
                     <dc:call-macro name="basedata_4_heightmarks-points"/>
                     <computed_discharge_curve>
@@ -285,8 +286,8 @@
                     <dc:call-macro name="discharge_table_gauge"/>
                     <dc:call-macro name="basedata_2_fixations_wqkms"/>
                     <dc:call-macro name="basedata_5_flood-protections"/>
-                    <dc:call-macro name="basedata_0"/>
-                    <dc:call-macro name="basedata_1_additionals"/>
+                    <dc:call-macro name="basedata_0_wq"/>
+                    <dc:call-macro name="basedata_1_additionals_wq"/>
                     <dc:call-macro name="basedata_4_heightmarks-points"/>
                     <computed_discharge_curve>
                       <dc:call-macro name="mainvalues"/>
@@ -353,6 +354,17 @@
                     <dc:when test="$out = 'longitudinal_section'">
                       <dc:call-macro name="longitudinal"/>
                       <dc:call-macro name="differences"/>
+                      <dc:call-macro name="bedheight_differences"/>
+                      <dc:call-macro name="bedquality-bed"/>
+                      <dc:call-macro name="bedquality-load"/>
+                      <dc:call-macro name="flow-velocity"/>
+                      <dc:call-macro name="sediment-load"/>
+                      <dc:call-macro name="bedquality-density"/>
+                      <dc:call-macro name="bedquality-porosity"/>
+                      <dc:call-macro name="waterlevels-discharge"/>
+                      <dc:call-macro name="differenceable-fix"/>
+                      <dc:call-macro name="delta-wt-ls"/>
+                      <dc:call-macro name="longitudinal-section"/>
                     </dc:when>
                     <dc:when test="$out = 'discharge_longitudinal_section'">
                       <dc:call-macro name="longitudinal"/>
@@ -401,6 +413,7 @@
                     <dc:when test="$out = 'bedheight_middle'">
                       <dc:call-macro name="waterlevels-discharge"/>
                       <dc:call-macro name="differenceable-fix"/>
+                      <dc:call-macro name="differences"/>
                     </dc:when>
                     <dc:when test="$out = 'floodmap-hws'">
                       <dc:call-macro name="floodmap-hws-user"/>
@@ -485,22 +498,22 @@
 
 
     <dc:macro name="historical_discharge_curve">
+      <dc:variable name="refgauge" type="number" expr="$reference_gauge"/>
       <dc:context>
         <dc:statement>
           SELECT g.id   AS gauge_id,
-                 g.name AS gauge_name,
-                 dt.id AS dt_id,
-                 t.start_time AS start_time,
-                 t.stop_time AS stop_time,
-                 dt.description AS desc,
-                 dt.bfg_id AS bfg_id
+          g.name AS gauge_name,
+          dt.id AS dt_id,
+          t.start_time AS start_time,
+          t.stop_time AS stop_time,
+          dt.bfg_id AS bfg_id
           FROM gauges g
           JOIN discharge_tables dt ON g.id = dt.gauge_id
           LEFT JOIN time_intervals t ON dt.time_interval_id = t.id
           WHERE g.river_id = ${river_id}
           AND dt.kind &lt;&gt; 0
-          AND g.station = ${fromkm}
-          AND g.station = ${tokm}
+          AND ((g.station = ${fromkm} AND g.station = ${tokm})
+          OR g.official_number = ${refgauge})
           ORDER BY start_time
         </dc:statement>
         <dc:if test="dc:has-result()">
@@ -508,9 +521,6 @@
             <dc:group expr="$gauge_name">
               <dc:for-each>
                 <dc:variable name="combined_desc" expr="concat($bfg_id, ' ', dc:date-format('dd.MM.yyyy', $start_time), ' - ', dc:date-format('dd.MM.yyyy', $stop_time))"/>
-                <dc:message>
-                  Hallo ANDRE23 {dc:dump-variables()}
-                </dc:message>
                 <histdis name="{$combined_desc}"
                   description="{$combined_desc}"
                   factory="gaugedischarge" target_out="{$out}"
@@ -877,7 +887,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="bed_longitudinal_section"/>
                   </dc:element>
                 </dc:for-each>
@@ -901,7 +911,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="bed_longitudinal_section"/>
                   </dc:element>
                 </dc:for-each>
@@ -926,7 +936,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="bed_longitudinal_section"/>
                   </dc:element>
                 </dc:for-each>
@@ -951,7 +961,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="bed_longitudinal_section"/>
                   </dc:element>
                 </dc:for-each>
@@ -978,7 +988,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="flow_velocity"/>
                   </dc:element>
                 </dc:for-each>
@@ -1001,7 +1011,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="sedimentload_ls"/>
                   </dc:element>
                 </dc:for-each>
@@ -1040,7 +1050,7 @@
       <dc:filter expr="$out_name = 'discharge_longitudinal_section' and $facet_name = 'discharge_longitudinal_section.w'">
         <dc:if test="dc:has-result()">
           <waterlevels-discharge>
-            <dc:group expr="concat($oid, ' ', $river, ' ', $a_id, ' ', dc:date-format('dd.MM.yyyy - H:mm:ss', $a_creation), ' ', $collection_name)">
+            <dc:group expr="concat($oid, ' ', $river, ' ', $a_gid, ' ', dc:date-format('dd.MM.yyyy - H:mm:ss', $a_creation), ' ', $collection_name)">
               <discharge description="{dc:group-key()}">
                 <dc:for-each>
                   <dc:element name="${facet_name}">
@@ -1048,7 +1058,7 @@
                     <dc:attribute name="target_out" value="${out}"/>
                     <dc:attribute name="description" value="${facet_description}"/>
                     <dc:attribute name="ids" value="${facet_num}-${facet_name}"/>
-                    <dc:attribute name="artifact-id" value="${a_id}"/>
+                    <dc:attribute name="artifact-id" value="${a_gid}"/>
                     <dc:attribute name="out" value="longitudinal_section"/>
                   </dc:element>
                 </dc:for-each>
@@ -1210,8 +1220,74 @@
       <dc:call-macro name="basedata_3_officials"/>
       <dc:call-macro name="basedata_5_flood-protections"/>
       <dc:call-macro name="annotations_per_type"/>
+      <minfo>
+          <dc:call-macro name="basedata_6_delta_w"/>
+          <dc:call-macro name="basedata_7_waterlevels"/>
+          <dc:call-macro name="minfo-heights"/>
+          <dc:call-macro name="sounding-width"/>
+          <dc:call-macro name="yields"/>
+      </minfo>
     </dc:macro>
 
+    <dc:macro name="yields">
+      <yields>
+      <years>
+      <dc:context>
+        <dc:statement>
+            SELECT DISTINCT
+                sy.id AS syid,
+                sy.description AS description,
+                ti.start_time AS year
+            FROM     sediment_yield sy
+                JOIN rivers r ON sy.river_id = r.id
+                JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id
+                JOIN time_intervals ti ON sy.time_interval_id = ti.id
+            WHERE   r.name = 'Elbe'
+                AND ti.stop_time IS NULL
+                AND syv.station BETWEEN ${fromkm} AND ${tokm}
+        </dc:statement>
+        <dc:if test="dc:has-result()">
+          <dc:for-each>
+            <year description="{$description}"
+                   factory="sedimentyield"
+                   target_out="{$out}"
+                   info="infome"
+                  ids="{$syid}" />
+          </dc:for-each>
+        </dc:if>
+      </dc:context>
+      </years>
+      <epochs>
+      <dc:context>
+        <dc:statement>
+            SELECT DISTINCT
+                sy.id AS syid,
+                sy.description AS description,
+                ti.start_time AS year
+            FROM     sediment_yield sy
+                JOIN rivers r ON sy.river_id = r.id
+                JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id
+                JOIN time_intervals ti ON sy.time_interval_id = ti.id
+            WHERE   r.name = 'Elbe'
+                AND ti.stop_time IS NOT NULL
+                AND syv.station BETWEEN ${fromkm} AND ${tokm}
+        </dc:statement>
+        <dc:if test="dc:has-result()">
+          <dc:for-each>
+            <epoch description="{$description}"
+                   factory="sedimentyield"
+                   target_out="{$out}"
+                   info="infome"
+                   ids="{$syid}" />
+          </dc:for-each>
+        </dc:if>
+      </dc:context>
+      </epochs>
+
+      </yields>
+    </dc:macro>
+
+
     <dc:macro name="basedata_5_flood-protections">
       <dc:filter expr="$kind=5">
         <dc:if test="dc:has-result()">
@@ -1306,20 +1382,59 @@
       </dc:filter>
     </dc:macro>
 
+
+    <dc:macro name="basedata_7_waterlevels">
+      <dc:filter expr="$kind=7">
+        <dc:if test="dc:has-result()">
+          <wlevel>
+            <dc:group expr="dc:replace($wst_description, 'CSV/', '')">
+              <relativepoint name="{dc:group-key()}">
+                <dc:for-each>
+                  <column name="{$wst_column_name}"
+                          ids="additionals-wstv-{$wst_column_position}-{$wst_id}"
+                          factory="staticwqkms" target_out="{$out}"
+                          info="{$info} [km {$deffrom} - {$defto}]"/>
+                </dc:for-each>
+              </relativepoint>
+            </dc:group>
+          </wlevel>
+        </dc:if>
+      </dc:filter>
+    </dc:macro>
+
+
     <dc:macro name="basedata_6_delta_w">
       <dc:filter expr="$kind=6">
         <dc:if test="dc:has-result()">
           <delta_w>
-            <dc:group expr="$wst_description">
-              <relativepoint name="{dc:group-key()}">
-                <dc:for-each>
-                  <column name="{$wst_column_name}"
-                          ids="delta_w-wstv-{$wst_column_position}-{$wst_id}"
-                          factory="staticwkms" target_out="{$out}"
-                          info="{$info} [km {$deffrom} - {$defto}]"/>
-                </dc:for-each>
-              </relativepoint>
-            </dc:group>
+            <delta_w_cm>
+              <dc:filter expr="contains($wst_description, 'cm.csv')">
+                <dc:group expr="dc:replace($wst_description, 'CSV/', '')">
+                  <relativepoint name="{dc:group-key()}">
+                    <dc:for-each>
+                      <column name="{$wst_column_name}"
+                              ids="delta_w-wstv-{$wst_column_position}-{$wst_id}"
+                              factory="staticwkms" target_out="{$out}"
+                              info="{$info} [km {$deffrom} - {$defto}]"/>
+                    </dc:for-each>
+                  </relativepoint>
+                </dc:group>
+              </dc:filter>
+            </delta_w_cm>
+            <delta_w_cma>
+              <dc:filter expr="contains($wst_description, 'cm-a.csv')">
+                <dc:group expr="dc:replace($wst_description, 'CSV/', '')">
+                  <relativepoint name="{dc:group-key()}">
+                    <dc:for-each>
+                      <column name="{$wst_column_name}"
+                              ids="delta_w-wstv-{$wst_column_position}-{$wst_id}"
+                              factory="staticwkms" target_out="{$out}"
+                              info="{$info} [km {$deffrom} - {$defto}]"/>
+                    </dc:for-each>
+                  </relativepoint>
+                </dc:group>
+              </dc:filter>
+            </delta_w_cma>
           </delta_w>
         </dc:if>
       </dc:filter>
@@ -1337,17 +1452,14 @@
           LEFT JOIN time_intervals t ON dt.time_interval_id = t.id
           WHERE g.river_id = ${river_id}
           AND dt.kind = 0
-          AND g.station = ${fromkm}
-          AND g.station = ${tokm}
+          AND ((g.station = ${fromkm} AND g.station = ${tokm})
+          OR g.official_number = ${refgauge})
         </dc:statement>
         <dc:if test="dc:has-result()">
-          <current_gauge>
-            <dc:for-each>
-              <gauge name="{$gauge_name} ({dc:date-format('dd.MM.yyyy', $start_time)})"
-                factory="gaugedischarge" target_out="{$out}"
-                ids="{$gauge_name}"/>
-            </dc:for-each>
-          </current_gauge>
+          <dc:for-each>
+            <current_gauge factory="gaugedischarge" target_out="{$out}"
+              ids="{$gauge_name}"/>
+          </dc:for-each>
         </dc:if>
       </dc:context>
     </dc:macro>
--- a/artifacts/doc/conf/themes.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/themes.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -177,9 +177,12 @@
         <mapping from="other.wq" to="WQPoints" />
         <mapping from="other.wkms" to="WKms" />
         <mapping from="other.wkms.marks" to="WKmsAnnotation" />
+        <mapping from="other.wqkms" to="WQKmsHorizontal" masterAttr="calculation_mode==calc.discharge.curve" />
         <mapping from="other.wqkms" to="WQKms" />
+        <mapping from="other.wqkms.w" to="WQKmsHorizontal" masterAttr="calculation_mode==calc.discharge.curve" />
         <mapping from="other.wqkms.w" to="WQKms" />
         <mapping from="other.wqkms.q" to="WQKms" />
+        <mapping from="other.w.interpol" to="MainValuesW" />
         <mapping from="heightmarks_points" to="heightmarks_points" />
         <mapping from="area" to="Area" />
         <mapping from="cross_section.area" to="CrossSectionArea" />
@@ -207,6 +210,7 @@
         <mapping from="flow_velocity.mainchannel.filtered" to="FlowVelocityVMainChannel" />
         <mapping from="flow_velocity.tau.filtered" to="FlowVelocityTau" />
         <mapping from="flow_velocity.discharge" to="FlowVelocityDischarge" />
+        <mapping from="flow_velocity.measurement" to="FlowVelocityDischarge" />
         <mapping from="bedheight_middle.single" to="MiddleBedHeightSingle" />
         <mapping from="bedheight_middle.epoch" to="MiddleBedHeightEpoch" />
         <mapping from="bed_longitudinal_section.porosity_toplayer" to="PorosityTopLayer" />
@@ -273,6 +277,7 @@
         <mapping from="fix_analysis_events_wq" to="FixingAnalysisEventsWQ" />
         <mapping from="fix_outlier" to="FixingOutlier" />
         <mapping from="fix_wq_curve" to="FixingWQCurve" />
+        <mapping from="fix_wq_ls" to="FixingCalculatedPoint" />
         <mapping from="fix_reference_events_wq" to="FixingReferenceEvents" />
         <mapping from="fix_sector_average_dwt_0" to="FixingDeltaWtAverage0" />
         <mapping from="fix_sector_average_dwt_1" to="FixingDeltaWtAverage1" />
--- a/artifacts/doc/conf/themes/default.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/themes/default.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -84,6 +84,8 @@
                 display="Punktbeschriftung anzeigen" default="false" hints="hidden" />
             <field name="linecolor" type="Color" display="Linienfarbe"
                 default="204, 204, 204" />
+            <field name="textorientation" type="boolean" display="Textausrichtung"
+                default="true" />
         </fields>
     </theme>
 
@@ -116,6 +118,19 @@
         </fields>
     </theme>
 
+    <theme name="WQKmsHorizontal">
+        <inherits>
+            <inherit from="WQKms" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Linienfarbe"
+                default="204, 204, 204" />
+            <field name="textorientation" type="boolean" display="Textausrichtung"
+                default="true" />
+        </fields>
+    </theme>
+
+
     <theme name="WQPoints">
         <inherits>
             <inherit from="Points" />
@@ -152,7 +167,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
     <!-- top level classes defining additional attributes-->
@@ -1185,7 +1200,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
 
@@ -1254,7 +1269,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="true"/>
         </fields>
     </theme>
 
@@ -1455,7 +1470,21 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
+        </fields>
+    </theme>
+
+    <theme name="FixingCalculatedPoint">
+        <inherits>
+            <inherit from="FixPoints" />
+        </inherits>
+        <fields>
+            <field name="pointcolor" type="Color" display="Punktfarbe"
+                default="0, 0, 0" />
+            <field name="pointsize" type="int" display="Punktdicke"
+                default="5"/>
+            <field name="showpointlabel" type="boolean"
+                display="Punktbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
 
@@ -1561,9 +1590,21 @@
         </fields>
     </theme>
 
+    <theme name="FixingDeltaWtAverage">
+        <inherits>
+            <inherit from="FixLines" />
+        </inherits>
+        <fields>
+            <field name="showpointlabel" type="boolean"
+                display="Punktbeschriftung anzeigen" default="false" hints="hidden"/>
+            <field name="showlinelabel" type="boolean"
+                display="Beschriftung anzeigen" default="true" />
+        </fields>
+    </theme>
+
     <theme name="FixingDeltaWtAverage0">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1573,7 +1614,7 @@
 
     <theme name="FixingDeltaWtAverage1">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1583,7 +1624,7 @@
 
     <theme name="FixingDeltaWtAverage2">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1593,7 +1634,7 @@
 
     <theme name="FixingDeltaWtAverage3">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
--- a/artifacts/doc/conf/themes/second.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/doc/conf/themes/second.xml	Fri Sep 27 17:36:50 2013 +0200
@@ -14,6 +14,20 @@
         </fields>
     </theme>
 
+    <theme name="MainValuesQVerticalText">
+        <inherits>
+            <inherit from="Lines" />
+        </inherits>
+        <fields>
+            <field name="linecolor" type="Color" display="Farbe"
+                default="200, 0, 15" />
+            <field name="textorientation" type="boolean" display="Textausrichtung"
+                default="false" />
+            <field name="showlinelabel" type="boolean"
+                display="Linienbeschriftung anzeigen" default="false" hints="hidden" />
+        </fields>
+    </theme>
+
     <theme name="MainValuesW">
         <inherits>
             <inherit from="Lines" />
@@ -28,20 +42,6 @@
         </fields>
     </theme>
 
-    <theme name="MainValuesQVerticalText">
-        <inherits>
-            <inherit from="Lines" />
-        </inherits>
-        <fields>
-            <field name="linecolor" type="Color" display="Farbe"
-                default="200, 0, 15" />
-            <field name="textorientation" type="boolean" display="Textausrichtung"
-                default="false" />
-            <field name="showlinelabel" type="boolean"
-                display="Linienbeschriftung anzeigen" default="false" hints="hidden" />
-        </fields>
-    </theme>
-
     <theme name="RelativePoint">
         <inherits>
             <inherit from="Points" />
@@ -152,7 +152,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
     <!-- top level classes defining additional attributes-->
@@ -843,7 +843,7 @@
             <field name="showlines" type="boolean" display="Linie anzeigen"
                 default="true" />
             <field name="linesize" type="int" display="Liniendicke"
-                default="2" />
+                default="1" />
             <field name="linetype" type="Dash" display="Linienart"
                 default="10" />
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1185,7 +1185,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
 
@@ -1254,7 +1254,7 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="true"/>
         </fields>
     </theme>
 
@@ -1455,7 +1455,21 @@
             <field name="showarea" type="boolean" display="Flaeche anzeigen"
               default="true" hints="hidden" />
             <field name="showarealabel" type="boolean"
-                display="Flächenbeschriftung anzeigen" default="false" hints="hidden" />
+                display="Flächenbeschriftung anzeigen" default="false"/>
+        </fields>
+    </theme>
+
+    <theme name="FixingCalculatedPoint">
+        <inherits>
+            <inherit from="FixPoints" />
+        </inherits>
+        <fields>
+            <field name="pointcolor" type="Color" display="Punktfarbe"
+                default="0, 0, 0" />
+            <field name="pointsize" type="int" display="Punktdicke"
+                default="5"/>
+            <field name="showpointlabel" type="boolean"
+                display="Punktbeschriftung anzeigen" default="false"/>
         </fields>
     </theme>
 
@@ -1561,9 +1575,21 @@
         </fields>
     </theme>
 
+    <theme name="FixingDeltaWtAverage">
+        <inherits>
+            <inherit from="FixLines" />
+        </inherits>
+        <fields>
+            <field name="showpointlabel" type="boolean"
+                display="Punktbeschriftung anzeigen" default="false" hints="hidden"/>
+            <field name="showlinelabel" type="boolean"
+                display="Beschriftung anzeigen" default="true" />
+        </fields>
+    </theme>
+
     <theme name="FixingDeltaWtAverage0">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1573,7 +1599,7 @@
 
     <theme name="FixingDeltaWtAverage1">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1583,7 +1609,7 @@
 
     <theme name="FixingDeltaWtAverage2">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
@@ -1593,7 +1619,7 @@
 
     <theme name="FixingDeltaWtAverage3">
         <inherits>
-            <inherit from="FixLines" />
+            <inherit from="FixingDeltaWtAverage" />
         </inherits>
         <fields>
             <field name="linecolor" type="Color" display="Linienfarbe"
--- a/artifacts/pom.xml	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/pom.xml	Fri Sep 27 17:36:50 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/CrossSectionArtifact.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/CrossSectionArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -135,7 +135,8 @@
             CrossSectionLine csl = csls.get(0);
             // Find min-km of cross sections,
             // then set DATA_KM to min(DATA_KM, minCross).
-            double dataKm = Double.valueOf(getDataAsString(DATA_KM));
+            String dataKmValue = getDataAsString(DATA_KM);
+            double dataKm = (dataKmValue != null) ? Double.valueOf(dataKmValue) : Double.MIN_VALUE;
             if (dataKm < csl.getKm().doubleValue()) {
                 addStringData(DATA_KM, csl.getKm().toString());
             }
@@ -248,6 +249,10 @@
      */
     private double getParentKm() {
         String val = getDataAsString(PARENT_KM);
+        if (val == null) {
+            logger.warn("Empty data: " + PARENT_KM);
+            return 0;
+        }
         try {
             return Double.valueOf(val);
         }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/D4EArtifact.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/D4EArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -1305,8 +1305,9 @@
 
         for (Output out: list) {
             log.debug("check facets for output: " + out.getName());
+            String outName = out.getName();
             Output o = new DefaultOutput(
-                out.getName(),
+                outName,
                 out.getDescription(),
                 out.getMimeType(),
                 out.getType());
@@ -1323,7 +1324,14 @@
             for (Facet f: fs) {
                 String type = f.getName();
 
-                if (outTypes.contains(type)) {
+                /* Match the facets to the output configuration.
+                 * This is only done when we are not already bound to an out.
+                 * If we are bound we come from the datacage so the user has
+                 * explicitly requested our nice and shiny facets. And who
+                 * are we to deny them to him. */
+                if (outTypes.contains(type) ||
+                        (boundToOut != null &&
+                         boundToOut.equals(outName))) {
                     if (debug) {
                         log.debug("Add facet " + f);
                     }
@@ -1491,7 +1499,7 @@
     /**
      * Method to dump the artifacts state/data.
      */
-    protected void dumpArtifact() {
+    public void dumpArtifact() {
         log.debug("++++++++++++++ DUMP ARTIFACT DATA +++++++++++++++++");
         // Include uuid, type, name
         log.debug(" - Name: " + getName());
@@ -1525,7 +1533,7 @@
     }
 
 
-    protected void debugFacets() {
+    public void debugFacets() {
         log.debug("######### FACETS #########");
 
         for (Map.Entry<String, List<Facet>> entry: facets.entrySet()) {
@@ -1541,7 +1549,7 @@
     }
 
 
-    protected void dumpFilterFacets() {
+    public void dumpFilterFacets() {
         log.debug("######## FILTER FACETS ########");
 
         if (filterFacets == null || filterFacets.isEmpty()) {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/FlowVelocityMeasurementArtifact.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/FlowVelocityMeasurementArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,6 +8,8 @@
 
 package org.dive4elements.river.artifacts;
 
+import java.text.DateFormat;
+
 import java.util.ArrayList;
 import java.util.List;
 
@@ -29,6 +31,8 @@
 
 import org.dive4elements.river.artifacts.model.FacetTypes;
 
+import org.dive4elements.river.utils.Formatter;
+
 
 /** Artefact to access flow velocity measurements. */
 public class FlowVelocityMeasurementArtifact
@@ -111,13 +115,24 @@
 
         List<Facet> fs = new ArrayList<Facet>();
         String code = getDatacageIDValue(data);
+        DateFormat dateFormatter = Formatter.getDateFormatter(
+            callMeta, "dd.MM.yyy HH:mm");
 
         if (code != null) {
             // parse code, interact with factory, add real facets.
             // store relevant parts of code as data.
+            FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue
+                flowVelocityMeasurement =
+                    FlowVelocityMeasurementFactory.getFlowVelocityMeasurement(
+                        Integer.parseInt(code));
+            String name = flowVelocityMeasurement.getDescription();
+            logger.debug ("datetime " + flowVelocityMeasurement.getDatetime());
+            name += " - " + dateFormatter.format(
+                flowVelocityMeasurement.getDatetime());
+
             Facet facet = new FlowVelocityMeasurementFacet(
                 FLOW_VELOCITY_MEASUREMENT,
-                "flowvelocity-name");
+                name);
             fs.add(facet);
             addFacets(state.getID(), fs);
             addStringData(DATA_NAME, code);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/SedimentYieldArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,229 @@
+/* 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.artifacts;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.w3c.dom.Document;
+
+import org.dive4elements.artifactdatabase.state.DefaultOutput;
+import org.dive4elements.artifactdatabase.state.Facet;
+import org.dive4elements.artifactdatabase.state.FacetActivity;
+import org.dive4elements.artifactdatabase.state.State;
+import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactFactory;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.artifacts.common.utils.XMLUtils;
+import org.dive4elements.river.artifacts.states.DefaultState.ComputeType;
+import org.dive4elements.river.artifacts.model.Calculation;
+import org.dive4elements.river.artifacts.model.CalculationResult;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoad;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFacet;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFactory;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadResult;
+import org.dive4elements.river.artifacts.states.StaticState;
+
+import org.dive4elements.river.artifacts.model.FacetTypes;
+
+import org.dive4elements.river.utils.Formatter;
+
+
+/** Artifact to access sediment yield measurements (inspired from flow velocity artifact). */
+public class SedimentYieldArtifact
+extends      StaticD4EArtifact
+implements   FacetTypes
+{
+    /** The logger for this class. */
+    private static Logger logger =
+        Logger.getLogger(SedimentYieldArtifact.class);
+
+    /** Artifact key name. */
+    private static final String NAME = "sedimentyield";
+
+    /** Spawn only inactive facets. */
+    static {
+        // TODO: Move to configuration.
+        FacetActivity.Registry.getInstance()
+            .register(NAME, FacetActivity.INACTIVE);
+    }
+
+    /** Need to give the state an id. */
+    public static final String STATIC_STATE_NAME =
+        "state.sedimentyield.static";
+
+    /** One and only state to be in. */
+    protected transient State state = null;
+
+    protected String DATA_NAME = "ID";
+
+    /**
+     * Trivial Constructor.
+     */
+    public SedimentYieldArtifact() {
+        logger.debug("SedimentYieldArtifact.SedimentYieldArtifact");
+    }
+
+
+    /** Get artifact key name. */
+    @Override
+    public String getName() {
+        return NAME;
+    }
+
+
+    private Object getSedimentLoad() {
+        logger.debug("SedimentYieldArtifact.getSedimentLoad");
+        String id = getDataAsString(DATA_NAME);
+        String river = getDataAsString("river");
+
+        SedimentLoad myLoad = SedimentLoadFactory.getSedimentLoadWithDataUncached(id, river);
+        return new CalculationResult(
+            new SedimentLoadResult[] {
+                new SedimentLoadResult(1983,2042,myLoad)
+            }, new Calculation());
+    }
+
+
+    /** Create a new state with bogus output. */
+    protected State spawnState() {
+        state = new StaticState(STATIC_STATE_NAME) {
+
+            public Object staticCompute(List<Facet> facets) {
+                 return getSedimentLoad();
+            }
+        };
+        List<Facet> fs = getFacets(STATIC_STATE_NAME);
+        DefaultOutput output = new DefaultOutput(
+            "general",
+            "general",
+            "image/png",
+            fs,
+            "chart");
+
+        state.getOutputs().add(output);
+
+        return state;
+    }
+
+
+    /**
+     * Gets called from factory, to set things up.
+     */
+    @Override
+    public void setup(
+        String          identifier,
+        ArtifactFactory factory,
+        Object          context,
+        CallMeta        callMeta,
+        Document        data)
+    {
+        logger.debug("SedimentYieldArtifact.setup");
+
+        // Refactor? this happens at another place, too
+        // Store id, yield yields.
+        state = new StaticState(STATIC_STATE_NAME) {
+
+            public Object staticCompute(List<Facet> facets) {
+                 return getSedimentLoad();
+            }
+        };
+        if (logger.isDebugEnabled()) {
+            logger.debug(XMLUtils.toString(data));
+        }
+
+        List<Facet> fs = new ArrayList<Facet>();
+        String code = getDatacageIDValue(data);
+
+        // TODO need river, too.
+        //
+        if (code != null) {
+            String name = SedimentLoadFactory.getSedimentYieldDescription(Integer.valueOf(code));
+
+            Facet facet = new SedimentLoadFacet(
+                0,
+                SEDIMENT_LOAD_COARSE,
+                name,
+                //????
+                ComputeType.ADVANCE, state.getID(), "hash"
+                );
+            fs.add(facet);
+            addFacets(state.getID(), fs);
+            addStringData(DATA_NAME, code);
+        }
+
+        spawnState();
+        super.setup(identifier, factory, context, callMeta, data);
+    }
+
+
+    /**
+     * Get a list containing the one and only State.
+     * @param  context ignored.
+     * @return list with one and only state.
+     */
+    @Override
+    protected List<State> getStates(Object context) {
+        ArrayList<State> states = new ArrayList<State>();
+        states.add(getState());
+        return states;
+    }
+
+
+    /**
+     * Get the "current" state (there is but one).
+     * @param cc ignored.
+     * @return the "current" (only possible) state.
+     */
+    @Override
+    public State getCurrentState(Object cc) {
+        return getState();
+    }
+
+
+    /**
+     * Get the only possible state.
+     * @return the state.
+     */
+    protected State getState() {
+        return getState(null, null);
+    }
+
+
+    /**
+     * Get the state.
+     * @param context ignored.
+     * @param stateID ignored.
+     * @return the state.
+     */
+    @Override
+    protected State getState(Object context, String stateID) {
+        return (state != null)
+            ? state
+            : spawnState();
+    }
+
+
+    /**
+     * Called via setup. Overridden to avoid cloning all data.
+     *
+     * @param artifact The master-artifact.
+     */
+    @Override
+    protected void initialize(
+        Artifact artifact,
+        Object context,
+        CallMeta meta)
+    {
+        logger.debug("SedimentYieldArtifact.initialize");
+        importData((D4EArtifact) artifact, "river");
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWQKmsArtifact.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/StaticWQKmsArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -104,7 +104,7 @@
 
         // Store the 'ids' (from datacage).
         if (logger.isDebugEnabled()) {
-            logger.debug("StaticWQKmsArtiact.setup" + XMLUtils.toString(data));
+            logger.debug("StaticWQKmsArtifact.setup" + XMLUtils.toString(data));
         }
 
         String code = getDatacageIDValue(data);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/WQKmsInterpolArtifact.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/WQKmsInterpolArtifact.java	Fri Sep 27 17:36:50 2013 +0200
@@ -132,7 +132,15 @@
                     name = STATIC_WKMS_INTERPOL;
                 }
                 else {
-                    name = STATIC_WQ;
+                    // If all Qs are zero, add different facet to
+                    // signalize that we want data to be drawn as marks
+                    // on axis.
+                    if (wstValueHasZeroQ()) {
+                        name = STATIC_W_INTERPOL;
+                    }
+                    else {
+                        name = STATIC_WQ;
+                    }
                 }
 
                 Facet wQFacet = new WQFacet(name,
@@ -204,23 +212,51 @@
     }
 
 
+    /** True if Wst has only 'fake' (zero) Q-ranges. */
+    private boolean wstValueHasZeroQ() {
+        WstValueTable table = getValueTable();
+        return table.hasEmptyQ();
+    }
+
+
+    /** Get the WstValueTable that matches parameterization. */
+    private WstValueTable getValueTable() {
+        // Get WstValueTable
+        int wstId = getDataAsInt("wst_id");
+        if (getDataAsString("col_pos") != null) {
+            return WstValueTableFactory.getWstColumnTable(
+                wstId, getDataAsInt("col_pos"));
+        }
+        else {
+            return WstValueTableFactory.getTable(wstId);
+        }
+    }
+
+
+    /**
+     * Get WQ Values at a certain km, interpolating only if distance
+     * between two stations is smaller than given distance.
+     */
+    public double [][] getWQAtKm(
+        Double currentKm,
+        double maxKmInterpolDistance
+    ) {
+        // TODO yet to be implemented (issue1378).
+        return null;
+    }
+
+
     /**
      * Get WQ at a given km.
+     *
      * @param currentKm the requested km. If NULL, ld_location data
      *        will be used.
+     * @return [[q1,q2,q2],[w1,w2,w3]] ...
      */
     public double [][] getWQAtKm(Double currentKm) {
 
-        WstValueTable interpolator = null;
-        // Get WstValueTable
-        if (getDataAsString("col_pos") != null) {
-            interpolator = WstValueTableFactory.getWstColumnTable(
-                getDataAsInt("wst_id"), getDataAsInt("col_pos"));
-        }
-        else {
-            interpolator = WstValueTableFactory.getTable(
-                getDataAsInt("wst_id"));
-        }
+        // TODO issue1378: only interpolate if dist <= 100m
+        WstValueTable interpolator = getValueTable();
 
         Double tmp = (currentKm != null)
                      ? currentKm
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/access/DGMAccess.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,35 @@
+package org.dive4elements.river.artifacts.access;
+
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.model.DGM;
+
+public class DGMAccess
+extends      RangeAccess
+{
+    private DGM dgm;
+
+    private String geoJSON;
+
+    public DGMAccess() {
+    }
+
+    public DGMAccess(D4EArtifact artifact) {
+        super(artifact);
+    }
+
+    public DGM getDGM() {
+        if (dgm == null) {
+            Integer sridId = getInteger("dgm");
+            dgm = DGM.getDGM(sridId);
+        }
+        return dgm;
+    }
+
+    public String getGeoJSON() {
+        if (geoJSON == null) {
+            geoJSON = getString("uesk.barriers");
+        }
+        return geoJSON;
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/access/RangeAccess.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/access/RangeAccess.java	Fri Sep 27 17:36:50 2013 +0200
@@ -37,6 +37,8 @@
 
     private KM_MODE mode;
 
+    public RangeAccess() {
+    }
 
     public RangeAccess(D4EArtifact artifact) {
         super(artifact);
@@ -111,6 +113,14 @@
         return locations.toNativeArray();
     }
 
+    public boolean hasFrom() {
+        return from != null || (from = getDouble("ld_from")) != null;
+    }
+
+    public boolean hasTo() {
+        return to != null || (to = getDouble("ld_to")) != null;
+    }
+
 
     /** Return ld_from data (in km). */
     public double getFrom() {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/access/SQRelationAccess.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/access/SQRelationAccess.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,11 +9,13 @@
 package org.dive4elements.river.artifacts.access;
 
 import java.util.Date;
+import java.util.List;
 
 import org.apache.log4j.Logger;
 
 import org.dive4elements.river.artifacts.D4EArtifact;
 import org.dive4elements.river.artifacts.model.DateRange;
+import org.dive4elements.river.model.MeasurementStation;
 
 public class SQRelationAccess
 extends      RiverAccess
@@ -28,6 +30,8 @@
 
     private String      method;
 
+    protected MeasurementStation measurementStation;
+
     public SQRelationAccess() {
     }
 
@@ -80,5 +84,30 @@
         }
         return method;
     }
+
+    public String getMeasurementStationName() {
+        MeasurementStation station = getMeasurementStation();
+        return station == null ? null : station.getName();
+    }
+
+    public String getMeasurementStationGaugeName() {
+        MeasurementStation station = getMeasurementStation();
+        return station == null ? null : station.getGaugeName();
+    }
+
+    public MeasurementStation getMeasurementStation() {
+        if (measurementStation != null) {
+            return measurementStation;
+        }
+        List<MeasurementStation> candidates = MeasurementStation.getStationsAtKM(
+                getRiver(), getLocation());
+        if (candidates != null) {
+            // Just take the first one as we only use the name
+            // and that "should" be unique at the location
+            measurementStation = candidates.get(0);
+        }
+
+        return measurementStation;
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContext.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/context/RiverContextFactory.java	Fri Sep 27 17:36:50 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.
@@ -93,6 +106,8 @@
 
     private static final String XPATH_DGM_PATH = "/artifact-database/options/dgm-path/text()";
 
+    private static GlobalContext GLOBAL_CONTEXT_INSTANCE;
+
 
     /**
      * Creates a new D4EArtifactContext object and initialize all
@@ -115,9 +130,17 @@
         configureZoomScales(config, context);
         configureDGMPath(config, context);
 
+        synchronized (RiverContextFactory.class) {
+            GLOBAL_CONTEXT_INSTANCE = context;
+        }
+
         return context;
     }
 
+    public static synchronized GlobalContext getGlobalContext() {
+        return GLOBAL_CONTEXT_INSTANCE;
+    }
+
 
     private void configureDGMPath(Document config, RiverContext context) {
         String dgmPath = (String) XMLUtils.xpath(
@@ -297,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,
@@ -313,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/datacage/Datacage.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Datacage.java	Fri Sep 27 17:36:50 2013 +0200
@@ -896,8 +896,8 @@
             @Override
             public boolean doIt() throws SQLException {
                 prepareStatement(SQL_UPDATE_ARTIFACT_STATE);
-                stmnt.setInt(1, artifactId);
-                stmnt.setString(2, artifact.getCurrentStateId());
+                stmnt.setString(1, artifact.getCurrentStateId());
+                stmnt.setInt(2, artifactId);
                 stmnt.execute();
                 conn.commit();
                 return true;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Recommendations.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/Recommendations.java	Fri Sep 27 17:36:50 2013 +0200
@@ -142,6 +142,7 @@
     ) {
         parameters.put("CURRENT-STATE-ID", artifact.getCurrentStateId());
         parameters.put("ARTIFACT-ID",      artifact.identifier());
+        parameters.put("ARTIFACT-NAME",    artifact.getName());
 
         for (StateData sd: artifact.getAllData()) {
             Object value = sd.getValue();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/datacage/templating/FunctionResolver.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,10 +10,12 @@
 
 import java.text.SimpleDateFormat;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.HashMap;
+import java.util.Set;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -25,6 +27,10 @@
 import javax.xml.xpath.XPathFunctionResolver;
 
 import org.apache.log4j.Logger;
+import org.dive4elements.artifactdatabase.transition.TransitionEngine;
+import org.dive4elements.artifacts.GlobalContext;
+import org.dive4elements.river.artifacts.context.RiverContext;
+import org.dive4elements.river.artifacts.context.RiverContextFactory;
 
 
 /** Resolves functions (e.g. dc:contains) in Datacage/Meta-Data system. */
@@ -151,6 +157,19 @@
                 return StackFrames.NULL;
             }
         });
+
+        addFunction("all-state-successors", 2, new XPathFunction() {
+            @Override
+            public Object evaluate(List args) throws XPathFunctionException {
+                Object artifactName  = args.get(0);
+                Object stateId       = args.get(1);
+
+                return artifactName instanceof String
+                    && stateId      instanceof String 
+                    ? allStateSuccessors((String)artifactName, (String)stateId)
+                    : Collections.<String>emptySet();
+            }
+        });
     }
 
     /**
@@ -378,5 +397,18 @@
         }
         return "";
     }
+
+    public Set<String> allStateSuccessors(String artifactName, String stateId) {
+        GlobalContext gc = RiverContextFactory.getGlobalContext();
+        if (gc == null) {
+            return Collections.<String>emptySet();
+        }
+        Object o = gc.get(RiverContext.TRANSITION_ENGINE_KEY);
+        if (o instanceof TransitionEngine) {
+            TransitionEngine te = (TransitionEngine)o;
+            return te.allRecursiveSuccessorStateIds(artifactName, stateId);
+        }
+        return Collections.<String>emptySet();
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/math/MovingAverage.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/math/MovingAverage.java	Fri Sep 27 17:36:50 2013 +0200
@@ -37,7 +37,11 @@
         return new double [][] { xs, ys };
     }
 
-    public static double[][] weighted(double[][] values, double radius) {
+    /** Build moving average over values. Weight them. */
+    public static double[][] weighted(
+        double[][] values,
+        double radius
+    ) {
         TreeMap<Double, Double> map = toMap(values);
         int N = map.size();
         double [] xs = new double[N];
@@ -62,6 +66,7 @@
         return new double [][] { xs, ys };
     }
 
+    /** From [x1,x2][y1,y2] makes {x1:y1,x2:y2}. Sorted by x! */
     private static TreeMap<Double, Double> toMap(double[][] values) {
         TreeMap<Double, Double> map = new TreeMap<Double, Double>();
         double [] xs = values[0];
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/Calculation6.java	Fri Sep 27 17:36:50 2013 +0200
@@ -37,6 +37,7 @@
     private long []   timerange;
     private double [] values;
     private Long      officialGaugeNumber;
+    private String    riverName;
 
 
     public Calculation6(HistoricalDischargeAccess access) {
@@ -45,6 +46,7 @@
         double [] vs = mode != null && mode == EvaluationMode.W
             ? access.getWs()
             : access.getQs();
+        riverName = access.getRiver();
 
         Long officialGaugeNumber = access.getOfficialGaugeNumber();
 
@@ -98,7 +100,8 @@
             return null;
         }
 
-        Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber);
+        Gauge gauge = Gauge.getGaugeByOfficialNumber(officialGaugeNumber,
+                riverName);
         if (gauge == null) {
             // TODO: i18n
             return error("hist.discharge.gauge.not.found");
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/CrossSectionFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -30,7 +30,7 @@
 
     protected final static String CACHE_NAME = "cross_sections";
 
-    // TODO use caching consistently, streamline acces.
+    // TODO use caching consistently, streamline access.
     /**
      * Get CrossSections for an instantiated River.
      *
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FacetTypes.java	Fri Sep 27 17:36:50 2013 +0200
@@ -240,6 +240,7 @@
     String STATIC_WQKMS_W = "other.wqkms.w";
     String STATIC_WQKMS_Q = "other.wqkms.q";
     String STATIC_WKMS_INTERPOL = "other.wkms.interpol";
+    String STATIC_W_INTERPOL = "other.w.interpol";
 
     String HEIGHTMARKS_POINTS = "heightmarks_points";
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/FixingsOverview.java	Fri Sep 27 17:36:50 2013 +0200
@@ -28,8 +28,10 @@
 import org.w3c.dom.Document;
 import org.w3c.dom.Element;
 
+import org.dive4elements.river.utils.BatchLoader;
 
-/** Generate Fixings Table chart. */
+
+/** Generate Fixings Table overview data structure to be stored in cache. */
 public class FixingsOverview
 implements   Serializable
 {
@@ -56,39 +58,156 @@
         "WHERE" +
         "    river_id = :river_id AND kind = 2";
 
-    /** All columns from given wst. */
-    public static final String SQL_FIXING_COLUMNS =
-        "SELECT" +
-        "    wc.id         AS wst_column_id," +
-        "    ti.start_time AS start_time," +
-        "    wc.name       AS name " +
-        "FROM wst_columns wc" +
-        "    JOIN time_intervals ti ON wc.time_interval_id = ti.id " +
-        "WHERE" +
-        "    wc.wst_id = :wst_id " +
-        "ORDER BY position";
+    public static final String SQL_FIXING_COLUMNS_BATCH =
+        "SELECT " +
+            "wc.wst_id     AS wst_id," +
+            "wc.id         AS wst_column_id," +
+            "ti.start_time AS start_time," +
+            "wc.name       AS name " +
+        "FROM wst_columns wc " +
+            "JOIN time_intervals ti ON wc.time_interval_id = ti.id " +
+        "WHERE " +
+            "wc.wst_id IN ($IDS) " +
+        "ORDER BY wc.wst_id, position";
 
-    public static final String SQL_FIXING_COLUMN_Q_RANGES =
-        "SELECT" +
-        "    wqr.q AS q," +
-        "    r.a   AS start_km," +
-        "    r.b   AS stop_km " +
-        "FROM wst_column_q_ranges wcqr" +
-        "    JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id" +
-        "    JOIN ranges       r   ON wqr.range_id        = r.id " +
-        "WHERE" +
-        "    wcqr.wst_column_id = :column_id " +
-        "ORDER BY r.a";
+    public static final String SQL_FIXING_COLUMN_Q_RANGES_BATCH =
+        "SELECT " +
+            "wcqr.wst_column_id AS wst_column_id," +
+            "wqr.q              AS q," +
+            "r.a                AS start_km," +
+            "r.b                AS stop_km " +
+        "FROM wst_column_q_ranges wcqr " +
+            "JOIN wst_q_ranges wqr ON wcqr.wst_q_range_id = wqr.id " +
+            "JOIN ranges       r   ON wqr.range_id        = r.id " +
+        "WHERE " +
+            "wcqr.wst_column_id IN ($IDS) " +
+        "ORDER BY wcqr.wst_column_id, r.a";
 
-    public static final String SQL_FIXING_COLUMN_KM_RANGE =
-        "SELECT" +
-        "    MIN(position) AS start_km," +
-        "    MAX(position) AS stop_km " +
-        "FROM" +
-        "    wst_column_values " +
-        "WHERE" +
-        "    wst_column_id = :column_id";
+    public static final String SQL_FIXING_COLUMN_KM_RANGE_BATCH =
+        "SELECT " +
+            "wst_column_id," +
+            "MIN(position) AS start_km," +
+            "MAX(position) AS stop_km " +
+        "FROM " +
+            "wst_column_values " +
+        "WHERE " +
+            "wst_column_id IN ($IDS) " +
+        "GROUP BY wst_column_id";
 
+    public static final class KMRangeLoader extends BatchLoader<double []> {
+
+        public KMRangeLoader(List<Integer> columns, Session session) {
+            super(columns, session, SQL_FIXING_COLUMN_KM_RANGE_BATCH);
+        }
+
+        @Override
+        protected void fill(SQLQuery query) {
+            query
+                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
+                .addScalar("start_km",      StandardBasicTypes.DOUBLE)
+                .addScalar("stop_km",       StandardBasicTypes.DOUBLE);
+
+            List<Object []> ranges = query.list();
+            for (Object [] r: ranges) {
+                Integer cid = (Integer)r[0];
+                double [] vs = new double [] { (Double)r[1], (Double)r[2] };
+                cache(cid, vs);
+            }
+        }
+    } // class KMRangeLoader
+
+    public static final class ColumnQRangeLoader
+    extends                   BatchLoader<List<double []>>
+    {
+        public ColumnQRangeLoader(List<Integer> columns, Session session) {
+            super(columns, session, SQL_FIXING_COLUMN_Q_RANGES_BATCH);
+        }
+
+        @Override
+        protected void fill(SQLQuery query) {
+            query
+                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
+                .addScalar("q",             StandardBasicTypes.DOUBLE)
+                .addScalar("start_km",      StandardBasicTypes.DOUBLE)
+                .addScalar("stop_km",       StandardBasicTypes.DOUBLE);
+
+            int lastId = Integer.MIN_VALUE;
+            List<double []> column = new ArrayList<double []>();
+
+            List<Object []> ranges = query.list();
+            for (Object [] r: ranges) {
+                int cid = (Integer)r[0];
+
+                if (cid != lastId && !column.isEmpty()) {
+                    cache(lastId, column);
+                    column = new ArrayList<double []>();
+                }
+                column.add(new double [] {
+                    (Double)r[1],
+                    (Double)r[2],
+                    (Double)r[3]
+                });
+
+                lastId = cid;
+            }
+
+            if (!column.isEmpty()) {
+                cache(lastId, column);
+            }
+        }
+    } // class ColumnQRangeLoader
+
+    /** Helper class to store data from batching fixing columns. */
+    private static final class FixColumn {
+        int    columnId;
+        Date   startTime;
+        String name;
+
+        FixColumn(int columnId, Date startTime, String name) {
+            this.columnId  = columnId;
+            this.startTime = startTime;
+            this.name      = name;
+        }
+    } // class FixColumn
+
+    public static final class FixColumnLoader
+    extends                   BatchLoader<List<FixColumn>>
+    {
+        public FixColumnLoader(List<Integer> columns, Session session) {
+            super(columns, session, SQL_FIXING_COLUMNS_BATCH);
+        }
+
+        @Override
+        protected void fill(SQLQuery query) {
+            query
+                .addScalar("wst_id",        StandardBasicTypes.INTEGER)
+                .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
+                .addScalar("start_time",    StandardBasicTypes.TIMESTAMP)
+                .addScalar("name",          StandardBasicTypes.STRING);
+
+            int lastId = Integer.MIN_VALUE;
+            List<FixColumn> cols = new ArrayList<FixColumn>();
+
+            List<Object []> columns = query.list();
+            for (Object [] c: columns) {
+                int wid = (Integer)c[0];
+
+                if (wid != lastId && !cols.isEmpty()) {
+                    cache(lastId, cols);
+                    cols = new ArrayList<FixColumn>();
+                }
+                cols.add(new FixColumn(
+                    (Integer)c[1],
+                    (Date)   c[2],
+                    (String) c[3]));
+
+                lastId = wid;
+            }
+            if (!cols.isEmpty()) {
+                cache(lastId, cols);
+            }
+        }
+    } // class FixColumnLoader
 
     public static class QRange extends Range {
 
@@ -255,34 +374,35 @@
                 } // for all Q ranges
             }
 
-            public void loadKmRange(SQLQuery query) {
-                query.setInteger("column_id", columnId);
-
-                List<Object []> kms = query.list();
+            public void loadKmRange(KMRangeLoader loader) {
 
-                if (kms.isEmpty()) {
+                double [] range = loader.get(columnId);
+
+                if (range == null) {
                     log.warn("No km range for column " + columnId + ".");
+                    return;
                 }
-                else {
-                    Object [] obj = kms.get(0);
-                    start = (Double)obj[0];
-                    end   = (Double)obj[1];
-                }
+                start = range[0];
+                end   = range[1];
             }
 
             public void loadQRanges(
-                SQLQuery    query,
-                GaugeFinder gaugeFinder
+                ColumnQRangeLoader loader,
+                GaugeFinder        gaugeFinder
             ) {
-                query.setInteger("column_id", columnId);
-                List<Object []> list = query.list();
+                List<double []> qrs = loader.get(columnId);
+                if (qrs == null) {
+                    log.warn("No q ranges found for column " + columnId);
+                    return;
+                }
 
-                List<QRange> qRanges = new ArrayList<QRange>(list.size());
+                List<QRange> qRanges = new ArrayList<QRange>(qrs.size());
 
-                for (Object [] row: list) {
-                    double q     = (Double)row[0];
-                    double start = (Double)row[1];
-                    double end   = (Double)row[2];
+                for (double [] qr: qrs) {
+                    double q     = qr[0];
+                    double start = qr[1];
+                    double end   = qr[2];
+
                     QRange qRange = new QRange(start, end, q);
                     if (qRange.clip(this)) {
                         qRanges.add(qRange);
@@ -314,20 +434,26 @@
             columns = new ArrayList<Column>();
         }
 
-        public void loadColumns(SQLQuery query) {
-            query.setInteger("wst_id", wstId);
-            List<Object []> list = query.list();
-            for (Object [] row: list) {
-                int    columnId  = (Integer)row[0];
-                Date   startTime = (Date)   row[1];
-                String name      = (String) row[2];
-                columns.add(new Column(columnId, startTime, name));
+        public void allColumnIds(List<Integer> cIds) {
+            for (Column column: columns) {
+                cIds.add(column.columnId);
             }
         }
 
-        public void loadColumnsKmRange(SQLQuery query) {
+        public void loadColumns(FixColumnLoader loader) {
+            List<FixColumn> fcs = loader.get(wstId);
+            if (fcs == null) {
+                log.warn("No columns for wst " + wstId);
+                return;
+            }
+            for (FixColumn fc: fcs) {
+                columns.add(new Column(fc.columnId, fc.startTime, fc.name));
+            }
+        }
+
+        public void loadColumnsKmRange(KMRangeLoader loader) {
             for (Column column: columns) {
-                column.loadKmRange(query);
+                column.loadKmRange(loader);
             }
         }
 
@@ -338,11 +464,11 @@
         }
 
         public void loadColumnsQRanges(
-            SQLQuery    query,
-            GaugeFinder gaugeFinder
+            ColumnQRangeLoader loader,
+            GaugeFinder        gaugeFinder
         ) {
             for (Column column: columns) {
-                column.loadQRanges(query, gaugeFinder);
+                column.loadQRanges(loader, gaugeFinder);
             }
         }
 
@@ -427,23 +553,40 @@
     }
 
     protected void loadFixingsColumns(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMNS)
-            .addScalar("wst_column_id", StandardBasicTypes.INTEGER)
-            .addScalar("start_time",    StandardBasicTypes.DATE)
-            .addScalar("name",          StandardBasicTypes.STRING);
+
+        FixColumnLoader loader = new FixColumnLoader(
+            allFixingIds(),
+            session);
 
         for (Fixing fixing: fixings) {
-            fixing.loadColumns(query);
+            fixing.loadColumns(loader);
         }
     }
 
+    protected List<Integer> allFixingIds() {
+        List<Integer> ids = new ArrayList<Integer>(fixings.size());
+        for (Fixing fixing: fixings) {
+            ids.add(fixing.getId());
+        }
+        return ids;
+    }
+
+    protected List<Integer> allColumnIds() {
+        List<Integer> cIds = new ArrayList<Integer>();
+        for (Fixing fixing: fixings) {
+            fixing.allColumnIds(cIds);
+        }
+        return cIds;
+    }
+
     protected void loadFixingsColumnsKmRange(Session session) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_KM_RANGE)
-            .addScalar("start_km", StandardBasicTypes.DOUBLE)
-            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
+
+        KMRangeLoader loader = new KMRangeLoader(
+            allColumnIds(),
+            session);
 
         for (Fixing fixing: fixings) {
-            fixing.loadColumnsKmRange(query);
+            fixing.loadColumnsKmRange(loader);
         }
     }
 
@@ -451,13 +594,13 @@
         Session     session,
         GaugeFinder gaugeFinder
     ) {
-        SQLQuery query = session.createSQLQuery(SQL_FIXING_COLUMN_Q_RANGES)
-            .addScalar("q",        StandardBasicTypes.DOUBLE)
-            .addScalar("start_km", StandardBasicTypes.DOUBLE)
-            .addScalar("stop_km",  StandardBasicTypes.DOUBLE);
+
+        ColumnQRangeLoader loader = new ColumnQRangeLoader(
+            allColumnIds(),
+            session);
 
         for (Fixing fixing: fixings) {
-            fixing.loadColumnsQRanges(query, gaugeFinder);
+            fixing.loadColumnsQRanges(loader, gaugeFinder);
         }
     }
 
@@ -627,14 +770,21 @@
         protected Date end;
 
         public DateRangeFilter(Date start, Date end) {
-            this.start = start;
-            this.end   = end;
+            if (start.before(end)) {
+                this.start = start;
+                this.end   = end;
+            }
+            else {
+                this.start = end;
+                this.end   = start;
+            }
         }
 
         @Override
         public boolean accept(Fixing.Column column) {
             Date date = column.getStartTime();
-            return start.compareTo(date) <= 0 && end.compareTo(date) >= 0;
+            // start <= date <= end
+            return !(date.before(start) || date.after(end));
         }
     } // class DateRangeFilter
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/ManagedFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,7 +16,7 @@
 import org.dive4elements.artifactdatabase.state.Facet;
 import org.dive4elements.artifacts.ArtifactNamespaceContext;
 import org.dive4elements.artifacts.common.utils.XMLUtils.ElementCreator;
-
+import org.dive4elements.river.utils.CompareUtil;
 
 /**
  * Facet with user-supplied theme-control-information (pos in list,
@@ -140,5 +140,24 @@
             return 0;
         }
     }
+
+    /**
+     * Returns true if the other is likely the same facet.
+     * This happens if a facet is defined for two outs.
+     */
+    public boolean isSame(Object other) {
+        if (!(other instanceof ManagedFacet)) {
+            return false;
+        }
+        ManagedFacet otherFacet = (ManagedFacet) other;
+        return  this.getVisible() == otherFacet.getVisible() &&
+                this.getActive() == otherFacet.getActive() &&
+                CompareUtil.areSame(this.getArtifact(), otherFacet.getArtifact()) &&
+                this.getIndex() == otherFacet.getIndex() &&
+                CompareUtil.areSame(this.getName(), otherFacet.getName()) &&
+                CompareUtil.areSame(this.getBoundToOut(), otherFacet.getBoundToOut()) &&
+                CompareUtil.areSame(this.getDescription(), otherFacet.getDescription());
+        // Missing properties are blackboard, data, position.
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/OfficialLineFinder.java	Fri Sep 27 17:36:50 2013 +0200
@@ -274,6 +274,18 @@
         return list;
     }
 
+    private static List<ValueRange> filterByQValues(double[] values, List<ValueRange> ranges) {
+        List<ValueRange> list = new ArrayList<ValueRange>(ranges.size());
+        for (ValueRange r: ranges) {
+            for (double val: values) {
+                if (r.sameValue(val) && !list.contains(r)) {
+                    list.add(r);
+                }
+            }
+        }
+        return list;
+    }
+
     private static boolean isQ(D4EArtifact artifact) {
         Boolean b = artifact.getDataAsBoolean("wq_isq");
         return b != null && b;
@@ -286,25 +298,23 @@
 
     public static final Range Q_OUT_OF_RANGE = new Range(-10000, -9999);
 
-    private static Range singleQs(D4EArtifact artifact) {
+    private static double[] singleQs(D4EArtifact artifact) {
         String singleData = nn(artifact.getDataAsString("wq_single"));
-        double min =  Double.MAX_VALUE;
-        double max = -Double.MAX_VALUE;
+        String[] values = singleData.split(" ");
+        double[] ret = new double[values.length];
+        int i = 0;
 
-        for (String value: singleData.split(" ")) {
+        for (String value: values) {
             try {
-                double x = Double.parseDouble(value);
-                if (x < min) min = x;
-                if (x > max) max = x;
+                ret[i] = Double.parseDouble(value);
             }
             catch (NumberFormatException nfe) {
+                ret[i] = -1; // INVALID_Q_VALUE
             }
+            i++;
         }
 
-        return min == Double.MAX_VALUE
-            ? Q_OUT_OF_RANGE
-            : new Range(min, max);
-
+        return ret;
     }
 
     private static Range qRange(D4EArtifact artifact) {
@@ -383,21 +393,20 @@
             return Collections.<ValueRange>emptyList();
         }
 
-        Range qRange = isRange(artifact)
-            ? qRange(artifact)
-            : singleQs(artifact);
-
-        if (qRange == Q_OUT_OF_RANGE) {
-            qRange = tripleQRange(artifact);
+        if (isRange(artifact)) {
+            Range qRange = qRange(artifact);
+            if (qRange == Q_OUT_OF_RANGE) {
+                qRange = tripleQRange(artifact);
+            }
+            ranges = filterByQRange(qRange, ranges);
+            if (debug) {
+                log.debug("Q range filter: " + qRange);
+            }
+        } else {
+            ranges = filterByQValues(singleQs(artifact), ranges);
         }
 
         if (debug) {
-            log.debug("Q range filter: " + qRange);
-        }
-
-        ranges = filterByQRange(qRange, ranges);
-
-        if (debug) {
             log.debug("After q range filter: " + ranges);
         }
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/QRangeTree.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/QRangeTree.java	Fri Sep 27 17:36:50 2013 +0200
@@ -84,6 +84,7 @@
             return m*pos + n;
         }
 
+        /** @param pos the station (km). */
         public double findQ(double pos) {
 
             Node current = this;
@@ -297,6 +298,7 @@
         return max;
     }
 
+    /** @param pos the station (km). */
     public double findQ(double pos) {
         return root != null ? root.findQ(pos) : Double.NaN;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/W.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKms.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WKmsImpl.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WQKms.java	Fri Sep 27 17:36:50 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/model/WstValueTable.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/WstValueTable.java	Fri Sep 27 17:36:50 2013 +0200
@@ -265,6 +265,7 @@
             }
 
             double [] splineQs = new double[W];
+            double [] splineWs = new double[W];
 
             for (int i = 0; i < W; ++i) {
                 double sq = table.getQIndex(i, km);
@@ -273,12 +274,15 @@
                         km, "no.q.found.in.column", (i+1));
                 }
                 splineQs[i] = sq;
+                splineWs[i] = ws[i];
             }
 
+            DoubleUtil.sortByFirst(splineQs, splineWs);
+
             try {
                 SplineInterpolator interpolator = new SplineInterpolator();
                 PolynomialSplineFunction spline =
-                    interpolator.interpolate(splineQs, ws);
+                    interpolator.interpolate(splineQs, splineWs);
 
                 return new SplineFunction(spline, splineQs, ws);
             }
@@ -973,7 +977,7 @@
 
         for (int col = 0; col < columns.length; col++) {
             qs[col] = columns[col].getQRangeTree().findQ(km);
-            if (startRow.km == km && startRow.ws[col] != Double.NaN) {
+            if (startRow.km == km && !Double.isNaN(startRow.ws[col])) {
                 // Great. W is defined at km.
                 ws[col] = startRow.ws[col];
                 continue;
@@ -1275,6 +1279,27 @@
     }
 
 
+    /** True if no QRange is given or Q equals zero. */
+    public boolean hasEmptyQ() {
+        for (Column column: columns) {
+            if (column.getQRangeTree() == null) {
+                return true;
+            }
+            else {
+                if (Math.abs(column.getQRangeTree().maxQ()) <= 0.01d) {
+                    return true;
+                }
+            }
+        }
+
+        if (columns.length == 0) {
+            log.warn("No columns in WstValueTable.");
+        }
+
+        return false;
+    }
+
+
     /** Find ranges that are between km1 and km2 (inclusive?) */
     public List<Range> findSegments(double km1, double km2) {
         return columns.length != 0
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/ZoomScale.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/ZoomScale.java	Fri Sep 27 17:36:50 2013 +0200
@@ -18,6 +18,7 @@
 import org.dive4elements.river.artifacts.math.Linear;
 
 
+/** Has to do with adaptive smoothing based on current diagram extent. */
 public class ZoomScale
 {
     private static Logger logger = Logger.getLogger(ZoomScale.class);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/DateUniqueMaker.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,41 @@
+package org.dive4elements.river.artifacts.model.fixings;
+
+import java.util.Date;
+
+import gnu.trove.TIntObjectHashMap;
+import gnu.trove.TLongHashSet;
+
+public class DateUniqueMaker {
+
+    private TLongHashSet      times;
+    private TIntObjectHashMap already;
+
+    public DateUniqueMaker() {
+        times   = new TLongHashSet();
+        already = new TIntObjectHashMap();
+    }
+
+    public <T extends QWI> void makeUnique(T t) {
+
+        // Map same index to same new value
+        if (already.containsKey(t.index)) {
+            t.date = (Date)already.get(t.index);
+            return;
+        }
+        long time = t.date.getTime();
+        if (!times.add(time)) { // same found before
+            do {
+                time += 30L*1000L; // Add 30secs
+            }
+            while (!times.add(time));
+            Date newDate = new Date(time);
+            already.put(t.index, newDate);
+            // Write back modified time.
+            t.date = newDate;
+        }
+        else {
+            // register as seen.
+            already.put(t.index, t.date);
+        }
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/Fitting.java	Fri Sep 27 17:36:50 2013 +0200
@@ -121,8 +121,9 @@
         double maxQ = -Double.MAX_VALUE;
         if (referenced != null) {
             for (QWI qw: referenced) {
-                if (qw.getQ() > maxQ) {
-                    maxQ = qw.getQ();
+                double q = qw.getQ();
+                if (q > maxQ) {
+                    maxQ = q;
                 }
             }
         }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisCalculation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -101,6 +101,15 @@
             fitResult.getOutliers(),
             analysisPeriods);
 
+        // Workaraound to deal with same dates in data set
+        far.makeReferenceEventsDatesUnique();
+        far.remapReferenceIndicesToRank();
+
+        far.makeAnalysisEventsUnique();
+        for (int i = 0; i < this.analysisPeriods.length; ++i) {
+            far.remapAnalysisEventsIndicesToRank(i);
+        }
+
         return new CalculationResult(far, this);
     }
 
@@ -117,7 +126,7 @@
         Function        function,
         Parameters      parameters,
         FixingsOverview overview,
-        ColumnCache cc
+        ColumnCache     cc
     ) {
         Range range = new Range(from, to);
 
@@ -141,15 +150,28 @@
         TIntIntHashMap [] col2indices =
             new TIntIntHashMap[analysisPeriods.length];
 
+        DateRangeFilter [] drfs = new DateRangeFilter[analysisPeriods.length];
+
+        boolean debug = log.isDebugEnabled();
+
         for (int i = 0; i < analysisPeriods.length; ++i) {
             col2indices[i] = new TIntIntHashMap();
+            drfs[i] = new DateRangeFilter(
+                analysisPeriods[i].getFrom(),
+                analysisPeriods[i].getTo());
+
+            if (debug) {
+                log.debug("Analysis period " + (i+1) + " date range: " +
+                    analysisPeriods[i].getFrom() + " - " +
+                    analysisPeriods[i].getTo());
+            }
         }
 
         for (int row = 0, R = parameters.size(); row < R; ++row) {
             double km = parameters.get(row, kmIndex);
             parameters.get(row, parameterIndices, parameterValues);
 
-            // This is the paraterized function for a given km.
+            // This is the parameterized function for a given km.
             org.dive4elements.river.artifacts.math.Function instance =
                 function.instantiate(parameterValues);
 
@@ -162,9 +184,7 @@
                 DateRange analysisPeriod = analysisPeriods[ap];
                 TIntIntHashMap col2index = col2indices[ap];
 
-                DateRangeFilter drf = new DateRangeFilter(
-                    analysisPeriod.getFrom(),
-                    analysisPeriod.getTo());
+                DateRangeFilter drf = drfs[ap];
 
                 QWD []    qSectorAverages = new QWD[4];
                 double [] qSectorStdDevs  = new double[4];
@@ -282,8 +302,9 @@
                 parameters.set(row, maxQIndex, maxQ);
             }
 
-            results.add(km, periodResults.toArray(
-                new AnalysisPeriod[periodResults.size()]));
+            AnalysisPeriod [] rap = new AnalysisPeriod[periodResults.size()];
+            periodResults.toArray(rap);
+            results.add(km, rap);
         }
 
         return results;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisEventsFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -64,48 +64,47 @@
     public Object getData(Artifact artifact, CallContext context) {
         logger.debug("FixAnalysisEventsFacet.getData");
 
-        if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixAnalysisResult result = (FixAnalysisResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
-            KMIndex.Entry<AnalysisPeriod []> kmPeriodsEntry =
-                kmPeriods.binarySearch(currentKm);
-
-            if(kmPeriodsEntry == null) {
-                logger.debug("getData: kmPeriodsEntry == null");
-                return null;
-            }
-
-            AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
-            if (periods == null) {
-                logger.debug("getData: periods == null");
-                return null;
-            }
-            int ndx = index >> 8;
-            QWD[] qwdData = periods[ndx].getQWDs();
-            if (qwdData == null) {
-                return null;
-            }
-            int ndy = index & 255;
-            for (int i = 0; i < qwdData.length; i++) {
-                if (qwdData[i].getIndex() == ndy) {
-                    return qwdData[i];
-                }
-            }
-            return null;
-        }
-        else {
+        if (!(artifact instanceof D4EArtifact)) {
             logger.debug("Not an instance of FixationArtifact.");
             return null;
         }
+        D4EArtifact flys = (D4EArtifact)artifact;
+
+        CalculationResult res =
+            (CalculationResult) flys.compute(context,
+                                             ComputeType.ADVANCE,
+                                             false);
+
+        FixAnalysisResult result = (FixAnalysisResult) res.getData();
+        double currentKm = getCurrentKm(context);
+
+        KMIndex<AnalysisPeriod []> kmPeriods = result.getAnalysisPeriods();
+        KMIndex.Entry<AnalysisPeriod []> kmPeriodsEntry =
+            kmPeriods.binarySearch(currentKm);
+
+        if (kmPeriodsEntry == null) {
+            logger.debug("getData: kmPeriodsEntry == null");
+            return null;
+        }
+
+        AnalysisPeriod[] periods = kmPeriodsEntry.getValue();
+        if (periods == null) {
+            logger.debug("getData: periods == null");
+            return null;
+        }
+        int ndx = index >> 8;
+        QWD[] qwdData = periods[ndx].getQWDs();
+        if (qwdData == null) {
+            return null;
+        }
+        int ndy = index & 255;
+
+        for (QWD qwd: qwdData) {
+            if (qwd.getIndex() == ndy) {
+                return qwd;
+            }
+        }
+        return null;
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixAnalysisResult.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,10 +8,14 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import gnu.trove.TIntObjectHashMap;
+
 import java.util.Collection;
 import java.util.Date;
+import java.util.TreeMap;
 import java.util.TreeSet;
 
+import org.apache.log4j.Logger;
 import org.dive4elements.river.artifacts.model.Parameters;
 
 import org.dive4elements.river.utils.KMIndex;
@@ -19,6 +23,9 @@
 public class FixAnalysisResult
 extends      FixResult
 {
+    private static Logger log =
+        Logger.getLogger(FixAnalysisResult.class);
+
     protected KMIndex<AnalysisPeriod []> analysisPeriods;
 
     public FixAnalysisResult() {
@@ -49,31 +56,70 @@
         return result;
     }
 
-    public Collection<Date> getReferenceEventsDates() {
-        TreeSet<Date> dates = new TreeSet<Date>();
-        for (KMIndex.Entry<QWD []> entry: referenced) {
-            QWD [] values = entry.getValue();
-            for (int i = 0; i < values.length; i++) {
-                dates.add(values[i].date);
+
+    public void makeAnalysisEventsUnique() {
+        TIntObjectHashMap dums = new TIntObjectHashMap();
+
+        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
+            AnalysisPeriod [] aps = entry.getValue();
+            for (int i = 0; i < aps.length; ++i) {
+                AnalysisPeriod ap = aps[i];
+                QWD [] qwds = ap.getQWDs();
+                if (qwds == null) {
+                    continue;
+                }
+                DateUniqueMaker dum = (DateUniqueMaker)dums.get(i);
+                if (dum == null) {
+                    dums.put(i, dum = new DateUniqueMaker());
+                }
+                for (QWD qwd: qwds) {
+                    dum.makeUnique(qwd);
+                }
             }
         }
-        return dates;
     }
 
     public Collection<Date> getAnalysisEventsDates(int analysisPeriod) {
         TreeSet<Date> dates = new TreeSet<Date>();
         for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
             QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
-            if (qwds == null) {
-                continue;
-            }
-            for (int i = 0; i < qwds.length; i++) {
-                dates.add(qwds[i].date);
+            if (qwds != null) {
+                for (QWD qwd: qwds) {
+                    dates.add(qwd.date);
+                }
             }
         }
         return dates;
     }
 
+    public Collection<Integer> getAnalysisEventsIndices(int analysisPeriod) {
+        TreeMap<Date, Integer> dates = new TreeMap<Date, Integer>();
+        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
+            QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
+            if (qwds != null) {
+                for (QWD qwd: qwds) {
+                    dates.put(qwd.date, qwd.index);
+                }
+            }
+        }
+        return dates.values();
+    }
+
+    public void remapAnalysisEventsIndicesToRank(int analysisPeriod) {
+        RankRemapper remapper = new RankRemapper();
+        for (Integer index: getAnalysisEventsIndices(analysisPeriod)) {
+            remapper.toMap(index);
+        }
+        for (KMIndex.Entry<AnalysisPeriod []> entry: analysisPeriods) {
+            QWD [] qwds = entry.getValue()[analysisPeriod].getQWDs();
+            if (qwds != null) {
+                for (QWD qwd: qwds) {
+                    remapper.remap(qwd);
+                }
+            }
+        }
+    }
+
     public KMIndex<AnalysisPeriod []> getAnalysisPeriods() {
         return analysisPeriods;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixCalculation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -114,6 +114,10 @@
             return index;
         }
 
+        public int getId() {
+            return meta.getId();
+        }
+
         public boolean getQW(
             double    km,
             double [] qs,
@@ -313,7 +317,7 @@
                             column.getDate(),
                             interpolated[i],
                             0d,
-                            column.getIndex());
+                            column.getId()); // Use database id here
                     }
                 }
                 log.warn("cannot find column for (" + q + ", " + w + ")");
@@ -364,7 +368,9 @@
                 continue;
             }
 
-            referenced.add(km, fitting.referencedToArray());
+            QWD [] refs = fitting.referencedToArray();
+
+            referenced.add(km, refs);
 
             if (fitting.hasOutliers()) {
                 outliers.add(km, fitting.outliersToArray());
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixReferenceEventsFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -61,38 +61,36 @@
     public Object getData(Artifact artifact, CallContext context) {
         logger.debug("FixReferenceEventsFacet.getData");
 
-        if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixResult result = (FixResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            logger.debug("current km in FRE: " + currentKm);
-
-            KMIndex<QWD []> kmQWs = result.getReferenced();
-            KMIndex.Entry<QWD []> kmQWsEntry = kmQWs.binarySearch(currentKm);
-            QWD[] qwds = null;
-            if (kmQWsEntry != null) {
-                int ndx = index & 255;
-                qwds = kmQWsEntry.getValue();
-                for (int i = 0; i < qwds.length; i++) {
-                    if (qwds[i].getIndex() == ndx) {
-                        return qwds[i];
-                    }
-                }
-                return null;
-            }
-            return null;
-        }
-        else {
+        if (!(artifact instanceof D4EArtifact)) {
             logger.debug("Not an instance of FixationArtifact.");
             return null;
         }
+
+        D4EArtifact flys = (D4EArtifact)artifact;
+
+        CalculationResult res =
+            (CalculationResult) flys.compute(context,
+                                             ComputeType.ADVANCE,
+                                             false);
+
+        FixResult result = (FixResult) res.getData();
+        double currentKm = getCurrentKm(context);
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("current km in FRE: " + currentKm);
+        }
+
+        KMIndex<QWD []> kmQWs = result.getReferenced();
+        KMIndex.Entry<QWD []> kmQWsEntry = kmQWs.binarySearch(currentKm);
+        if (kmQWsEntry != null) {
+            int ndx = index & 255;
+            for (QWD qwd: kmQWsEntry.getValue()) {
+                if (qwd.getIndex() == ndx) {
+                    return qwd;
+                }
+            }
+        }
+        return null;
     }
 
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixResult.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,15 +8,23 @@
 
 package org.dive4elements.river.artifacts.model.fixings;
 
+import org.apache.log4j.Logger;
 import org.dive4elements.river.artifacts.model.Parameters;
 
 import org.dive4elements.river.utils.KMIndex;
 
 import java.io.Serializable;
+import java.util.Collection;
+import java.util.Date;
+import java.util.TreeMap;
+import java.util.TreeSet;
 
 public class FixResult
 implements   Serializable
 {
+    private static Logger log =
+        Logger.getLogger(FixResult.class);
+
     protected Parameters      parameters;
     protected KMIndex<QWD []> referenced;
     protected KMIndex<QWI []> outliers;
@@ -42,6 +50,48 @@
         this.referenced = referenced;
     }
 
+    public void makeReferenceEventsDatesUnique() {
+        DateUniqueMaker dum = new DateUniqueMaker();
+        for (KMIndex.Entry<QWD []> entry: referenced) {
+            for (QWD ref: entry.getValue()) {
+                dum.makeUnique(ref);
+            }
+        }
+    }
+
+    public Collection<Integer> getReferenceEventsIndices() {
+        TreeMap<Date, Integer> dates = new TreeMap<Date, Integer>();
+        for (KMIndex.Entry<QWD []> entry: referenced) {
+            for (QWD value: entry.getValue()) {
+                dates.put(value.date, value.index);
+            }
+        }
+        return dates.values();
+    }
+
+    public void remapReferenceIndicesToRank() {
+        RankRemapper remapper = new RankRemapper();
+        for (Integer idx: getReferenceEventsIndices()) {
+            remapper.toMap(idx);
+        }
+        for (KMIndex.Entry<QWD []> entry: referenced) {
+            for (QWD value: entry.getValue()) {
+                remapper.remap(value);
+            }
+        }
+    }
+
+    public Collection<Date> getReferenceEventsDates() {
+        TreeSet<Date> dates = new TreeSet<Date>();
+        for (KMIndex.Entry<QWD []> entry: referenced) {
+            for (QWD qwd: entry.getValue()) {
+                dates.add(qwd.date);
+            }
+        }
+        return dates;
+    }
+
+
     public KMIndex<QWI []> getOutliers() {
         return outliers;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWQCurveFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/FixWQCurveFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -66,57 +66,57 @@
      */
     @Override
     public Object getData(Artifact artifact, CallContext context) {
-        logger.debug("getData");
-        if (artifact instanceof D4EArtifact) {
-            D4EArtifact flys = (D4EArtifact)artifact;
-            FixAnalysisAccess access = new FixAnalysisAccess(flys);
-
-            CalculationResult res =
-                (CalculationResult) flys.compute(context,
-                                                 ComputeType.ADVANCE,
-                                                 false);
-
-            FixResult result = (FixResult) res.getData();
-            double currentKm = getCurrentKm(context);
-
-            logger.debug("getData: km = " + currentKm);
-
-            String function = access.getFunction();
-            Function ff = FunctionFactory.getInstance().getFunction(function);
-
-            if (ff == null) {
-                logger.warn("getData: ff == null");
-                return null;
-            }
 
-            Parameters params = result.getParameters();
-            String[] paramNames = ff.getParameterNames();
-
-            double [] coeffs = params.interpolate("km", currentKm, paramNames);
-
-            if (coeffs == null) {
-                logger.warn("getData: coeffs == null");
-                return null;
-            }
-
-            org.dive4elements.river.artifacts.math.Function mf =
-                ff.instantiate(coeffs);
-
-            double maxQ = FixFacetUtils.getMaxQ(params, currentKm);
-            logger.debug("getData: maxQ = " + maxQ);
-
-            FixFunction fix = new FixFunction(
-                ff.getName(),
-                ff.getDescription(),
-                mf,
-                maxQ);
-
-            return fix;
-        }
-        else {
+        logger.debug("getData");
+        if (!(artifact instanceof D4EArtifact)) {
             logger.debug("Not an instance of D4EArtifact / FixationArtifact.");
             return null;
         }
+
+        D4EArtifact flys = (D4EArtifact)artifact;
+        FixAnalysisAccess access = new FixAnalysisAccess(flys);
+
+        CalculationResult res =
+            (CalculationResult) flys.compute(context,
+                                             ComputeType.ADVANCE,
+                                             false);
+
+        FixResult result = (FixResult) res.getData();
+        double currentKm = getCurrentKm(context);
+
+        logger.debug("getData: km = " + currentKm);
+
+        String function = access.getFunction();
+        Function ff = FunctionFactory.getInstance().getFunction(function);
+
+        if (ff == null) {
+            logger.warn("getData: ff == null");
+            return null;
+        }
+
+        Parameters params = result.getParameters();
+        String[] paramNames = ff.getParameterNames();
+
+        double [] coeffs = params.interpolate("km", currentKm, paramNames);
+
+        if (coeffs == null) {
+            logger.warn("getData: coeffs == null");
+            return null;
+        }
+
+        org.dive4elements.river.artifacts.math.Function mf =
+            ff.instantiate(coeffs);
+
+        double maxQ = FixFacetUtils.getMaxQ(params, currentKm);
+        logger.debug("getData: maxQ = " + maxQ);
+
+        FixFunction fix = new FixFunction(
+            ff.getName(),
+            ff.getDescription(),
+            mf,
+            maxQ);
+
+        return fix;
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/fixings/RankRemapper.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,44 @@
+/* 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.artifacts.model.fixings;
+
+import java.util.IdentityHashMap;
+
+import org.apache.log4j.Logger;
+
+import gnu.trove.TIntIntHashMap;
+
+public class RankRemapper {
+
+    private static Logger log = Logger.getLogger(RankRemapper.class);
+
+    private TIntIntHashMap                index2rank;
+    private IdentityHashMap<QWI, Boolean> visited;
+
+    public RankRemapper() {
+        index2rank = new TIntIntHashMap();
+        visited    = new IdentityHashMap<QWI, Boolean>();
+    }
+
+    public void toMap(int index) {
+        index2rank.put(index, index2rank.size());
+    }
+
+    public <I extends QWI> void remap(I qwi) {
+        if (!visited.containsKey(qwi)) {
+            int idx = qwi.index;
+            if (index2rank.containsKey(idx)) {
+                qwi.index = index2rank.get(idx);
+            } else if (log.isDebugEnabled()) {
+                log.debug("Cannot remap " + idx);
+            }
+            visited.put(qwi, true);
+        }
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffCalculation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffCalculation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -180,3 +180,4 @@
             s2.getName());
     }
 }
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFilterFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearFilterFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -50,6 +50,7 @@
         Double end = (Double)context.getContextValue("endkm");
         if(start != null && end != null) {
             RiverContext fc = (RiverContext)context.globalContext();
+            // Adaptive smoothing, based on zoom factor/diagram extents.
             ZoomScale scales = (ZoomScale)fc.get("zoomscale");
             RiverAccess access = new RiverAccess((D4EArtifact)artifact);
             String river = access.getRiver();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearResult.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedDiffYearResult.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,10 +11,10 @@
 import gnu.trove.TDoubleArrayList;
 
 
+/** Result of a bed diff year calculation. */
 public class BedDiffYearResult
 extends BedDifferencesResult
 {
-
     protected TDoubleArrayList bedHeights;
     protected TDoubleArrayList dataGap;
     protected TDoubleArrayList morphWidth;
@@ -115,3 +115,4 @@
         };
     }
 }
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/BedHeightFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -24,6 +24,8 @@
 import org.dive4elements.river.artifacts.model.StaticBedHeightCacheKey;
 import org.dive4elements.river.backend.SessionHolder;
 
+
+/** Create BedHeights from database. */
 public class BedHeightFactory {
     /** Private logger to use here. */
     private static Logger log = Logger.getLogger(BedHeightFactory.class);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/FlowVelocityMeasurementFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,12 +8,15 @@
 
 package org.dive4elements.river.artifacts.model.minfo;
 
+import java.util.Date;
 import java.util.List;
 
 import org.apache.log4j.Logger;
 import org.hibernate.SQLQuery;
 import org.hibernate.Session;
 
+import org.hibernate.type.StandardBasicTypes;
+
 import org.dive4elements.river.model.FlowVelocityMeasurementValue;
 import org.dive4elements.river.backend.SessionHolder;
 
@@ -40,7 +43,13 @@
         log.debug("FlowVelocityMeasurementFactory.getFlowVelocityMeasurementValue");
         Session session = SessionHolder.HOLDER.get();
         SQLQuery sqlQuery = null;
-        sqlQuery = session.createSQLQuery(SQL_SELECT_ONE);
+        sqlQuery = session.createSQLQuery(SQL_SELECT_ONE)
+            .addScalar("station", StandardBasicTypes.DOUBLE)
+            .addScalar("datetime", StandardBasicTypes.DATE)
+            .addScalar("w", StandardBasicTypes.DOUBLE)
+            .addScalar("q", StandardBasicTypes.DOUBLE)
+            .addScalar("v", StandardBasicTypes.DOUBLE)
+            .addScalar("description", StandardBasicTypes.STRING);
         sqlQuery.setParameter("id", id);
 
         List<Object []> results = sqlQuery.list();
@@ -50,10 +59,12 @@
                 return null;
             }
             return FlowVelocityMeasurementValue.getUnmapped(
-                Double.parseDouble(row[0].toString()),
-                Double.parseDouble(row[2].toString()),
-                Double.valueOf(row[3].toString()),
-                Double.valueOf(row[4].toString()), null, row[5].toString());
+                (Double) row[0],
+                (Double) row[2],
+                (Double) row[3],
+                (Double) row[4],
+                (Date) row[1],
+                (String) row[5]);
         }
         return null;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurement.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurement.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,8 +11,11 @@
 import java.util.Date;
 import java.util.Map;
 
+import org.apache.log4j.Logger;
 
+/** A measurement of the bed quality, serving different diameter at given km. */
 public class QualityMeasurement {
+    private static Logger logger = Logger.getLogger(QualityMeasurements.class);
 
     private double              km;
     private Date                date;
@@ -20,7 +23,7 @@
     private double              depth2;
     private Map<String, Double> charDiameter;
 
-    public QualityMeasurement() {
+    private QualityMeasurement() {
 
     }
 
@@ -61,8 +64,16 @@
         this.charDiameter = charDiameter;
     }
 
+    /**
+     * Get the stored diameter for given key (e.g. d10).
+     * @return NaN if no data found in this measurement.
+     */
     public double getDiameter(String key) {
-        return charDiameter.get(key);
+        Double diameter = charDiameter.get(key);
+        if (diameter == null) {
+            logger.warn("No Diameter at km " + km + " for " + key);
+        }
+        return (diameter != null) ? diameter : Double.NaN;
     }
 
     public void setDiameter(String key, double value) {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurementFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurementFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -86,14 +86,18 @@
         "      m.datum BETWEEN :start AND :end " +
         "ORDER BY m.km";
 
+    /** Transform query result into objects, use INSTANCE singleton. */
     public static final class QualityMeasurementResultTransformer
     extends BasicTransformerAdapter {
 
-        public static QualityMeasurementResultTransformer INSTANCE = new QualityMeasurementResultTransformer();
+        // Make a singleton
+        public static QualityMeasurementResultTransformer INSTANCE =
+            new QualityMeasurementResultTransformer();
 
-        public QualityMeasurementResultTransformer() {
+        private QualityMeasurementResultTransformer() {
         }
 
+        /** tuples is a row. */
         @Override
         public Object transformTuple(Object[] tuple, String[] aliases) {
             Map<String, Double> map = new HashMap<String, Double>();
@@ -172,6 +176,7 @@
         return new QualityMeasurements(query.list());
     }
 
+    /** Get all measurements. */
     public static QualityMeasurements getBedMeasurements(
         String river,
         double from,
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurements.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/QualityMeasurements.java	Fri Sep 27 17:36:50 2013 +0200
@@ -56,3 +56,4 @@
         this.measurements.add(qm);
     }
 }
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensity.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensity.java	Fri Sep 27 17:36:50 2013 +0200
@@ -65,23 +65,25 @@
         this.years = years;
     }
 
+    /**
+     * Get the density at year.
+     * measured densities are valid until the next measurement.
+     * if no measurement was found 1.8 is returned.
+     */
     public double getDensity(double km, int year) {
         Collections.sort(this.years);
-        if (this.years.size() == 1) {
-            return getDensityAtKm(densities.get(year), km);
+        if (this.years.size() == 1 && years.get(0) <= year) {
+            logger.debug("get density from year " + year + " at km " + km);
+            return getDensityAtKm(densities.get(years.get(0)), km);
         }
-        else {
+        else if (this.years.size() > 1) {
             for (int i = 0; i < years.size() -1; i++) {
                 int y1 = years.get(i);
                 int y2 = years.get(i + 1);
-                int mid = Math.round((y1 + y2) / 2);
-                if (year < mid) {
+                if (year >= y1 && year < y2) {
                     return getDensityAtKm(densities.get(y1), km);
                 }
-                else if (i == years.size() -1) {
-                    continue;
-                }
-                else {
+                else if (year >= y2 && i == years.size() -1) {
                     return getDensityAtKm(densities.get(y2), km);
                 }
             }
@@ -93,15 +95,12 @@
         List<SedimentDensityValue> values,
         double km
     ) {
-        boolean found = true;
         SedimentDensityValue prev = null;
         SedimentDensityValue next = null;
         for (SedimentDensityValue sdv: values) {
-logger.debug("year: " + sdv.getYear());
-            if (sdv.getKm() == km) {
-                prev = sdv;
-                found = true;
-                break;
+            logger.debug("year: " + sdv.getYear());
+            if (Math.abs(sdv.getKm() - km) < 0.00001) {
+                return prev.getDensity();
             }
             if (sdv.getKm() > km) {
                 next = sdv;
@@ -109,32 +108,39 @@
             }
             prev = sdv;
         }
-        if (found) {
-            return prev.getDensity();
-        }
-        else {
-            return spline(prev, next, km);
-        }
+        return spline(prev, next, km);
     }
 
-    private double spline(
+    private static double spline(
         SedimentDensityValue prev,
         SedimentDensityValue next,
         double km
     ) {
+        if (prev == null && next == null) {
+            logger.warn("prev and next are null -> NaN");
+            return Double.NaN;
+        }
+
+        if (prev == null) return next.getDensity();
+        if (next == null) return prev.getDensity();
+
+        // XXX: This is no spline interpolation!
         double lower = prev.getKm();
         double upper = next.getKm();
         double upperDensity = next.getDensity();
         double lowerDensity = prev.getDensity();
 
-        double m =(upperDensity - lowerDensity)/(upper - lower) * km;
-        double b = lowerDensity -
-            ((upperDensity - lowerDensity)/(upper - lower) * lower);
-        return (m * km) + b;
+        double m = (upperDensity - lowerDensity)/(upper - lower);
+        double b = lowerDensity - (m * lower);
+        return m * km + b;
     }
 
+
+    /** If multiple values for same year and station are found,
+     * build and store average, dismiss multiple values. */
     public void cleanUp() {
         Set<Integer> keys = densities.keySet();
+        // Walk over years
         for (Integer key : keys) {
             List<SedimentDensityValue> list = densities.get(key);
             if (list.size() == 0) {
@@ -146,6 +152,7 @@
             int counter = 0;
             double sum = 0d;
             for (SedimentDensityValue value : list) {
+                // Apparently we can assume that values are ordered by km.
                 if (value.getKm() == prevkm) {
                     sum += value.getDensity();
                     counter++;
@@ -164,3 +171,4 @@
         }
     }
 }
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -79,14 +79,13 @@
         int year
     ) {
         log.debug("getSedimentDensityUncached");
-        List<Object[]> results = null;
         Session session = SessionHolder.HOLDER.get();
         SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_DENSITY)
             .addScalar("km", StandardBasicTypes.DOUBLE)
             .addScalar("density", StandardBasicTypes.DOUBLE)
             .addScalar("year", StandardBasicTypes.INTEGER);
         sqlQuery.setString("name", river);
-        results = sqlQuery.list();
+        List<Object[]> results = sqlQuery.list();
         SedimentDensity density = new SedimentDensity();
         for (Object[] row : results) {
             if (row[0] != null && row[1] != null && row[2] != null) {
@@ -98,3 +97,4 @@
         return density;
     }
 }
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityValue.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentDensityValue.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,7 +8,7 @@
 
 package org.dive4elements.river.artifacts.model.minfo;
 
-
+/** A density value at a km, year. */
 public class SedimentDensityValue
 {
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoad.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoad.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,27 +9,35 @@
 package org.dive4elements.river.artifacts.model.minfo;
 
 import java.util.Date;
-import java.util.HashMap;
+import java.util.Map;
 import java.util.Set;
+import java.util.TreeMap;
 
 import org.dive4elements.river.artifacts.model.NamedObjectImpl;
 import org.dive4elements.river.artifacts.model.Range;
+import org.dive4elements.river.utils.EpsilonComparator;
+
+import org.apache.log4j.Logger;
 
 
 /** Gives access to Fractions (at kms). */
 public class SedimentLoad
 extends NamedObjectImpl
 {
+    /** Private logger. */
+    private static final Logger logger = Logger
+        .getLogger(SedimentLoad.class);
+
     protected String description;
     protected Date start;
     protected Date end;
     protected boolean isEpoch;
     protected String unit;
 
-    protected HashMap<Double, SedimentLoadFraction> kms;
+    protected Map<Double, SedimentLoadFraction> kms;
 
     public SedimentLoad() {
-        kms = new HashMap<Double, SedimentLoadFraction>();
+        kms = new TreeMap<Double, SedimentLoadFraction>(EpsilonComparator.CMP);
     }
 
     public SedimentLoad(
@@ -88,124 +96,107 @@
     }
 
     public SedimentLoadFraction getFraction(double km) {
-        if (kms.get(km) == null) {
-            return new SedimentLoadFraction();
+        SedimentLoadFraction f = kms.get(km);
+        if (f == null) {
+            f = new SedimentLoadFraction();
+            kms.put(km, f);
         }
-        return kms.get(km);
+        return f;
     }
 
     public void setCoarse(double km, double coarse, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setCoarse(coarse);
+        if (range == null) {
+            logger.error("coarse/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setCoarse(coarse);
-            f.setCoarseRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setCoarse(coarse);
+        f.setCoarseRange(range);
     }
 
     public void setFineMiddle(double km, double fine_middle, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setFineMiddle(fine_middle);
-            kms.get(km).setFineMiddleRange(range);
+        if (range == null) {
+            logger.error("finemiddle/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setFineMiddle(fine_middle);
-            f.setFineMiddleRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setFineMiddle(fine_middle);
+        f.setFineMiddleRange(range);
     }
 
+
     public void setSand(double km, double sand, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSand(sand);
-            kms.get(km).setSandRange(range);
+        if (range == null) {
+            logger.error("sand/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSand(sand);
-            f.setSandRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setSand(sand);
+        f.setSandRange(range);
     }
 
     public void setSuspSand(double km, double susp_sand, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSuspSand(susp_sand);
-            kms.get(km).setSuspSandRange(range);
+        if (range == null) {
+            logger.error("suspsand/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSuspSand(susp_sand);
-            f.setSuspSandRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setSuspSand(susp_sand);
+        f.setSuspSandRange(range);
     }
 
     public void setSuspSandBed(double km, double susp_sand_bed, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSuspSandBed(susp_sand_bed);
-            kms.get(km).setSuspSandBedRange(range);
+        if (range == null) {
+            logger.error("suspsandbed/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSuspSandBed(susp_sand_bed);
-            f.setSuspSandBedRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setSuspSandBed(susp_sand_bed);
+        f.setSuspSandBedRange(range);
     }
 
     public void setSuspSediment(double km, double susp_sediment, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setSuspSediment(susp_sediment);
-            kms.get(km).setSuspSedimentRange(range);
+        if (range == null) {
+            logger.error("suspsed/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setSuspSediment(susp_sediment);
-            f.setSuspSedimentRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setSuspSediment(susp_sediment);
+        f.setSuspSedimentRange(range);
     }
 
     public void setLoadTotal(double km, double total) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setLoadTotal(total);
+        setLoadTotal(km, total, null);
+    }
+
+    public void setLoadTotal(double km, double total, Range range) {
+        if (range == null) {
+            logger.error("loadtotal/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setLoadTotal(total);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setLoadTotal(total);
+        f.setLoadTotalRange(range);
     }
 
     public void setTotal(double km, double total, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setTotal(total);
-            kms.get(km).setTotalRange(range);
+        if (range == null) {
+            logger.error("total/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setTotal(total);
-            f.setTotalRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setTotal(total);
+        f.setTotalRange(range);
     }
 
     public void setUnknown(double km, double unknown, Range range) {
-        if (kms.containsKey(km)) {
-            kms.get(km).setUnknown(unknown);
-            kms.get(km).setUnknownRange(range);
+        if (range == null) {
+            logger.error("unknown/range is null!");
+            return;
         }
-        else {
-            SedimentLoadFraction f = new SedimentLoadFraction();
-            f.setUnknown(unknown);
-            f.setUnknownRange(range);
-            kms.put(km, f);
-        }
+        SedimentLoadFraction f = getFraction(km);
+        f.setUnknown(unknown);
+        f.setUnknownRange(range);
     }
 
     public String getUnit() {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadCalculation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadCalculation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,7 +11,6 @@
 import gnu.trove.TDoubleArrayList;
 
 import java.util.ArrayList;
-import java.util.TreeSet;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -122,9 +121,99 @@
             return new CalculationResult(
                 results.toArray(new SedimentLoadResult[results.size()]), this);
         }
+        else {
+            logger.error("Unknown mode " + yearEpoch);
+        }
         return null;
     }
 
+
+    /** Returns val if not NaN, 0d otherwise. */
+    private static double makeNaN0(double val) {
+        return Double.isNaN(val)
+            ? 0d
+            : val;
+    }
+
+    /**
+     * Take Loads and build average of all fractions at given km.
+     * The average fractions value is set in resLoad.
+     *
+     * @param km km at which to build fractions average.
+     * @param epochLoads the loads to build average over.
+     * @param[out] resLoad resulting SedimentLoad.
+     */
+    private void calculateEpochKm(
+        List<SedimentLoad> epochLoads,
+        SedimentLoad resLoad,
+        double km
+    ) {
+        int cSum = 0;
+        int fmSum = 0;
+        int sSum = 0;
+        int ssSum = 0;
+        int ssbSum = 0;
+        int sseSum = 0;
+        for (SedimentLoad load : epochLoads) {
+            SedimentLoadFraction f = load.getFraction(km);
+            if (f.getCoarse() > 0d) {
+                double c = makeNaN0(resLoad.getFraction(km).getCoarse());
+                resLoad.setCoarse(km, c + f.getCoarse(), f.getCoarseRange());
+                cSum++;
+            }
+            if (f.getFineMiddle() > 0d) {
+                double fm = makeNaN0(resLoad.getFraction(km).getFineMiddle());
+                resLoad.setFineMiddle(km, fm + f.getFineMiddle(), f.getFineMiddleRange());
+                fmSum++;
+            }
+            if (f.getSand() > 0d) {
+                double s = makeNaN0(resLoad.getFraction(km).getSand());
+                resLoad.setSand(km, s + f.getSand(), f.getSandRange());
+                sSum++;
+            }
+            if (f.getSuspSand() > 0d) {
+                double s = makeNaN0(resLoad.getFraction(km).getSuspSand());
+                resLoad.setSuspSand(km, s + f.getSuspSand(), f.getSuspSandRange());
+                ssSum++;
+            }
+            if (f.getSuspSandBed() > 0d) {
+                double s = makeNaN0(resLoad.getFraction(km).getSuspSandBed());
+                resLoad.setSuspSandBed(km, s + f.getSuspSandBed(), f.getSuspSandBedRange());
+                ssbSum++;
+            }
+            if (f.getSuspSediment() > 0d) {
+                double s = makeNaN0(resLoad.getFraction(km).getSuspSediment());
+                resLoad.setSuspSediment(km, s + f.getSuspSediment(), f.getSuspSedimentRange());
+                sseSum++;
+            }
+        }
+
+        SedimentLoadFraction fr = resLoad.getFraction(km);
+        // Prevent divisions by zero.
+        if (cSum != 0) {
+            resLoad.setCoarse(km, fr.getCoarse()/cSum, fr.getCoarseRange());
+        }
+        if (fmSum != 0) {
+            resLoad.setFineMiddle(km, fr.getFineMiddle()/fmSum,
+                fr.getFineMiddleRange());
+        }
+        if (sSum != 0) {
+            resLoad.setSand(km, fr.getSand()/sSum, fr.getSandRange());
+        }
+        if (ssSum != 0) {
+            resLoad.setSuspSand(km, fr.getSuspSand()/ssSum,
+                fr.getSuspSandRange());
+        }
+        if (ssbSum != 0) {
+            resLoad.setSuspSandBed(km, fr.getSuspSandBed()/ssbSum,
+                fr.getSuspSandBedRange());
+        }
+        if (sseSum != 0) {
+            resLoad.setSuspSediment(km, fr.getSuspSediment()/sseSum, fr.getSuspSedimentRange());
+        }
+    }
+
+    /** Calculate result for an epoch. */
     private SedimentLoadResult calculateEpoch(int i) {
         List<SedimentLoad> epochLoads = new ArrayList<SedimentLoad>();
         for (int j = epoch[i][0]; j < epoch[i][1]; j++) {
@@ -133,8 +222,8 @@
                 this.yearEpoch,
                 this.kmLow,
                 this.kmUp,
-                j,
-                j));
+                j, //syear
+                j)); //eyear
         }
 
         SedimentLoad resLoad = new SedimentLoad();
@@ -149,93 +238,27 @@
         }
 
         for (int j = 0; j < kms.size(); j++) {
-            int cSum = 0;
-            int fmSum = 0;
-            int sSum = 0;
-            int ssSum = 0;
-            int ssbSum = 0;
-            int sseSum = 0;
-            double km = kms.get(j);
-            for (SedimentLoad load : epochLoads) {
-                SedimentLoadFraction f = load.getFraction(km);
-                if (f.getCoarse() > 0d) {
-                    double c = resLoad.getFraction(km).getCoarse();
-                    resLoad.setCoarse(km, c + f.getCoarse(), f.getCoarseRange());
-                    cSum++;
-                }
-                if (f.getFineMiddle() > 0d) {
-                    double fm = resLoad.getFraction(km).getFineMiddle();
-                    resLoad.setFineMiddle(km, fm + f.getFineMiddle(), f.getFineMiddleRange());
-                    fmSum++;
-                }
-                if (f.getSand() > 0d) {
-                    double s = resLoad.getFraction(km).getSand();
-                    resLoad.setSand(km, s + f.getSand(), f.getSandRange());
-                    sSum++;
-                }
-                if (f.getSuspSand() > 0d) {
-                    double s = resLoad.getFraction(km).getSuspSand();
-                    resLoad.setSuspSand(km, s + f.getSuspSand(), f.getSuspSandRange());
-                    ssSum++;
-                }
-                if (f.getSuspSandBed() > 0d) {
-                    double s = resLoad.getFraction(km).getSuspSandBed();
-                    resLoad.setSuspSandBed(km, s + f.getSuspSandBed(), f.getSuspSandBedRange());
-                    ssbSum++;
-                }
-                if (f.getSuspSediment() > 0d) {
-                    double s = resLoad.getFraction(km).getSuspSediment();
-                    resLoad.setSuspSediment(km, s + f.getSuspSediment(), f.getSuspSedimentRange());
-                    sseSum++;
-                }
-            }
-            SedimentLoadFraction fr = resLoad.getFraction(km);
-            // Prevent divisions by zero, the fraction defaults to 0d.
-            if (cSum != 0) {
-                resLoad.setCoarse(km, fr.getCoarse()/cSum, fr.getCoarseRange());
-            }
-            if (fmSum != 0) {
-                resLoad.setFineMiddle(km, fr.getFineMiddle()/fmSum,
-                    fr.getFineMiddleRange());
-            }
-            if (sSum != 0) {
-                resLoad.setSand(km, fr.getSand()/sSum, fr.getSandRange());
-            }
-            if (ssSum != 0) {
-                resLoad.setSuspSand(km, fr.getSuspSand()/ssSum,
-                    fr.getSuspSandRange());
-            }
-            if (ssbSum != 0) {
-                resLoad.setSuspSandBed(km, fr.getSuspSandBed()/ssbSum,
-                    fr.getSuspSandBedRange());
-            }
-            if (sseSum != 0) {
-                resLoad.setSuspSediment(km, fr.getSuspSediment()/sseSum, fr.getSuspSedimentRange());
-            }
+            calculateEpochKm(epochLoads, resLoad, kms.get(j));
         }
         resLoad.setDescription("");
         resLoad.setEpoch(true);
 
-        SedimentLoadResult result;
         SedimentLoad sl = calculateTotalLoad(resLoad, this.epoch[i][0]);
+
         if (this.unit.equals("m3_per_a")) {
-            SedimentLoad slu = calculateUnit(sl, this.epoch[i][0]);
-            result = new SedimentLoadResult(
-                this.epoch[i][0],
-                this.epoch[i][1],
-                slu);
-        }
-        else {
-            result = new SedimentLoadResult(
-                this.epoch[i][0],
-                this.epoch[i][1],
-                sl);
+            sl = calculateUnit(sl, this.epoch[i][0]);
         }
 
-        return result;
+        return new SedimentLoadResult(
+            this.epoch[i][0],
+            this.epoch[i][1],
+            sl);
     }
 
-    /** Calculate/Fetch values at off. epochs. */
+    /**
+     * Calculate/Fetch values at off. epochs.
+     * @param i index in epochs.
+     */
     private SedimentLoadResult calculateOffEpoch(int i) {
         SedimentLoad load = SedimentLoadFactory.getLoadWithData(
             this.river,
@@ -321,35 +344,6 @@
     }
 
 
-    /** Returns true if all fraction values except SuspSediment are unset. */
-    private boolean hasOnlySuspValues(SedimentLoadFraction fraction) {
-        return (fraction.getSuspSediment() != 0d &&
-            fraction.getCoarse() == 0d &&
-            fraction.getFineMiddle() == 0d &&
-            fraction.getSand() == 0d &&
-            fraction.getSuspSand() == 0d);
-    }
-
-
-    /** Returns true if all fraction values except SuspSediment are set. */
-    private boolean hasButSuspValues(SedimentLoadFraction fraction) {
-        return (fraction.getSuspSediment() == 0d &&
-            fraction.getCoarse() != 0d &&
-            fraction.getFineMiddle() != 0d &&
-            fraction.getSand() != 0d &&
-            fraction.getSuspSand() != 0d);
-    }
-
-
-    /** Returns true if all fraction needed for total calculation are set. */
-    private boolean complete(SedimentLoadFraction fraction) {
-        return (fraction.getCoarse() != 0d &&
-                fraction.getFineMiddle() != 0d &&
-                fraction.getSand() != 0d &&
-                fraction.getSuspSand() != 0d &&
-                fraction.getSuspSediment() != 0d);
-    }
-
 
     /**
      * Set total values in load.
@@ -364,6 +358,7 @@
      * @return input param load, with total values set.
      */
     private SedimentLoad partialTotal(SedimentLoad load) {
+        // The load with balanced ranges, will be returned.
         SedimentLoad fairLoad = load;
 
         Range lastOtherRange = null;
@@ -372,12 +367,10 @@
         Range lastSuspRange = null;
         double lastSuspValue = 0d;
 
-        TreeSet<Double> kms = new TreeSet<Double>(load.getKms());
-
-        for (double km: kms) {
+        for (double km: load.getKms()) { // kms are already sorted!
             logger.debug ("Trying to add at km " + km);
             SedimentLoadFraction fraction = load.getFraction(km);
-            if (complete(fraction)) {
+            if (fraction.isComplete()) {
                 double total = fraction.getCoarse() +
                     fraction.getFineMiddle() +
                     fraction.getSand() +
@@ -405,7 +398,7 @@
                     else {
                         // Geschiebe is longer.
                         // Adjust and remember other values.
-                        lastOtherRange = (Range) fraction.getSuspSedimentRange().clone();
+                        lastOtherRange = (Range) fraction.getCoarseRange().clone();
                         lastOtherRange.setStart(fraction.getSuspSedimentRange().getEnd());
                         lastOtherValue = (total - fraction.getSuspSediment());
                         lastSuspRange = null;
@@ -413,7 +406,7 @@
                     }
                 }
             }
-            else if (hasOnlySuspValues(fraction) && lastOtherRange != null) {
+            else if (fraction.hasOnlySuspValues() && lastOtherRange != null) {
                 // Split stuff.
                 Range suspSedimentRange = fraction.getSuspSedimentRange();
                 // if intersects with last other range, cool! merge and add!
@@ -438,11 +431,12 @@
                         lastOtherRange.setStart(suspSedimentRange.getEnd());
                         lastSuspRange = null;
                     }
-                    if (Math.abs(suspSedimentRange.getEnd() - lastOtherRange.getEnd()) < 0.1d) {
+                    if (lastOtherRange != null
+                    && Math.abs(suspSedimentRange.getEnd() - lastOtherRange.getEnd()) < 0.1d) {
                         lastOtherRange = null;
                         lastSuspRange = null;
                     }
-                    fairLoad.setTotal(km, total + fraction.getSuspSediment(), totalRange);
+                    fairLoad.setTotal(km, total, totalRange);
                 }
                 else {
                     lastSuspRange = suspSedimentRange;
@@ -450,7 +444,7 @@
                     lastOtherRange = null;
                 }
             }
-            else if (hasButSuspValues(fraction) && lastSuspRange != null) {
+            else if (fraction.hasButSuspValues() && lastSuspRange != null) {
                 // If intersects with last suspsed range, merge and add
                 double total = fraction.getCoarse() +
                     fraction.getFineMiddle() +
@@ -475,7 +469,9 @@
                         lastSuspRange = null;
                         lastOtherValue = total - lastSuspValue;
                     }
-                    if (lastSuspRange != null && Math.abs(lastSuspRange.getEnd() - lastOtherRange.getEnd()) < 0.1d) {
+                    if (lastSuspRange != null
+                    &&  lastOtherRange != null
+                    && Math.abs(lastSuspRange.getEnd() - lastOtherRange.getEnd()) < 0.1d) {
                         lastOtherRange = null;
                         lastSuspRange = null;
                     }
@@ -491,7 +487,7 @@
             else {
                 // Some values are missing or no intersection with former values.
                 // Stay as we are.
-                if (hasButSuspValues(fraction)) {
+                if (fraction.hasButSuspValues()) {
                     double total = fraction.getCoarse() +
                         fraction.getFineMiddle() +
                         fraction.getSand() +
@@ -500,7 +496,7 @@
                     lastOtherValue = total;
                     lastSuspRange = null;
                 }
-                else if (hasOnlySuspValues(fraction)) {
+                else if (fraction.hasOnlySuspValues()) {
                     lastSuspRange = fraction.getSuspSedimentRange();
                     lastSuspValue = fraction.getSuspSediment();
                     lastOtherRange = null;
@@ -513,12 +509,18 @@
 
     /**
      * Transform values in load.
+     * Background is to transform values measured in
+     * t/a to m^3/a using the specific measured densities.
+     *
      * @param load The load of which values should be transformed.
+     * @param year The year at which to look at density (e.g. 2003).
+     *
      * @return parameter load with transformed values.
      */
     private SedimentLoad calculateUnit(SedimentLoad load, int year) {
         SedimentDensity density =
             SedimentDensityFactory.getSedimentDensity(river, kmLow, kmUp, year);
+
         for (double km: load.getKms()) {
             double dens = density.getDensity(km, year);
             SedimentLoadFraction fraction = load.getFraction(km);
@@ -529,6 +531,7 @@
             double bedSand = fraction.getSuspSandBed();
             double sediment = fraction.getSuspSediment();
             double total = fraction.getTotal();
+            double loadTotal = fraction.getLoadTotal();
             load.setCoarse(km, (coarse * dens), fraction.getCoarseRange());
             load.setFineMiddle(km, (fineMiddle * dens), fraction.getFineMiddleRange());
             load.setSand(km, (sand * dens), fraction.getSandRange());
@@ -536,6 +539,7 @@
             load.setSuspSandBed(km, (bedSand * dens), fraction.getSuspSandBedRange());
             load.setSuspSediment(km, (sediment * dens), fraction.getSuspSedimentRange());
             load.setTotal(km, (total * dens), fraction.getTotalRange());
+            load.setLoadTotal(km, (loadTotal * dens), fraction.getLoadTotalRange());
         }
         return load;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -31,6 +31,7 @@
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeSet;
 import java.util.TreeMap;
 
 import org.apache.log4j.Logger;
@@ -105,7 +106,7 @@
             TDoubleArrayList xPos = new TDoubleArrayList();
             TDoubleArrayList yPos = new TDoubleArrayList();
             double lastX = -1d;
-            for (double km: load.getKms()) {
+            for (double km: new TreeSet<Double>(load.getKms())) {
                 SedimentLoadFraction fraction = load.getFraction(km);
                 if (fraction.getTotal() != 0) {
                     if (Math.abs(lastX-km) >= EPSILON) {
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -54,6 +54,13 @@
         "       AND ti.stop_time IS NULL " +
         "       AND syv.station BETWEEN :startKm AND :endKm";
 
+    /** Query to get description of single sediment_yield. */
+    public static final String SQL_SELECT_SINGLE_BY_ID =
+        "SELECT DISTINCT " +
+        "       sy.description AS description " +
+        "   FROM     sediment_yield sy " +
+        "   WHERE   sy.id = :id ";
+
     /** Query to get description, name and time range for official
      * epoch-type sediment yields. */
     public static final String SQL_SELECT_OFFEPOCHS =
@@ -86,6 +93,20 @@
         "       AND ti.stop_time IS NOT NULL " +
         "       AND syv.station BETWEEN :startKm AND :endKm";
 
+    public static final String SQL_SELECT_SINGLES_DATA_BY_ID =
+        "SELECT" +
+        "       sy.description AS description, " +
+        "       syv.value AS load, " +
+        "       syv.station AS km, " +
+        "       u.name AS unit, " +
+        "       gf.name AS fraction " +
+        "   FROM     sediment_yield sy " +
+        "       JOIN sediment_yield_values syv ON sy.id = syv.sediment_yield_id " +
+        "       JOIN units u ON u.id = sy.unit_id" +
+        "       JOIN grain_fraction gf ON sy.grain_fraction_id = gf.id " +
+        "   WHERE   sy.id = :id" +
+        "   ORDER BY syv.station";
+
     public static final String SQL_SELECT_SINGLES_DATA =
         "SELECT" +
         "       sy.description AS description, " +
@@ -137,6 +158,7 @@
         "    JOIN units u ON sy.unit_id = u.id " +
         "WHERE r.name = :river " +
         "    AND gf.name = 'unknown' " +
+        "    AND sy.kind = :type " +
         "    AND u.name = :unit";
 
     public static final String SQL_SELECT_EPOCHS_DATA =
@@ -204,7 +226,8 @@
 
     /**
      * Get a sedimentLoad filled with data from db (or cache).
-     * @param type "epoch","off_epoch" or "single"
+     * @param type "epoch", "off_epoch" or "single"
+     * @return A Sedimentload filled with values from db or cache.
      */
     public static SedimentLoad getLoadWithData(
         String river,
@@ -247,7 +270,7 @@
             eyear);
 
         if (values != null && key != null) {
-            log.debug("Store static bed height values in cache.");
+            log.debug("Store sediment loads in cache.");
             element = new Element(key, values);
             cache.put(element);
         }
@@ -255,6 +278,26 @@
     }
 
     /**
+     * Get sediment load description.
+     * @param id the sediment yield by id.
+     * @return sediment yields description
+     */
+    public static String getSedimentYieldDescription(int id) {
+        log.debug("SedimentLoadFactory.getSedimentYieldDescription");
+
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLE_BY_ID)
+                .addScalar("description", StandardBasicTypes.STRING);
+        sqlQuery.setDouble("id", id);
+
+        List<Object> results = sqlQuery.list();
+
+        return (String) results.get(0);
+
+    }
+
+
+    /**
      * Get sediment loads from db.
      * @param river the river
      * @param type the sediment load type (year or epoch)
@@ -341,9 +384,106 @@
     }
 
     /**
+     * Get a specific sediment load from db.
+     *
+     * @param id the sediment yields id.
+     *
+     * @return according sediment load.
+     */
+    public static SedimentLoad getSedimentLoadWithDataUncached(
+        String id,
+        String river
+    ) {
+        log.debug("SedimentLoadFactory.getSedimentLoadWithDataUncached / id " + id);
+        Session session = SessionHolder.HOLDER.get();
+        SQLQuery sqlQuery = null;
+
+        // Measurement stations: all, for float-stuff, for suspended stuff.
+        // Because they need fast sorted access, use TreeMaps.
+        // They map the starting validity range km to the station itself.
+        List<MeasurementStation> allStations =
+            RiverFactory.getRiver(river).getMeasurementStations();
+        TreeMap<Double,MeasurementStation> floatStations =
+            new TreeMap<Double, MeasurementStation>();
+        TreeMap<Double,MeasurementStation> suspStations =
+            new TreeMap<Double, MeasurementStation>();
+
+        // From all stations, sort into the two kinds, skip undefined ones.
+        for (MeasurementStation measurementStation: allStations) {
+            if (measurementStation.getMeasurementType() == null ||
+                measurementStation.getRange() == null) {
+                continue;
+            }
+            if (measurementStation.getMeasurementType().equals("Schwebstoff")) {
+                suspStations.put(
+                    measurementStation.getRange().getA().doubleValue(),
+                    measurementStation);
+            }
+            else if (measurementStation.getMeasurementType().equals("Geschiebe")) {
+                floatStations.put(
+                    measurementStation.getRange().getA().doubleValue(),
+                    measurementStation);
+            }
+        }
+
+        sqlQuery = session.createSQLQuery(SQL_SELECT_SINGLES_DATA_BY_ID)
+            .addScalar("description", StandardBasicTypes.STRING)
+            .addScalar("load", StandardBasicTypes.DOUBLE)
+            .addScalar("km", StandardBasicTypes.DOUBLE)
+            .addScalar("fraction", StandardBasicTypes.STRING)
+            .addScalar("unit", StandardBasicTypes.STRING);
+        sqlQuery.setInteger("id", Integer.valueOf(id));
+
+        List<Object []> results = sqlQuery.list();
+        SedimentLoad load = new SedimentLoad();
+        if (results.isEmpty()) {
+            log.warn("Empty result for year calculation.");
+        }
+        else {
+            Object[] row = results.get(0);
+            load = new SedimentLoad(
+                    (String) row[0], //description
+                    null,//(Date) row[1], //start
+                    null,  //end
+                    false, //isEpoch
+                    (String) row[4]); //unit
+
+            String fraction = (String) row[3];
+
+            TreeMap<Double,MeasurementStation> relevantStations = 
+                fraction.equals("suspended_sediment") /* || TODO clarify: fraction.equals("susp_sand") */
+                ? suspStations
+                : floatStations;
+
+            for (int i = 0; i < results.size(); i++) {
+                row = results.get(i);
+                double km = (Double) row[2];
+                Range range = findMeasurementStationRange(relevantStations, km);
+                if (range == null) {
+                    log.warn("No measurement station for " + fraction + " km " + km);
+                    continue;
+                }
+
+                double v = -1;
+
+                if (row[1] != null) {
+                    v = ((Double)row[1]).doubleValue();
+                }
+
+                setLoadFraction(load, km, v, range, fraction);
+            }
+
+        }
+
+        return load;
+    }
+
+    /**
      * Get sediment loads from db.
+     *
      * @param river the river
      * @param type the sediment load type (year, epoch or off_epoch)
+     *
      * @return according sediment loads.
      */
     public static SedimentLoad getSedimentLoadWithDataUncached(
@@ -358,22 +498,35 @@
         Session session = SessionHolder.HOLDER.get();
         SQLQuery sqlQuery = null;
 
-        List<MeasurementStation> allStations = RiverFactory.getRiver(river).getMeasurementStations();
-        TreeMap<Double,MeasurementStation> floatStations = new TreeMap<Double, MeasurementStation>();
-        TreeMap<Double,MeasurementStation> suspStations = new TreeMap<Double, MeasurementStation>();
+        // Measurement stations: all, for float-stuff, for suspended stuff.
+        // Because they need fast sorted access, use TreeMaps.
+        // They map the starting validity range km to the station itself.
+        List<MeasurementStation> allStations =
+            RiverFactory.getRiver(river).getMeasurementStations();
+        TreeMap<Double,MeasurementStation> floatStations =
+            new TreeMap<Double, MeasurementStation>();
+        TreeMap<Double,MeasurementStation> suspStations =
+            new TreeMap<Double, MeasurementStation>();
+
+        // From all stations, sort into the two kinds, skip undefined ones.
         for (MeasurementStation measurementStation: allStations) {
             if (measurementStation.getMeasurementType() == null ||
                 measurementStation.getRange() == null) {
                 continue;
             }
             if (measurementStation.getMeasurementType().equals("Schwebstoff")) {
-                suspStations.put(measurementStation.getRange().getA().doubleValue(), measurementStation);
+                suspStations.put(
+                    measurementStation.getRange().getA().doubleValue(),
+                    measurementStation);
             }
             else if (measurementStation.getMeasurementType().equals("Geschiebe")) {
-                floatStations.put(measurementStation.getRange().getA().doubleValue(), measurementStation);
+                floatStations.put(
+                    measurementStation.getRange().getA().doubleValue(),
+                    measurementStation);
             }
         }
 
+        // Construct date constraint.
         Calendar start = Calendar.getInstance();
         start.set(syear - 1, 11, 31);
         Calendar end = Calendar.getInstance();
@@ -394,19 +547,24 @@
             sqlQuery.setString("grain", "total");
             List<Object []> results = sqlQuery.list();
             SedimentLoad load = new SedimentLoad();
-            Object[] row = results.get(0);
-            load = new SedimentLoad(
-                    (String) row[0],
-                    (Date) row[1],
-                    null,
-                    false,
-                    (String) row[4]);
-            getValues("coarse", sqlQuery, load, floatStations);
-            getValues("fine_middle", sqlQuery, load, floatStations);
-            getValues("sand", sqlQuery, load, floatStations);
-            getValues("suspended_sediment", sqlQuery, load, suspStations);
-            getValues("susp_sand_bed", sqlQuery, load, floatStations);
-            getValues("susp_sand", sqlQuery, load, floatStations);
+            if (results.isEmpty()) {
+                log.warn("Empty result for year calculation.");
+            }
+            else {
+                Object[] row = results.get(0);
+                load = new SedimentLoad(
+                        (String) row[0], //description
+                        (Date) row[1], //start
+                        null,  //end
+                        false, //isEpoch
+                        (String) row[4]); //unit
+            }
+            load = getValues("coarse", sqlQuery, load, floatStations);
+            load = getValues("fine_middle", sqlQuery, load, floatStations);
+            load = getValues("sand", sqlQuery, load, floatStations);
+            load = getValues("suspended_sediment", sqlQuery, load, suspStations);
+            load = getValues("susp_sand_bed", sqlQuery, load, floatStations);
+            load = getValues("susp_sand", sqlQuery, load, floatStations);
 
             return load;
         }
@@ -467,6 +625,30 @@
         return new SedimentLoad();
     }
 
+    protected static Range findMeasurementStationRange(
+        TreeMap<Double, MeasurementStation> stations,
+        double km
+    ) {
+        MeasurementStation station = stations.get(km);
+        if (station == null) {
+            return null;
+        }
+
+        double endKm;
+
+        if (stations.ceilingEntry(km + 0.1d) != null) {
+            MeasurementStation nextStation = stations.ceilingEntry(km + 0.1d).getValue();
+            endKm = nextStation.getRange().getA().doubleValue();
+        }
+        else {
+            // TODO end-of-river instead of B.
+            endKm = station.getRange().getB().doubleValue();
+        }
+
+        return new Range(
+            station.getRange().getA().doubleValue(),
+            endKm);
+    }
 
     /**
      * Run query with grain parameter set to fraction, feed result into
@@ -501,53 +683,54 @@
         for (int i = 0; i < results.size(); i++) {
             Object[] row = results.get(i);
             double km = (Double)row[3];
-            MeasurementStation station = stations.get(km);
-            MeasurementStation nextStation = null;
-            if (stations.ceilingEntry(km + 0.1d) != null) {
-                nextStation = stations.ceilingEntry(km + 0.1d).getValue();
-            }
-            Range range = null;
-            if (station == null) {
+            Range range = findMeasurementStationRange(stations, km);
+            if (range == null) {
                 log.warn("No measurement station for " + fraction + " km " + km);
                 continue;
             }
-            else {
-                if (nextStation != null)
-                    range = new Range(station.getRange().getA().doubleValue(),
-                        nextStation.getRange().getA().doubleValue());
-                else {
-                    // TODO end-of-river instead of B.
-                    range = new Range(station.getRange().getA().doubleValue(),
-                        station.getRange().getB().doubleValue());
-                }
-            }
             double v = -1;
+
             if (row[2] != null) {
                 v = ((Double)row[2]).doubleValue();
             }
-            if (fraction.equals("coarse")) {
-                load.setCoarse(km, v, range);
-            }
-            else if (fraction.equals("sand")) {
-                load.setSand(km, v, range);
-            }
-            else if (fraction.equals("fine_middle")) {
-                load.setFineMiddle(km, v, range);
-            }
-            else if (fraction.equals("suspended_sediment")) {
-                load.setSuspSediment(km, v, range);
-            }
-            else if (fraction.equals("susp_sand")) {
-                load.setSuspSand(km, v, range);
-            }
-            else if (fraction.equals("susp_sand_bed")) {
-                load.setSuspSandBed(km, v, range);
-            }
+
+            setLoadFraction(load, km, v, range, fraction);
+        }
+
+        if (results.isEmpty()) {
+            log.warn("No " + fraction + " values found.");
         }
 
         return load;
     }
 
+
+    /** Set a fraction value of load to given km, value and range. */
+    private static void setLoadFraction(
+        SedimentLoad load, double km, double v, Range range, String fraction) {
+        if (fraction.equals("coarse")) {
+            load.setCoarse(km, v, range);
+        }
+        else if (fraction.equals("sand")) {
+            load.setSand(km, v, range);
+        }
+        else if (fraction.equals("fine_middle")) {
+            load.setFineMiddle(km, v, range);
+        }
+        else if (fraction.equals("suspended_sediment")) {
+            load.setSuspSediment(km, v, range);
+        }
+        else if (fraction.equals("susp_sand")) {
+            load.setSuspSand(km, v, range);
+        }
+        else if (fraction.equals("susp_sand_bed")) {
+            load.setSuspSandBed(km, v, range);
+        }
+        else {
+            log.error("Unknown fraction type " + fraction);
+        }
+    }
+
     public static SedimentLoad getLoadUnknown(
         String river,
         String description
@@ -627,9 +810,14 @@
 
     /**
      * Return sediment loads with 'unknown' fraction type.
+     * @param river Name of the river
      * @param unit Restrict result set to those of given unit.
+     * @param type Type like year, epoch, off_epoch
      */
-    public static SedimentLoad[] getSedimentLoadUnknown(String river, String unit) {
+    public static SedimentLoad[] getSedimentLoadUnknown(
+        String river,
+        String unit,
+        String type) {
         Session session = SessionHolder.HOLDER.get();
         SQLQuery sqlQuery = session.createSQLQuery(SQL_SELECT_UNKNOWN)
             .addScalar("description", StandardBasicTypes.STRING)
@@ -637,6 +825,12 @@
             .addScalar("end", StandardBasicTypes.DATE);
         sqlQuery.setString("river", river);
         sqlQuery.setString("unit", unit);
+        if (type.equals("off_epoch")) {
+            sqlQuery.setInteger("type", 1);
+        }
+        else {
+            sqlQuery.setInteger("type", 0);
+        }
         List<Object[]> results = sqlQuery.list();
         SedimentLoad[] loads = new SedimentLoad[results.size()];
         int counter = 0;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFraction.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadFraction.java	Fri Sep 27 17:36:50 2013 +0200
@@ -25,25 +25,25 @@
     double total;
     double unknown;
     /** Values are valid within this km range. */
-    Range sandRange = null;
-    Range fineMiddleRange = null;
-    Range coarseRange = null;
-    Range suspSandRange = null;
-    Range suspSandBedRange = null;
-    Range suspSedimentRange = null;
-    Range loadTotalRange = null;
-    Range totalRange = null;
-    Range unknownRange = null;
+    Range sandRange;
+    Range fineMiddleRange;
+    Range coarseRange;
+    Range suspSandRange;
+    Range suspSandBedRange;
+    Range suspSedimentRange;
+    Range loadTotalRange;
+    Range totalRange;
+    Range unknownRange;
 
     public SedimentLoadFraction() {
-        sand = 0d;
-        fineMiddle = 0d;
-        coarse = 0d;
-        suspSand = 0d;
-        suspSandBed = 0d;
-        suspSediment = 0d;
-        loadTotal = 0d;
-        unknown = 0d;
+        sand         = Double.NaN;
+        fineMiddle   = Double.NaN;
+        coarse       = Double.NaN;
+        suspSand     = Double.NaN;
+        suspSandBed  = Double.NaN;
+        suspSediment = Double.NaN;
+        loadTotal    = Double.NaN;
+        unknown      = Double.NaN;
     }
 
     public double getSand() {
@@ -166,6 +166,10 @@
         this.loadTotal = total;
     }
 
+    public Range getLoadTotalRange() {
+        return this.loadTotalRange;
+    }
+
     public void setLoadTotalRange(Range range) {
         this.loadTotalRange = range;
     }
@@ -185,5 +189,35 @@
     public void setUnknownRange(Range unknownRange) {
         this.unknownRange = unknownRange;
     }
+
+    /** Returns true if all fraction values except SuspSediment are unset. */
+    public boolean hasOnlySuspValues() {
+        return
+            !Double.isNaN(getSuspSediment()) &&
+            Double.isNaN(getCoarse()) &&
+            Double.isNaN(getFineMiddle()) &&
+            Double.isNaN(getSand()) &&
+            Double.isNaN(getSuspSand());
+    }
+
+    /** Returns true if all fraction values except SuspSediment are set. */
+    public boolean hasButSuspValues() {
+        return
+            Double.isNaN(getSuspSediment()) &&
+            !Double.isNaN(getCoarse()) &&
+            !Double.isNaN(getFineMiddle()) &&
+            !Double.isNaN(getSand()) &&
+            !Double.isNaN(getSuspSand());
+    }
+
+    /** Returns true if all fraction needed for total calculation are set. */
+    public boolean isComplete() {
+        return
+            !Double.isNaN(getCoarse()) &&
+            !Double.isNaN(getFineMiddle()) &&
+            !Double.isNaN(getSand()) &&
+            !Double.isNaN(getSuspSand()) &&
+            !Double.isNaN(getSuspSediment());
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadUnknownFacet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/minfo/SedimentLoadUnknownFacet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -35,7 +35,10 @@
         String river = access.getRiver();
         String unit  = access.getUnit();
         SedimentLoad[] unknown =
-            SedimentLoadFactory.getSedimentLoadUnknown(river, unit.replace("_per_","/"));
+            SedimentLoadFactory.getSedimentLoadUnknown(
+                river,
+                unit.replace("_per_","/"),
+                access.getYearEpoch());
 
         SedimentLoad load = SedimentLoadFactory.getLoadUnknown(
             river, unknown[index].getDescription());
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Fitting.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Fitting.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,6 +8,7 @@
 
 package org.dive4elements.river.artifacts.model.sq;
 
+import org.dive4elements.artifacts.common.utils.StringUtils;
 import org.dive4elements.river.artifacts.math.fitting.Function;
 
 import java.util.ArrayList;
@@ -18,12 +19,17 @@
 import org.apache.commons.math.optimization.fitting.CurveFitter;
 
 import org.apache.commons.math.optimization.general.LevenbergMarquardtOptimizer;
+import org.apache.commons.math.stat.regression.SimpleRegression;
 
 import org.apache.log4j.Logger;
 
 public class Fitting
 implements   Outlier.Callback
 {
+    // XXX: Hack to force linear fitting!
+    private static final boolean USE_NON_LINEAR_FITTING =
+        Boolean.getBoolean("minfo.sq.fitting.nonlinear");
+
     private static Logger log = Logger.getLogger(Fitting.class);
 
     public interface Callback {
@@ -47,13 +53,15 @@
 
     protected Callback callback;
 
+    protected SQ.View sqView;
+
     public Fitting() {
     }
 
-    public Fitting(Function function, double stdDevFactor) {
-        this();
+    public Fitting(Function function, double stdDevFactor, SQ.View sqView) {
         this.function     = function;
         this.stdDevFactor = stdDevFactor;
+        this.sqView       = sqView;
     }
 
     public Function getFunction() {
@@ -75,12 +83,70 @@
     @Override
     public void initialize(List<SQ> sqs) throws MathException {
 
-        LevenbergMarquardtOptimizer lmo =
+        if (USE_NON_LINEAR_FITTING
+        || function.getParameterNames().length != 2) {
+            nonLinearFitting(sqs);
+        }
+        else {
+            linearFitting(sqs);
+        }
+    }
+
+    protected void linearFitting(List<SQ> sqs) {
+        coeffs   = linearRegression(sqs);
+        instance = function.instantiate(coeffs);
+    }
+
+    protected double [] linearRegression(List<SQ> sqs) {
+
+        String [] pns = function.getParameterNames();
+        double [] result = new double[pns.length];
+
+        if (sqs.size() < 2) {
+            log.debug("not enough points");
+            return result;
+        }
+
+        SimpleRegression reg = new SimpleRegression();
+
+        for (SQ sq: sqs) {
+            double s = sqView.getS(sq);
+            double q = sqView.getQ(sq);
+            reg.addData(q, s);
+        }
+
+        double m = reg.getIntercept();
+        double b = reg.getSlope();
+
+        if (log.isDebugEnabled()) {
+            log.debug("m: " + m);
+            log.debug("b: " + b);
+        }
+
+        int mIdx = StringUtils.indexOf("m", pns);
+        int bIdx = StringUtils.indexOf("b", pns);
+
+        if (mIdx == -1 || bIdx == -1) {
+            log.error("index not found: " + mIdx + " " + bIdx);
+            return result;
+        }
+
+        result[bIdx] = m;
+        result[mIdx] = b;
+
+        return result;
+    }
+
+
+    protected void nonLinearFitting(List<SQ> sqs) throws MathException {
+
+        LevenbergMarquardtOptimizer optimizer =
             new LevenbergMarquardtOptimizer();
 
-        CurveFitter cf = new CurveFitter(lmo);
+        CurveFitter cf = new CurveFitter(optimizer);
+
         for (SQ sq: sqs) {
-            cf.addObservedPoint(sq.getQ(), sq.getS());
+            cf.addObservedPoint(sqView.getQ(sq), sqView.getS(sq));
         }
 
         coeffs = cf.fit(
@@ -88,13 +154,13 @@
 
         instance = function.instantiate(coeffs);
 
-        chiSqr = lmo.getChiSquare();
+        chiSqr = optimizer.getChiSquare();
     }
 
     @Override
     public double eval(SQ sq) {
-        double s = instance.value(sq.q);
-        return sq.s - s;
+        double s = instance.value(sqView.getQ(sq));
+        return sqView.getS(sq) - s;
     }
 
     @Override
@@ -119,28 +185,15 @@
             chiSqr);
     }
 
-    protected static final List<SQ> onlyValid(List<SQ> sqs) {
-
-        List<SQ> good = new ArrayList<SQ>(sqs.size());
-
-        for (SQ sq: sqs) {
-            if (sq.isValid()) {
-                good.add(sq);
-            }
-        }
-
-        return good;
-    }
-
-    public boolean fit(List<SQ> sqs, String method, Callback callback) {
-
-        sqs = onlyValid(sqs);
+    public boolean fit(List<SQ> sqs, String  method, Callback callback) {
 
         if (sqs.size() < 2) {
             log.warn("Too less points for fitting.");
             return false;
         }
 
+        sqs = new ArrayList<SQ>(sqs);
+
         this.callback = callback;
 
         try {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/LogSQ.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,68 @@
+/* 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.artifacts.model.sq;
+
+public class LogSQ extends SQ {
+
+    public static final View LOG_SQ_VIEW = new View() {
+        @Override
+        public double getS(SQ sq) {
+            return ((LogSQ)sq).getLogS();
+        }
+
+        @Override
+        public double getQ(SQ sq) {
+            return ((LogSQ)sq).getLogQ();
+        }
+    };
+
+    public static final Factory LOG_SQ_FACTORY = new Factory() {
+        @Override
+        public SQ createSQ(double s, double q) {
+            return new LogSQ(s, q);
+        }
+    };
+
+    protected double logS;
+    protected double logQ;
+
+    protected boolean logTrans;
+
+    public LogSQ() {
+    }
+
+    public LogSQ(double s, double q) {
+        super(s, q);
+    }
+
+    /** important: We cannot process negative s/q. */
+    @Override
+    public boolean isValid() {
+        return super.isValid() && s > 0d && q > 0d;
+    }
+
+    protected void ensureLogTrans() {
+        if (!logTrans) {
+            logTrans = true;
+            logS = Math.log(s);
+            logQ = Math.log(q);
+        }
+    }
+
+    public double getLogS() {
+        ensureLogTrans();
+        return logS;
+    }
+
+    public double getLogQ() {
+        ensureLogTrans();
+        return logQ;
+    }
+}
+
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/MeasurementFactory.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/MeasurementFactory.java	Fri Sep 27 17:36:50 2013 +0200
@@ -58,32 +58,32 @@
             "gp.LFDNR       AS LFDNR," +
             "g.UFERABST     AS UFERABST," +
             "g.UFERABLINKS  AS UFERABLINKS," +
-            "m.TSCHWEB      AS TSCHWEB," +
-            "m.TSAND        AS TSAND," +
-            "gp.GTRIEB_F    AS GTRIEB," +
-            "m.TGESCHIEBE   AS TGESCHIEBE," +
-            "si.SIEB01 AS SIEB01, si.SIEB02 AS SIEB02," +
-            "si.SIEB03 AS SIEB03, si.SIEB04 AS SIEB04," +
-            "si.SIEB05 AS SIEB05, si.SIEB06 AS SIEB06," +
-            "si.SIEB07 AS SIEB07, si.SIEB08 AS SIEB08," +
-            "si.SIEB09 AS SIEB09, si.SIEB10 AS SIEB10," +
-            "si.SIEB11 AS SIEB11, si.SIEB12 AS SIEB12," +
-            "si.SIEB13 AS SIEB13, si.SIEB14 AS SIEB14," +
-            "si.SIEB15 AS SIEB15, si.SIEB16 AS SIEB16," +
-            "si.SIEB17 AS SIEB17, si.SIEB18 AS SIEB18," +
-            "si.SIEB19 AS SIEB19, si.SIEB20 AS SIEB20," +
-            "si.SIEB21 AS SIEB21," +
-            "gs.RSIEB01 AS RSIEB01, gs.RSIEB02 AS RSIEB02," +
-            "gs.RSIEB03 AS RSIEB03, gs.RSIEB04 AS RSIEB04," +
-            "gs.RSIEB05 AS RSIEB05, gs.RSIEB06 AS RSIEB06," +
-            "gs.RSIEB07 AS RSIEB07, gs.RSIEB08 AS RSIEB08," +
-            "gs.RSIEB09 AS RSIEB09, gs.RSIEB10 AS RSIEB10," +
-            "gs.RSIEB11 AS RSIEB11, gs.RSIEB12 AS RSIEB12," +
-            "gs.RSIEB13 AS RSIEB13, gs.RSIEB14 AS RSIEB14," +
-            "gs.RSIEB15 AS RSIEB15, gs.RSIEB16 AS RSIEB16," +
-            "gs.RSIEB17 AS RSIEB17, gs.RSIEB18 AS RSIEB18," +
-            "gs.RSIEB19 AS RSIEB19, gs.RSIEB20 AS RSIEB20," +
-            "gs.RSIEB21 AS RSIEB21, gs.REST    AS REST " +
+            "COALESCE(m.TSCHWEB, 0)    AS TSCHWEB," +
+            "COALESCE(m.TSAND, 0)      AS TSAND," +
+            "COALESCE(gp.GTRIEB_F, 0)  AS GTRIEB," +
+            "COALESCE(m.TGESCHIEBE, 0) AS TGESCHIEBE," +
+            "COALESCE(si.SIEB01, 0) AS SIEB01, COALESCE(si.SIEB02, 0) AS SIEB02," +
+            "COALESCE(si.SIEB03, 0) AS SIEB03, COALESCE(si.SIEB04, 0) AS SIEB04," +
+            "COALESCE(si.SIEB05, 0) AS SIEB05, COALESCE(si.SIEB06, 0) AS SIEB06," +
+            "COALESCE(si.SIEB07, 0) AS SIEB07, COALESCE(si.SIEB08, 0) AS SIEB08," +
+            "COALESCE(si.SIEB09, 0) AS SIEB09, COALESCE(si.SIEB10, 0) AS SIEB10," +
+            "COALESCE(si.SIEB11, 0) AS SIEB11, COALESCE(si.SIEB12, 0) AS SIEB12," +
+            "COALESCE(si.SIEB13, 0) AS SIEB13, COALESCE(si.SIEB14, 0) AS SIEB14," +
+            "COALESCE(si.SIEB15, 0) AS SIEB15, COALESCE(si.SIEB16, 0) AS SIEB16," +
+            "COALESCE(si.SIEB17, 0) AS SIEB17, COALESCE(si.SIEB18, 0) AS SIEB18," +
+            "COALESCE(si.SIEB19, 0) AS SIEB19, COALESCE(si.SIEB20, 0) AS SIEB20," +
+            "COALESCE(si.SIEB21, 0) AS SIEB21," +
+            "COALESCE(gs.RSIEB01, 0) AS RSIEB01, COALESCE(gs.RSIEB02, 0) AS RSIEB02," +
+            "COALESCE(gs.RSIEB03, 0) AS RSIEB03, COALESCE(gs.RSIEB04, 0) AS RSIEB04," +
+            "COALESCE(gs.RSIEB05, 0) AS RSIEB05, COALESCE(gs.RSIEB06, 0) AS RSIEB06," +
+            "COALESCE(gs.RSIEB07, 0) AS RSIEB07, COALESCE(gs.RSIEB08, 0) AS RSIEB08," +
+            "COALESCE(gs.RSIEB09, 0) AS RSIEB09, COALESCE(gs.RSIEB10, 0) AS RSIEB10," +
+            "COALESCE(gs.RSIEB11, 0) AS RSIEB11, COALESCE(gs.RSIEB12, 0) AS RSIEB12," +
+            "COALESCE(gs.RSIEB13, 0) AS RSIEB13, COALESCE(gs.RSIEB14, 0) AS RSIEB14," +
+            "COALESCE(gs.RSIEB15, 0) AS RSIEB15, COALESCE(gs.RSIEB16, 0) AS RSIEB16," +
+            "COALESCE(gs.RSIEB17, 0) AS RSIEB17, COALESCE(gs.RSIEB18, 0) AS RSIEB18," +
+            "COALESCE(gs.RSIEB19, 0) AS RSIEB19, COALESCE(gs.RSIEB20, 0) AS RSIEB20," +
+            "COALESCE(gs.RSIEB21, 0) AS RSIEB21, COALESCE(gs.REST   , 0) AS REST " +
         "FROM MESSUNG m " +
             "JOIN STATION    s ON m.STATIONID    = s.STATIONID " +
             "JOIN GEWAESSER  r ON s.GEWAESSERID  = r.GEWAESSERID " +
@@ -190,9 +190,10 @@
     }
 
     public static Measurements getMeasurements(
-        String    river,
-        double    location,
-        DateRange dateRange
+        String     river,
+        double     location,
+        DateRange  dateRange,
+        SQ.Factory sqFactory
     ) {
         Session session = SedDBSessionHolder.HOLDER.get();
         try {
@@ -202,7 +203,7 @@
             List<Measurement> accumulated = loadFractions(
                 session, river, location, dateRange);
 
-            return new Measurements(totals, accumulated);
+            return new Measurements(totals, accumulated, sqFactory);
         }
         finally {
             session.close();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurements.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/Measurements.java	Fri Sep 27 17:36:50 2013 +0200
@@ -66,13 +66,17 @@
     protected List<Measurement> measuments;
     protected List<Measurement> accumulated;
 
+    protected SQ.Factory sqFactory;
+
     public Measurements() {
     }
 
     public Measurements(
         List<Measurement> measuments,
-        List<Measurement> accumulated
+        List<Measurement> accumulated,
+        SQ.Factory        sqFactory
     ) {
+        this.sqFactory = sqFactory;
         if (log.isDebugEnabled()) {
             log.debug("number of measuments: " + measuments.size());
             log.debug("number of accumulated: " + accumulated.size());
@@ -81,14 +85,14 @@
         this.accumulated = accumulated;
     }
 
-    public static List<SQ> extractSQ(
+    public List<SQ> extractSQ(
         List<Measurement> measuments,
         SExtractor extractor
     ) {
         List<SQ> result = new ArrayList<SQ>(measuments.size());
         int invalid = 0;
         for (Measurement measument: measuments) {
-            SQ sq = new SQ(extractor.getS(measument), measument.Q());
+            SQ sq = sqFactory.createSQ(extractor.getS(measument), measument.Q());
             if (sq.isValid()) {
                 result.add(sq);
             }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQ.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQ.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,8 +11,37 @@
 import java.io.Serializable;
 
 
+/** Represents S/Q pairs. They are immutable! */
 public class SQ implements Serializable {
 
+    public interface Factory {
+        SQ createSQ(double s, double q);
+    }
+
+    public static final Factory SQ_FACTORY = new Factory() {
+        @Override
+        public SQ createSQ(double s, double q) {
+            return new SQ(s, q);
+        }
+    };
+
+    public interface View {
+        double getS(SQ sq);
+        double getQ(SQ sq);
+    }
+
+    public static final View SQ_VIEW = new View() {
+        @Override
+        public double getS(SQ sq) {
+            return sq.getS();
+        }
+
+        @Override
+        public double getQ(SQ sq) {
+            return sq.getQ();
+        }
+    };
+
     protected double s;
     protected double q;
 
@@ -29,19 +58,10 @@
         return s;
     }
 
-    public void setS(double s) {
-        this.s = s;
-    }
-
-
     public double getQ() {
         return q;
     }
 
-    public void setQ(double q) {
-        this.q = q;
-    }
-
     public boolean isValid() {
         return !Double.isNaN(s) && !Double.isNaN(q);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationCalculation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationCalculation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,6 +8,7 @@
 
 package org.dive4elements.river.artifacts.model.sq;
 
+import org.dive4elements.artifacts.common.utils.StringUtils;
 import org.dive4elements.river.artifacts.access.SQRelationAccess;
 
 import org.dive4elements.river.artifacts.math.fitting.Function;
@@ -21,6 +22,7 @@
 import org.dive4elements.river.backend.SedDBSessionHolder;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.log4j.Logger;
@@ -30,7 +32,20 @@
     private static final Logger log =
         Logger.getLogger(SQRelationCalculation.class);
 
-    public static final String SQ_FUNCTION_NAME = "sq-pow";
+    public static final boolean NON_LINEAR_FITTING =
+        Boolean.getBoolean("minfo.sq.calcution.non.linear.fitting");
+
+    public static final String SQ_POW_FUNCTION_NAME = "sq-pow";
+    public static final String SQ_LIN_FUNCTION_NAME = "linear";
+
+    public static final String [] EXTRA_PARAMETERS = {
+        "chi_sqr",
+        "std_dev",
+        "max_q",
+        "c_ferguson",
+        "c_duan",
+        "r2"
+    };
 
     protected String    river;
     protected double    location;
@@ -49,8 +64,6 @@
         Double    outliers = access.getOutliers();
         String    method   = access.getOutlierMethod();
 
-        //river = "Rhein";
-
         if (river == null) {
             // TODO: i18n
             addProblem("sq.missing.river");
@@ -102,31 +115,95 @@
         }
     }
 
+    public interface TransformCoeffs {
+        double [] transform(double [] coeffs);
+    }
+
+    public static final TransformCoeffs IDENTITY_TRANS =
+        new TransformCoeffs() {
+            @Override
+            public double [] transform(double [] coeffs) {
+                return coeffs;
+            }
+        };
+
+    public static final TransformCoeffs LINEAR_TRANS =
+        new TransformCoeffs() {
+            @Override
+            public double [] transform(double [] coeffs) {
+                log.debug("before transform: " + Arrays.toString(coeffs));
+                if (coeffs.length == 2) {
+                    coeffs = new double [] { Math.exp(coeffs[1]), coeffs[0] };
+                }
+                log.debug("after transform: " + Arrays.toString(coeffs));
+                return coeffs;
+            }
+        };
+
     protected CalculationResult internalCalculate() {
 
-        Function function = FunctionFactory
+        Function powFunction = FunctionFactory
             .getInstance()
-            .getFunction(SQ_FUNCTION_NAME);
+            .getFunction(SQ_POW_FUNCTION_NAME);
 
-        if (function == null) {
-            log.error("No '" + SQ_FUNCTION_NAME + "' function found.");
+        if (powFunction == null) {
+            log.error("No '" + SQ_POW_FUNCTION_NAME + "' function found.");
             // TODO: i18n
             addProblem("sq.missing.sq.function");
+            return new CalculationResult(new SQResult[0], this);
+        }
+
+        Function         function;
+        SQ.View          sqView;
+        SQ.Factory       sqFactory;
+        ParameterCreator pc;
+
+        if (NON_LINEAR_FITTING) {
+            log.debug("Use non linear fitting.");
+            sqView    = SQ.SQ_VIEW;
+            sqFactory = SQ.SQ_FACTORY;
+            function  = powFunction;
+            pc = new ParameterCreator(
+                powFunction.getParameterNames(),
+                powFunction.getParameterNames(),
+                powFunction,
+                sqView);
+        }
+        else {
+            log.debug("Use linear fitting.");
+            sqView    = LogSQ.LOG_SQ_VIEW;
+            sqFactory = LogSQ.LOG_SQ_FACTORY;
+            function  = FunctionFactory
+                .getInstance()
+                .getFunction(SQ_LIN_FUNCTION_NAME);
+            if (function == null) {
+                log.error("No '" + SQ_LIN_FUNCTION_NAME + "' function found.");
+                // TODO: i18n
+                addProblem("sq.missing.sq.function");
+                return new CalculationResult(new SQResult[0], this);
+            }
+            pc = new LinearParameterCreator(
+                powFunction.getParameterNames(),
+                function.getParameterNames(),
+                function,
+                sqView);
         }
 
         Measurements measurements =
-            MeasurementFactory.getMeasurements(river, location, period);
+            MeasurementFactory.getMeasurements(
+                river, location, period, sqFactory);
 
         SQFractionResult [] fractionResults =
             new SQFractionResult[SQResult.NUMBER_FRACTIONS];
 
+
         for (int i = 0; i < fractionResults.length; ++i) {
             List<SQ> sqs = measurements.getSQs(i);
 
             SQFractionResult fractionResult;
 
             List<SQFractionResult.Iteration> iterations =
-                doFitting(function, sqs);
+                doFitting(function, sqs, sqView, pc);
 
             if (iterations == null) {
                 // TODO: i18n
@@ -148,13 +225,15 @@
     }
 
     protected List<SQFractionResult.Iteration> doFitting(
-        final Function function,
-        List<SQ> sqs
+        final Function         function,
+        List<SQ>               sqs,
+        SQ.View                sqView,
+        final ParameterCreator pc
     ) {
         final List<SQFractionResult.Iteration> iterations =
             new ArrayList<SQFractionResult.Iteration>();
 
-        boolean success = new Fitting(function, outliers).fit(
+        boolean success = new Fitting(function, outliers, sqView).fit(
             sqs,
             method,
             new Fitting.Callback() {
@@ -166,11 +245,11 @@
                     double    standardDeviation,
                     double    chiSqr
                 ) {
-                    Parameters parameters = createParameters(
-                        function.getParameterNames(),
+                    Parameters parameters = pc.createParameters(
                         coeffs,
                         standardDeviation,
-                        chiSqr);
+                        chiSqr,
+                        measurements);
                     iterations.add(new SQFractionResult.Iteration(
                         parameters,
                         measurements,
@@ -181,22 +260,178 @@
         return success ? iterations : null;
     }
 
-    public static final Parameters createParameters(
-        String [] names,
-        double [] values,
-        double    standardDeviation,
-        double    chiSqr
-    ) {
-        String [] columns = new String[names.length + 2];
-        columns[0] = "chi_sqr";
-        columns[1] = "std_dev";
-        System.arraycopy(names, 0, columns, 2, names.length);
-        Parameters parameters = new Parameters(columns);
-        int row = parameters.newRow();
-        parameters.set(row, names, values);
-        parameters.set(row, "chi_sqr", chiSqr);
-        parameters.set(row, "std_dev", standardDeviation);
-        return parameters;
+    public static class ParameterCreator {
+
+        protected String [] origNames;
+        protected String [] proxyNames;
+
+        protected Function  function;
+        protected SQ.View   view;
+
+        public ParameterCreator(
+            String [] origNames,
+            String [] proxyNames,
+            Function  function,
+            SQ.View   view
+        ) {
+            this.origNames  = origNames;
+            this.proxyNames = proxyNames;
+            this.function   = function;
+            this.view       = view;
+        }
+
+        protected double [] transformCoeffs(double [] coeffs) {
+            return coeffs;
+        }
+
+        private static double maxQ(SQ [] sqs) {
+            double max = -Double.MAX_VALUE;
+            for (SQ sq: sqs) {
+                double q = sq.getQ(); // Don't use view here!
+                if (q > max) {
+                    max = q;
+                }
+            }
+            return Math.max(0d, max);
+        }
+
+        private double cFerguson(
+            org.dive4elements.river.artifacts.math.Function instance,
+            SQ [] sqs
+        ) {
+            double sqrSum = 0d;
+
+            for (SQ sq: sqs) {
+                double s = view.getS(sq);
+                double q = view.getQ(sq);
+                double diffS = s - instance.value(q);
+                sqrSum += diffS*diffS;
+            }
+
+            return Math.exp(0.5d * sqrSum/(sqs.length-2));
+        }
+
+        private double cDuan(
+            org.dive4elements.river.artifacts.math.Function instance,
+            SQ [] sqs
+        ) {
+            double sum = 0d;
+
+            for (SQ sq: sqs) {
+                double s = view.getS(sq);
+                double q = view.getQ(sq);
+                double diffS = s - instance.value(q);
+                sum += Math.exp(diffS);
+            }
+            return sum / sqs.length;
+        }
+
+        private double r2(
+            org.dive4elements.river.artifacts.math.Function instance,
+            SQ [] sqs
+        ) {
+            double xm = 0;
+            double ym = 0;
+            for (SQ sq: sqs) {
+                double s  = view.getS(sq);
+                double q  = view.getQ(sq);
+                double fs = instance.value(q);
+                xm += s;
+                ym += fs;
+            }
+            xm /= sqs.length;
+            ym /= sqs.length;
+
+            double mixXY = 0d;
+            double sumX = 0d;
+            double sumY = 0d;
+
+            for (SQ sq: sqs) {
+                double s  = view.getS(sq);
+                double q  = view.getQ(sq);
+                double fs = instance.value(q);
+
+                double xDiff = xm - s;
+                double yDiff = ym - fs;
+
+                mixXY += xDiff*yDiff;
+
+                sumX += xDiff*xDiff;
+                sumY += yDiff*yDiff;
+            }
+
+            double r = mixXY/Math.sqrt(sumX*sumY);
+            return r*r;
+        }
+
+
+        public Parameters createParameters(
+            double [] coeffs,
+            double    standardDeviation,
+            double    chiSqr,
+            SQ []     measurements
+        ) {
+            String [] columns = StringUtils.join(EXTRA_PARAMETERS, origNames);
+
+            Parameters parameters = new Parameters(columns);
+            int row = parameters.newRow();
+            parameters.set(row, origNames, transformCoeffs(coeffs));
+            parameters.set(row, "chi_sqr", chiSqr);
+            parameters.set(row, "std_dev", standardDeviation);
+            parameters.set(row, "max_q", maxQ(measurements));
+
+            // We need to instantiate the function to calculate
+            // the remaining values.
+            org.dive4elements.river.artifacts.math.Function f =
+                function.instantiate(coeffs);
+
+            parameters.set(row, "c_ferguson", cFerguson(f, measurements));
+            parameters.set(row, "c_duan", cDuan(f, measurements));
+            parameters.set(row, "r2", r2(f, measurements));
+
+            return parameters;
+        }
+    }
+
+    /** We need to transform the coeffs back to the original function. */
+    public static class LinearParameterCreator extends ParameterCreator {
+
+        public LinearParameterCreator(
+            String [] origNames,
+            String [] proxyNames,
+            Function  function,
+            SQ.View   view
+        ) {
+            super(origNames, proxyNames, function, view);
+        }
+
+        @Override
+        protected double [] transformCoeffs(double [] coeffs) {
+
+            int bP = StringUtils.indexOf("m", proxyNames);
+            int mP = StringUtils.indexOf("b", proxyNames);
+
+            int aO = StringUtils.indexOf("a", origNames);
+            int bO = StringUtils.indexOf("b", origNames);
+
+            if (bP == -1 || mP == -1 || aO == -1 || bO == -1) {
+                log.error("index not found: "
+                    + bP + " " + mP + " " 
+                    + aO + " " + bO);
+                return coeffs;
+            }
+
+            double [] ncoeffs = (double [])coeffs.clone();
+            ncoeffs[aO] = Math.exp(coeffs[mP]);
+            ncoeffs[bO] = coeffs[bP];
+
+            if (log.isDebugEnabled()) {
+                log.debug("before transform: " + Arrays.toString(coeffs));
+                log.debug("after transform: " + Arrays.toString(ncoeffs));
+            }
+
+            return ncoeffs;
+        }
     }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationJRDataSource.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/model/sq/SQRelationJRDataSource.java	Fri Sep 27 17:36:50 2013 +0200
@@ -73,6 +73,12 @@
         else if ("periods".equals(fieldName)) {
             value = metaData.get("periods");
         }
+        else if ("msName".equals(fieldName)) {
+            value = metaData.get("msName");
+        }
+        else if ("msGauge".equals(fieldName)) {
+            value = metaData.get("msGauge");
+        }
         else if ("km".equals(fieldName)) {
             value = data.get(index)[0];
         }
@@ -86,12 +92,24 @@
             value = data.get(index)[3];
         }
         else if ("total".equals(fieldName)) {
-            value = data.get(index)[4];
+            value = data.get(index)[7];
         }
         else if ("out".equals(fieldName)) {
+            value = data.get(index)[8];
+        }
+        else if ("sd".equals(fieldName)) {
+            value = data.get(index)[4];
+        }
+        else if ("qmax".equals(fieldName)) {
             value = data.get(index)[5];
         }
-        else if ("variance".equals(fieldName)) {
+        else if ("cferg".equals(fieldName)) {
+            value = data.get(index)[10];
+        }
+        else if ("cduan".equals(fieldName)) {
+            value = data.get(index)[9];
+        }
+        else if ("r2".equals(fieldName)) {
             value = data.get(index)[6];
         }
         return value;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/D4EService.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/D4EService.java	Fri Sep 27 17:36:50 2013 +0200
@@ -44,6 +44,7 @@
     }
 
 
+    /** Override to do the meat work (called in processXML). */
     protected abstract Document doProcess(
         Document      data,
         GlobalContext globalContext,
@@ -56,6 +57,7 @@
     }
 
 
+    /** Called when processing done, close session. */
     protected void shutdown() {
         logger.debug("shutdown");
         Session session = SessionHolder.HOLDER.get();
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeInfoService.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/DischargeInfoService.java	Fri Sep 27 17:36:50 2013 +0200
@@ -40,6 +40,8 @@
 
     public static final String GAUGE_XPATH = "/art:gauge/text()";
 
+    public static final String RIVER_NAME_XPATH = "/art:gauge/art:river/text()";
+
     public DischargeInfoService() {
     }
 
@@ -58,30 +60,39 @@
         String gaugeNumber = XMLUtils.xpathString(
             data, GAUGE_XPATH, ArtifactNamespaceContext.INSTANCE);
 
+        String river = XMLUtils.xpathString(
+            data, RIVER_NAME_XPATH, ArtifactNamespaceContext.INSTANCE);
+
         if (gaugeNumber == null ||
            (gaugeNumber = gaugeNumber.trim()).length() == 0) {
             logger.warn("No gauge specified. Cannot return discharge info.");
             return XMLUtils.newDocument();
         }
 
-        logger.debug("Getting discharge for gauge: " + gaugeNumber);
+        logger.debug("Getting discharge for gauge: " + gaugeNumber + " at river: " + river);
 
         long gn;
         try {
             gn = Long.parseLong(gaugeNumber);
         }
         catch (NumberFormatException nfe) {
-            logger.warn("Invalid gauge number. Cannot return discharg info.");
+            logger.warn("Invalid gauge number. Cannot return discharge info.");
             return XMLUtils.newDocument();
         }
 
-        Gauge gauge = Gauge.getGaugeByOfficialNumber(gn);
+        Gauge gauge;
+        if (river == null || river.isEmpty()) {
+            gauge = Gauge.getGaugeByOfficialNumber(gn);
+        } else {
+            gauge = Gauge.getGaugeByOfficialNumber(gn, river);
+        }
+
         if (gauge == null) {
             logger.warn("No such gauge found.");
             return XMLUtils.newDocument();
         }
 
-        logger.debug("Found gauge: " + gauge.getName());
+        logger.debug("Found gauge: " + gauge.getName() + " id: " + gauge.getId());
 
         return buildDocument(gauge);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverInfoService.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/services/RiverInfoService.java	Fri Sep 27 17:36:50 2013 +0200
@@ -37,6 +37,7 @@
     protected River river;
     protected Element riverele;
 
+    @Override
     protected Document doProcess(
         Document      data,
         GlobalContext globalContext,
@@ -91,3 +92,4 @@
             ? Double.toString(value.doubleValue()) : "";
     }
 }
+// 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/CalculationSelect.java	Fri Sep 27 17:36:50 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/artifacts/states/FloodMapState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/FloodMapState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -30,6 +30,7 @@
 import org.dive4elements.artifacts.GlobalContext;
 import org.dive4elements.artifacts.common.utils.FileTools;
 import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.access.DGMAccess;
 import org.dive4elements.river.artifacts.access.RangeAccess;
 import org.dive4elements.river.artifacts.context.RiverContext;
 import org.dive4elements.river.artifacts.model.CalculationMessage;
@@ -412,7 +413,7 @@
                 MapfileGenerator.MS_LAYER_PREFIX + HWS_LINES,
                 HWS_LINES_SHAPE,
                 "LINE",
-                "31467",
+                "31467", // XXX: This should be dynamically fetched from database.
                 "hws");
             job.addLin(artifactDir + "/" + HWS_LINES_SHAPE);
             facetCreator.createShapeFacet(I18N_HWS_LINES_OFFICIAL,
@@ -536,16 +537,18 @@
         File         dir,
         WSPLGENJob   job
     ) {
-        String river   = artifact.getDataAsString("river");
-        String geoJSON = artifact.getDataAsString("uesk.barriers");
-        String srid    = RiverUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
+        DGMAccess access = new DGMAccess(artifact);
+        String geoJSON   = access.getGeoJSON();
 
         if (geoJSON == null || geoJSON.length() == 0) {
             logger.debug("No barrier features in parameterization existing.");
             return;
         }
 
+        String srid = String.valueOf(access.getDGM().getSrid());
+
+        String srs = "EPSG:" + srid;
+
         SimpleFeatureType ft = getBarriersFeatureType(
             "barriers", srs, Geometry.class);
 
@@ -748,9 +751,10 @@
 
 
     protected void setAxis(D4EArtifact artifact, File dir, WSPLGENJob job) {
-        String river = artifact.getDataAsString("river");
-        String srid    = RiverUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
+        DGMAccess access = new DGMAccess(artifact);
+        String river = access.getRiver();
+        String srid  = String.valueOf(access.getDGM().getSrid());
+        String srs   = "EPSG:" + srid;
 
         List<RiverAxis> axes = null;
         try {
@@ -794,9 +798,10 @@
 
 
     protected void setPro(D4EArtifact artifact, File dir, WSPLGENJob job) {
-        String river = artifact.getDataAsString("river");
-        String srid    = RiverUtils.getRiverDGMSrid(river);
-        String srs     = "EPSG:" + srid;
+        DGMAccess access = new DGMAccess(artifact);
+        String river = access.getRiver();
+        String srid  = String.valueOf(access.getDGM().getSrid());
+        String srs   = "EPSG:" + srid;
 
         List<CrossSectionTrack> cst =
             CrossSectionTrack.getCrossSectionTrack(river, WSPLGEN_QPS_NAME);
@@ -838,33 +843,25 @@
 
     protected void setDgm(
         D4EArtifact artifact,
-        WSPLGENJob job,
+        WSPLGENJob  job,
         CallContext context
     ) {
-        String dgm_id = artifact.getDataAsString("dgm");
-
-        int id = -1;
-        try {
-            id = Integer.parseInt(dgm_id);
-        }
-        catch (NumberFormatException nfe) { /* do nothing */ }
-
-        DGM dgm = DGM.getDGM(id);
+        DGMAccess access = new DGMAccess(artifact);
+        DGM dgm = access.getDGM();
 
         if (dgm == null) {
             logger.warn("Could not find specified DGM.");
-
             return;
         }
 
-        File dgmPath = new File (dgm.getPath());
+        File dgmPath = new File(dgm.getPath());
         if (dgmPath.isAbsolute()) {
             job.setDgm(dgm.getPath());
         }
         else {
             RiverContext fc = (RiverContext)context.globalContext();
-            String prefix = (String) fc.get("dgm-path");
-            job.setDgm(prefix.trim() + dgm.getPath().trim());
+            File prefix = new File((String)fc.get("dgm-path"));
+            job.setDgm(new File(prefix, dgm.getPath()).getAbsolutePath());
         }
     }
 
@@ -876,17 +873,29 @@
             return;
         }
 
-        String river = artifact.getDataAsString("river");
-        String srid  = RiverUtils.getRiverDGMSrid(river);
+        DGMAccess access = new DGMAccess(artifact);
+        String river = access.getRiver();
+        String srid  = String.valueOf(access.getDGM().getSrid());
         String srs   = "EPSG:" + srid;
 
         Floodplain plain = Floodplain.getFloodplain(river);
 
+        if (plain == null) {
+            logger.debug("No flood plain for river '" + river + "'");
+            return;
+        }
+
+        Polygon polygon = plain.getGeom();
+        if (polygon == null) {
+            logger.warn("Floodplain has no geometry.");
+            return;
+        }
+
         SimpleFeatureType ft = GeometryUtils.buildFeatureType(
             "talaue", srs, Polygon.class);
 
         SimpleFeatureBuilder builder = new SimpleFeatureBuilder(ft);
-        builder.add(plain.getGeom());
+        builder.add(polygon);
 
         FeatureCollection collection = FeatureCollections.newCollection();
         collection.add(builder.buildFeature("0"));
@@ -1002,8 +1011,5 @@
             }
         }
     }
-
-
-
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf-8 :
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeDischargeState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeDischargeState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,6 +10,8 @@
 
 import java.util.List;
 
+import java.text.DateFormat;
+
 import org.apache.log4j.Logger;
 
 import org.dive4elements.artifacts.CallMeta;
@@ -31,7 +33,10 @@
 import org.dive4elements.river.artifacts.resources.Resources;
 
 import org.dive4elements.river.model.Gauge;
+import org.dive4elements.river.model.DischargeTable;
+import org.dive4elements.river.model.TimeInterval;
 
+import org.dive4elements.river.utils.Formatter;
 
 /**
  * The only state for an GaugeDischargeState (River and km known).
@@ -64,9 +69,18 @@
         }
 
         Gauge gauge = artifact.getGauge();
+        DischargeTable mdt = gauge.fetchMasterDischargeTable();
+        TimeInterval validity = mdt.getTimeInterval();
+        DateFormat df = Formatter.getDateFormatter(meta, "dd.MM.yyyy");
+        String start = validity.getStartTime() != null ?
+                         df.format(validity.getStartTime()) : "Unknown";
+        String stop = validity.getStopTime() != null ?
+                          df.format(validity.getStopTime()) : "";
+
         Object[] args = new Object[] {
             gauge.getName(),
-            gauge.getStation()
+            start,
+            stop
         };
 
         String name = Resources.getMsg(
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeTimerangeState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/GaugeTimerangeState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -67,7 +67,8 @@
             }
         }
 
-        logger.warn("Could not determine time range for gauge: " + gauge);
+        logger.warn("Could not determine time range for gauge: " + gauge.getName()
+                + " id: " + gauge.getId());
 
         return null;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -80,6 +80,7 @@
         return staticCompute(facets);
     }
 
+    /** End-point and most important compute-method. Override for desired effect. */
     public Object staticCompute(List<Facet> facets) {
         return null;
     }
@@ -133,7 +134,7 @@
     }
 
     /**
-     * Allow to set the uiprovider for displaying the static data
+     * Allow to set the uiprovider for displaying the static data.
      */
     public void setUIProvider(String uiprovider) {
         this.uiprovider = uiprovider;
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticWQKmsState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/StaticWQKmsState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,6 +10,8 @@
 
 import java.util.List;
 
+import gnu.trove.TDoubleArrayList;
+
 import org.apache.log4j.Logger;
 
 import org.dive4elements.artifacts.CallContext;
@@ -79,18 +81,30 @@
                name = STATIC_WQKMS;
            }
         */
+        // Spawn Q Facet only if at least one discharge value
+        // is != -1
+        boolean qEmpty = true;
+        TDoubleArrayList qs = wqkms.allQs();
+        for (int i = 0; i < qs.size(); i++) {
+            if (qs.getQuick(i) != -1d) {
+                qEmpty = false;
+                break;
+            }
+        }
 
         String wkmsName = wqkms.getName();
-        Facet qfacet = new WQKmsFacet(
-            STATIC_WQKMS_Q,
-            wkmsName
-            // TODO re-enable translations.
-            /*
-            Resources.getMsg(
-                metaLocale,
-                wkmsName,
-                wkmsName)*/);
-        facets.add(qfacet);
+        if (!qEmpty) {
+            Facet qfacet = new WQKmsFacet(
+                STATIC_WQKMS_Q,
+                wkmsName
+                // TODO re-enable translations.
+                /*
+                Resources.getMsg(
+                    metaLocale,
+                    wkmsName,
+                    wkmsName)*/);
+            facets.add(qfacet);
+        }
 
         Facet rpFacet = new RelativePointFacet(wkmsName);
         facets.add(rpFacet);
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WDifferencesState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/WDifferencesState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -49,10 +49,6 @@
     private static Logger logger = Logger.getLogger(WDifferencesState.class);
 
 
-    public WDifferencesState() {
-    }
-
-
     /** Specify to display nothing (this is kind of a "final" state). */
     @Override
     protected String getUIProvider() {
@@ -65,6 +61,9 @@
     throws IllegalArgumentException
     {
         D4EArtifact flys = (D4EArtifact) artifact;
+        if (artifact instanceof ChartArtifact) {
+            return true;
+        }
 
         StateData data = flys.getData("diffids");
 
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/fixation/FixAnalysisCompute.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,6 +9,7 @@
 package org.dive4elements.river.artifacts.states.fixation;
 
 import java.text.DateFormat;
+import java.util.Collection;
 import java.util.Date;
 import java.util.List;
 
@@ -41,7 +42,9 @@
 import org.dive4elements.river.artifacts.model.fixings.FixWQCurveFacet;
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.artifacts.states.DefaultState;
+import org.dive4elements.river.utils.Formatter;
 import org.dive4elements.river.utils.IdGenerator;
+import org.dive4elements.river.utils.UniqueDateFormatter;
 
 /**
  * @author <a href="mailto:raimund.renkert@intevation.de">Raimund Renkert</a>
@@ -76,8 +79,6 @@
         "fix.hq5"
     };
 
-    // TODO Why does this happen here? In other cases its implemented in the
-    //      respective artifact, not State.
     static {
         // Active/deactivate facets.
         FacetActivity.Registry.getInstance().register(
@@ -171,15 +172,15 @@
         int qsS = access.getQSectorStart();
         int qsE = access.getQSectorEnd();
 
-        // TODO: i18n
-        DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM);
+        DateFormat df = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy");
+        DateFormat lf = Formatter.getDateFormatter(context.getMeta(), "dd.MM.yyyy'T'HH:mm");
 
         DateRange [] periods = access.getAnalysisPeriods();
 
         for (int i = 0; i < periods.length; i++) {
             DateRange period = periods[i];
             String startDate = df.format(period.getFrom());
-            String endDate = df.format(period.getTo());
+            String endDate   = df.format(period.getTo());
 
             for (int j = qsS; j <= qsE; j++) {
 
@@ -233,19 +234,22 @@
                                  I18N_ANALYSIS,
                                  I18N_ANALYSIS);
 
+            Collection<Date> aeds = fr.getAnalysisEventsDates(i);
+            UniqueDateFormatter cf = new UniqueDateFormatter(df, lf, aeds);
+
             int k = 0;
-            for (Date d: fr.getAnalysisEventsDates(i)) {
+            for (Date d: aeds) {
                 int anaNdx = i << 8;
                 anaNdx = anaNdx | k;
                 facets.add(new FixAnalysisEventsFacet(anaNdx,
                     FIX_ANALYSIS_EVENTS_DWT,
-                    eventDesc + (i+1) + " - " + df.format(d)));
+                    eventDesc + (i+1) + " - " + cf.format(d)));
                 facets.add(new FixLongitudinalAnalysisFacet(anaNdx,
                     FIX_ANALYSIS_EVENTS_LS,
-                    eventDesc + (i+1) + " - " + df.format(d)));
+                    eventDesc + (i+1) + " - " + cf.format(d)));
                 facets.add(new FixAnalysisEventsFacet(anaNdx,
                     FIX_ANALYSIS_EVENTS_WQ,
-                    eventDesc + (i+1) +" - " + df.format(d)));
+                    eventDesc + (i+1) +" - " + cf.format(d)));
                 k++;
             }
         }
@@ -259,27 +263,29 @@
                 I18N_REFERENCEDEVIATION,
                 I18N_REFERENCEDEVIATION);
 
+        Collection<Date> reds = fr.getReferenceEventsDates();
+        UniqueDateFormatter cf = new UniqueDateFormatter(df, lf, reds);
+
         int i = 0;
-        for (Date d: fr.getReferenceEventsDates()) {
+        for (Date d: reds) {
             int refNdx = idg.next() << 8;
             refNdx |=  i;
             facets.add(new FixReferenceEventsFacet(refNdx,
                 FIX_REFERENCE_EVENTS_DWT,
-                i18n_ref + " - " + df.format(d)));
+                i18n_ref + " - " + cf.format(d)));
             refNdx = idg.next() << 8;
             refNdx = refNdx | i;
             facets.add(new FixLongitudinalReferenceFacet(refNdx,
                 FIX_REFERENCE_EVENTS_LS,
-                i18n_ref + " - " + df.format(d)));
+                i18n_ref + " - " + cf.format(d)));
             refNdx = idg.next() << 8;
             refNdx |= i;
             facets.add(new FixReferenceEventsFacet(refNdx,
                 FIX_REFERENCE_EVENTS_WQ,
-                i18n_ref + " - " + df.format(d)));
+                i18n_ref + " - " + cf.format(d)));
             i++;
         }
 
-
         facets.add(new FixLongitudinalDeviationFacet(idg.next(),
             FIX_DEVIATION_LS,
             i18n_dev));
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferencesState.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/DifferencesState.java	Fri Sep 27 17:36:50 2013 +0200
@@ -98,8 +98,14 @@
         return res;
     }
 
-    protected void generateFacets(CallContext context, List<Facet> newFacets,
-        BedDifferencesResult[] results, String stateId, String hash) {
+    /** Generate Facets based on given results. */
+    protected void generateFacets(
+        CallContext context,
+        List<Facet> newFacets,
+        BedDifferencesResult[] results,
+        String stateId,
+        String hash
+    ) {
         logger.debug("DifferencesState.generateFacets");
 
         CallMeta meta = context.getMeta();
@@ -121,9 +127,7 @@
                 newFacets.add(new BedDiffYearFacet(
                     idx,
                     BED_DIFFERENCE_MORPH_WIDTH,
-                    createBedDiffMorphDescription(
-                        meta,
-                        (BedDiffYearResult)results[idx]),
+                    createBedDiffMorphDescription(meta),
                     ComputeType.ADVANCE,
                     stateId,
                     hash));
@@ -336,12 +340,9 @@
     }
 
     protected String createBedDiffMorphDescription(
-        CallMeta meta,
-        BedDiffYearResult result) {
-        String range = result.getStart() + " - " + result.getEnd();
-
+        CallMeta meta) {
         return Resources.getMsg(meta, I18N_FACET_BED_DIFF_MORPH,
-            I18N_FACET_BED_DIFF_MORPH, new Object[] { range });
+            I18N_FACET_BED_DIFF_MORPH);
     }
 
     protected String createBedDiffAbsoluteDescription(
--- a/artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadCalculate.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/artifacts/states/minfo/SedimentLoadCalculate.java	Fri Sep 27 17:36:50 2013 +0200
@@ -140,10 +140,6 @@
             return res;
         }
 
-        String river = access.getRiver();
-        SedimentLoad[] unknown =
-            SedimentLoadFactory.getSedimentLoadUnknown(river, access.getUnit().replace("_per_","/"));
-
         String type = access.getYearEpoch();
         if (type.equals("year")) {
             generateYearFacets(context, newFacets, results, getID(), hash);
@@ -155,6 +151,12 @@
             generateOffEpochFacets(context, newFacets, results, getID(), hash);
         }
         logger.debug("Created " + newFacets.size() + " new Facets.");
+
+        String river = access.getRiver();
+        SedimentLoad[] unknown =
+            SedimentLoadFactory.getSedimentLoadUnknown(river,
+                access.getUnit().replace("_per_","/"), type);
+
         if (res.getReport().hasProblems()) {
             newFacets.add(new ReportFacet(ComputeType.ADVANCE, hash, id));
         }
--- a/artifacts/src/main/java/org/dive4elements/river/collections/AttributeParser.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/collections/AttributeParser.java	Fri Sep 27 17:36:50 2013 +0200
@@ -51,6 +51,7 @@
     protected CollectionAttribute attribute;
 
 
+    /** Just store reference to document. */
     public AttributeParser(Document attributeDocument) {
         this.attributeDocument = attributeDocument;
     }
--- a/artifacts/src/main/java/org/dive4elements/river/collections/AttributeWriter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/collections/AttributeWriter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -195,7 +195,11 @@
                 logger.debug("Try to add Facet: " + facet.getName());
             }
 
-            if (!compatibleFacets.contains(facet.getName())) {
+            String bondage = facet.getBoundToOut();
+            if (bondage != null && bondage.equals(outputName)) {
+                logger.debug("Adding bound facet regardless of compatibility: " +
+                    facet.getName());
+            } else if (!compatibleFacets.contains(facet.getName())) {
                 logger.debug("Have incompatible facet, skip: " + facet.getName());
                 continue;
             } else if (facet.getBoundToOut() != null &&
@@ -211,7 +215,12 @@
             ManagedFacet picked = pickFacet(facet, oldFacets);
 
             if (facet.equals(picked)) {
-                genuinelyNewFacets.add(picked);
+                if (!facetInTwoOuts(facet, genuinelyNewFacets)) {
+                    genuinelyNewFacets.add(picked);
+                }
+                else {
+                    logger.debug("Skip clone facet that shall be present in two outs");
+                }
             }
             else {
                 currentFacets.add(picked);
@@ -260,17 +269,20 @@
         // Preparations to be able to detect gaps.
         Map<Integer, ManagedFacet> mfmap =
             new HashMap<Integer, ManagedFacet>();
-        int max = 0;
+        int maxPosition = 0;
         for (ManagedFacet mf: currentFacets) {
             int pos = mf.getPosition();
             mfmap.put(Integer.valueOf(pos), mf);
-            if (pos > max) max = pos;
+            if (pos > maxPosition) maxPosition = pos;
         }
 
-        // Finally do gap correction.
-        if (max != currentFacets.size()) {
+        // TODO issue1458: debug what happens
+
+        // Finally do gap correction
+        // (note that posistions start at 1, not at zero).
+        if (maxPosition != currentFacets.size()) {
             int gap = 0;
-            for (int i = 1; i <= max; i++) {
+            for (int i = 1; i <= maxPosition; i++) {
                 ManagedFacet mf = mfmap.get(Integer.valueOf(i));
                 if (mf == null) {
                     gap++;
@@ -281,14 +293,27 @@
         }
 
         // Now add all facets.
-        for (ManagedFacet oldMF: currentFacets) {
-            attribute.addFacet(outputName, oldMF);
+        for (ManagedFacet facet: currentFacets) {
+            attribute.addFacet(outputName, facet);
         }
 
         return !currentFacets.isEmpty();
     }
 
 
+    /** Returns true if a likely clone of facet is
+     * contained in genuinelyNewFacets, as happens when same facet is defined
+     * for two outs. */
+    private boolean facetInTwoOuts(ManagedFacet facet, List<ManagedFacet> genuinelyNewFacets) {
+        for (ManagedFacet otherFacet: genuinelyNewFacets) {
+            if (facet.isSame(otherFacet)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
     /**
      * Returns the facet to be added to Document.
      * Return the new facet only if the "same" facet was not present before.
--- a/artifacts/src/main/java/org/dive4elements/river/collections/CollectionAttribute.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/collections/CollectionAttribute.java	Fri Sep 27 17:36:50 2013 +0200
@@ -30,7 +30,11 @@
 import org.dive4elements.artifactdatabase.state.Settings;
 
 
-/** Create attribute part of collection document. */
+/**
+ * Create attribute part of collection document.
+ *
+ * Has outputs, settings, facets and list of loaded recommendations.
+ */
 public class CollectionAttribute {
 
     /** Privately owned logger. */
@@ -66,6 +70,7 @@
     }
 
 
+    /** Remove outputs without facets from outputMap. */
     public void cleanEmptyOutputs() {
         if (outputMap == null) {
             return;
@@ -95,14 +100,14 @@
         }
 
         if (outputMap == null) {
-            logger.warn("Tried to add facet but no Outputs are existing yet.");
+            logger.warn("Tried to add settings but no Outputs are existing yet.");
             return;
         }
 
         Output output = outputMap.get(outputKey);
 
         if (output == null) {
-            logger.warn("Tried to add facet for unknown Output: " + outputKey);
+            logger.warn("Tried to add settings for unknown Output: " + outputKey);
             return;
         }
 
@@ -112,7 +117,7 @@
 
     public void addFacet(String outputKey, Facet facet) {
         if (facet == null) {
-            logger.warn("Tried to add empty facet.");
+            logger.warn("Tried to add null facet.");
             return;
         }
 
@@ -139,6 +144,7 @@
     }
 
 
+    /** Empty facets list for outputKey output. */
     public void clearFacets(String outputKey) {
         if (outputKey == null || outputKey.length() == 0) {
             logger.warn("Tried to clear Facets, but no Output key specified!");
--- a/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/collections/D4EArtifactCollection.java	Fri Sep 27 17:36:50 2013 +0200
@@ -74,6 +74,22 @@
     public static final String XPATH_LOADED_RECOMMENDATIONS =
         "/art:attribute/art:loaded-recommendations";
 
+    private CallContext context;
+
+    private ArtifactDatabase db;
+
+    protected CallContext getContext() {
+        return this.context;
+    }
+
+    protected ArtifactDatabase getArtifactDB() {
+        return this.db;
+    }
+
+    protected void setContext(CallContext context) {
+        this.context = context;
+        this.db = context.getDatabase();
+    }
 
     /**
      * Create and return description Document for this collection.
@@ -82,26 +98,26 @@
     public Document describe(CallContext context) {
         log.debug("D4EArtifactCollection.describe: " + identifier);
 
+        setContext(context);
+
         CollectionDescriptionHelper helper = new CollectionDescriptionHelper(
             getName(), identifier(), getCreationTime(), getTTL(),
             context);
 
-        ArtifactDatabase db = context.getDatabase();
 
         Document        oldAttrs = getAttribute();
         AttributeParser parser   = new AttributeParser(oldAttrs);
 
         try {
-            String[] aUUIDs  = getArtifactUUIDs(context);
+            String[] aUUIDs  = getArtifactUUIDs();
 
-            oldAttrs = removeAttributes(oldAttrs, context);
+            oldAttrs = removeAttributes(oldAttrs);
             parser   = new AttributeParser(oldAttrs);
 
-            CollectionAttribute newAttr = mergeAttributes(
-                db, context, parser, aUUIDs);
+            CollectionAttribute newAttr = mergeAttributes(parser, aUUIDs);
 
-            if (checkOutputSettings(newAttr, context)) {
-                saveCollectionAttribute(db, context, newAttr);
+            if (checkOutputSettings(newAttr)) {
+                saveCollectionAttribute(newAttr);
             }
 
             helper.setAttribute(newAttr);
@@ -129,13 +145,11 @@
      * @param uuids Artifact uuids.
      */
     protected CollectionAttribute mergeAttributes(
-        ArtifactDatabase db,
-        CallContext      context,
         AttributeParser  oldParser,
         String[]         uuids
     ) {
         CollectionAttribute cAttribute =
-            buildOutAttributes(db, context, oldParser, uuids);
+            buildOutAttributes(oldParser, uuids);
 
         if (cAttribute == null) {
             log.warn("mergeAttributes: cAttribute == null");
@@ -145,13 +159,19 @@
         cAttribute.setLoadedRecommendations(
             getLoadedRecommendations(oldParser.getAttributeDocument()));
 
-        saveCollectionAttribute(db, context, cAttribute);
+        saveCollectionAttribute(cAttribute);
 
         return cAttribute;
     }
 
 
-    protected Document removeAttributes(Document attrs, CallContext context) {
+    /**
+     * Remove those output-elements which have a name that does
+     * not appear in master artifacts out-list.
+     * @param attr[in,out] Document to clean and return.
+     * @return param attr.
+     */
+    protected Document removeAttributes(Document attrs) {
         Node outs = (Node) XMLUtils.xpath(
             attrs,
             "/art:attribute/art:outputs",
@@ -167,7 +187,7 @@
         if (nodes != null) {
             for (int i = 0; i < nodes.getLength(); i++) {
                 Element e = (Element)nodes.item(i);
-                if(!outputExists(e.getAttribute("name"), context)) {
+                if(!outputExists(e.getAttribute("name"))) {
                     outs.removeChild(e);
                 }
             }
@@ -182,9 +202,9 @@
      * @param context current context
      * @return true if current master artifact has given output.
      */
-    protected boolean outputExists(String name, CallContext context) {
-        D4EArtifact master = getMasterArtifact(context);
-        List<Output> outList = master.getOutputs(context);
+    protected boolean outputExists(String name) {
+        D4EArtifact master = getMasterArtifact();
+        List<Output> outList = master.getOutputs(getContext());
 
         for (Output o : outList) {
             if (name.equals(o.getName())) {
@@ -203,8 +223,6 @@
      * @return true, if the transaction was successful, otherwise false.
      */
     protected boolean saveCollectionAttribute(
-        ArtifactDatabase    db,
-        CallContext         context,
         CollectionAttribute attribute
     ) {
         log.info("Save new CollectionAttribute into database.");
@@ -213,7 +231,7 @@
 
         try {
             // Save the merged document into database.
-            db.setCollectionAttribute(identifier(), context.getMeta(), doc);
+            getArtifactDB().setCollectionAttribute(identifier(), getContext().getMeta(), doc);
 
             log.info("Saving CollectionAttribute was successful.");
 
@@ -254,8 +272,7 @@
      * @return true, if the CollectionAttribute was modified, otherwise false.
      */
     protected boolean checkOutputSettings(
-        CollectionAttribute attribute,
-        CallContext         cc
+        CollectionAttribute attribute
     ) {
         boolean modified = false;
 
@@ -281,7 +298,7 @@
             if (settings == null) {
                 log.debug("No Settings set for Output '" + outName + "'.");
                 output.setSettings(
-                    createInitialOutputSettings(cc, attribute, outName));
+                    createInitialOutputSettings(attribute, outName));
 
                 modified = true;
             }
@@ -302,11 +319,10 @@
      * @return a default Settings object for the specified Output.
      */
     protected Settings createInitialOutputSettings(
-        CallContext         cc,
         CollectionAttribute attr,
         String              out
     ) {
-        OutGenerator outGen = RiverContext.getOutGenerator(cc, out, null);
+        OutGenerator outGen = RiverContext.getOutGenerator(getContext(), out, null);
 
         if (outGen == null) {
             return null;
@@ -314,13 +330,13 @@
 
         // XXX NOTE: the outGen is not able to process its generate() operation,
         // because it has no OutputStream set!
-        outGen.init(XMLUtils.newDocument(), null, cc);
-        prepareMasterArtifact(outGen, cc);
+        outGen.init(out, XMLUtils.newDocument(), null, getContext());
+        prepareMasterArtifact(outGen);
 
         try {
-            Document outAttr = getAttribute(cc, attr, out);
+            Document outAttr = getAttribute(attr, out);
             OutputHelper helper = new OutputHelper(identifier());
-            helper.doOut(outGen, out, out, outAttr, cc);
+            helper.doOut(outGen, out, out, outAttr, getContext());
         }
         catch (ArtifactDatabaseException adbe) {
             log.error(adbe, adbe);
@@ -343,6 +359,8 @@
     {
         boolean debug = log.isDebugEnabled();
 
+        setContext(context);
+
         long reqBegin = System.currentTimeMillis();
 
         if (debug) {
@@ -362,16 +380,14 @@
             log.debug("-> Output subtype = " + subtype);
         }
 
-        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);
-        }
+        // If type contains 'chartinfo' use a generator that
+        // just allow access to width, height etc.
+
+        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);
@@ -397,13 +413,13 @@
             }
         }
 
-        generator.init(format, out, context);
+        generator.init(key, format, out, context);
         generator.setSettings(settings);
         generator.setCollection(this);
-        prepareMasterArtifact(generator, context);
+        prepareMasterArtifact(generator);
 
         try {
-            Document attr = getAttribute(context, cAttr, name);
+            Document attr = getAttribute(cAttr, name);
             OutputHelper helper = new OutputHelper(identifier());
             if (name.equals("sq_overview")) {
                 helper.doOut(generator, name, subtype, format, context);
@@ -426,12 +442,11 @@
      * Sets the master Artifact at the given <i>generator</i>.
      *
      * @param generator The generator that gets a master Artifact.
-     * @param cc The CallContext.
      */
-    protected void prepareMasterArtifact(OutGenerator generator, CallContext cc
+    protected void prepareMasterArtifact(OutGenerator generator
     ) {
         // Get master artifact.
-        D4EArtifact master = getMasterArtifact(cc);
+        D4EArtifact master = getMasterArtifact();
         if (master != null) {
             log.debug("Set master Artifact to uuid: " + master.identifier());
             generator.setMasterArtifact(master);
@@ -445,18 +460,18 @@
     /**
      * @return masterartifact or null if exception/not found.
      */
-    protected D4EArtifact getMasterArtifact(CallContext context)
+    protected D4EArtifact getMasterArtifact()
     {
         try {
-            ArtifactDatabase db = context.getDatabase();
-            CallMeta callMeta   = context.getMeta();
+            ArtifactDatabase db = getArtifactDB();
+            CallMeta callMeta   = getContext().getMeta();
             Document document   = db.getCollectionsMasterArtifact(
                 identifier(), callMeta);
 
             String masterUUID   = XMLUtils.xpathString(
                 document, XPATH_MASTER_UUID, ArtifactNamespaceContext.INSTANCE);
             D4EArtifact masterArtifact =
-                (D4EArtifact) getArtifact(masterUUID, context);
+                (D4EArtifact) getArtifact(masterUUID);
             return masterArtifact;
         }
         catch (ArtifactDatabaseException ade) {
@@ -471,8 +486,6 @@
      * @param uuids List of artifact uuids.
      */
     protected CollectionAttribute buildOutAttributes(
-        ArtifactDatabase db,
-        CallContext      context,
         AttributeParser  aParser,
         String[]         uuids)
     {
@@ -485,14 +498,16 @@
             return null;
         }
 
-        D4EArtifact masterArtifact = getMasterArtifact(context);
+        D4EArtifact masterArtifact = getMasterArtifact();
 
         if (masterArtifact == null) {
             log.debug("buildOutAttributes: masterArtifact == null");
             return null;
         }
 
-        OutputParser oParser = new OutputParser(db, context);
+        OutputParser oParser = new OutputParser(
+            getArtifactDB(),
+            getContext());
 
         if (uuids != null) {
             for (String uuid: uuids) {
@@ -508,7 +523,7 @@
         aParser.parse();
 
         AttributeWriter aWriter = new AttributeWriter(
-            db,
+            getArtifactDB(),
             aParser.getCollectionAttribute(),
             aParser.getOuts(),
             aParser.getFacets(),
@@ -531,7 +546,6 @@
      * @return the attribute for the desired output type.
      */
     protected Document getAttribute(
-        CallContext         context,
         CollectionAttribute cAttr,
         String              output)
     throws    ArtifactDatabaseException
@@ -570,13 +584,13 @@
      *
      * @return a list of uuids.
      */
-    protected String[] getArtifactUUIDs(CallContext context)
+    protected String[] getArtifactUUIDs()
     throws    ArtifactDatabaseException
     {
         log.debug("D4EArtifactCollection.getArtifactUUIDs");
 
-        ArtifactDatabase db = context.getDatabase();
-        CallMeta meta       = context.getMeta();
+        ArtifactDatabase db = getArtifactDB();
+        CallMeta meta       = getContext().getMeta();
 
         Document itemList = db.listCollectionArtifacts(identifier(), meta);
         NodeList items    = (NodeList) XMLUtils.xpath(
@@ -617,7 +631,7 @@
      *
      * @return an Artifact.
      */
-    protected Artifact getArtifact(String uuid, CallContext context)
+    protected Artifact getArtifact(String uuid)
     throws    ArtifactDatabaseException
     {
         log.debug("D4EArtifactCollection.getArtifact");
@@ -629,7 +643,7 @@
     }
 
     /**
-     * Returns artifacts that name facetName.
+     * Returns artifacts with name name.
      *
      * @param name The Artifact name to search
      * @param context The CallContext
@@ -638,11 +652,25 @@
      */
     public List<Artifact> getArtifactsByName(String name, CallContext context)
     {
+        setContext(context);
+        return getArtifactsByName(name);
+    }
+
+
+    /**
+     * Returns artifacts with name name.
+     *
+     * @param name The Artifact name to search
+     *
+     * @return a list of artifacts matching this name.
+     */
+    protected List<Artifact> getArtifactsByName(String name)
+    {
         log.debug("Searching for Artifacts: " + name);
         List<Artifact> ret =  new ArrayList<Artifact>();
         try {
-            for (String uuid: getArtifactUUIDs(context)) {
-                D4EArtifact subArt = (D4EArtifact)getArtifact(uuid, context);
+            for (String uuid: getArtifactUUIDs()) {
+                D4EArtifact subArt = (D4EArtifact) getArtifact(uuid);
                 if (subArt.getName() != null && subArt.getName().equals(name)) {
                     ret.add(subArt);
                 }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ATExporter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -30,6 +30,7 @@
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.model.River;
 import org.dive4elements.river.model.TimeInterval;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 import org.dive4elements.river.artifacts.access.RangeAccess;
 
@@ -43,7 +44,8 @@
     protected WQ           data;
     protected CallContext  context;
     protected OutputStream out;
-    protected D4EArtifact master;
+    protected D4EArtifact  master;
+    protected String       outName;
 
     protected D4EArtifactCollection collection;
 
@@ -52,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;
     }
@@ -71,8 +79,8 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactf,
-        Document attr,
-        boolean  visible
+        ThemeDocument    attr,
+        boolean          visible
     ) {
         data = (WQ)artifactf.getData(context);
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/AbstractExporter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -32,6 +32,7 @@
 import org.dive4elements.river.artifacts.resources.Resources;
 import org.dive4elements.river.collections.D4EArtifactCollection;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.Formatter;
 
 
@@ -59,11 +60,14 @@
     public static final String DEFAULT_CSV_CHARSET = "UTF-8";
 
     /** The default separator for the CSV export. */
-    public static final char DEFAULT_CSV_SEPARATOR = ',';
+    public static final char DEFAULT_CSV_SEPARATOR = ';';
 
     /** 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;
 
@@ -114,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;
@@ -158,7 +172,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String name = artifactFacet.getFacetName();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartExportHelper.java	Fri Sep 27 17:36:50 2013 +0200
@@ -28,6 +28,9 @@
 import java.io.OutputStream;
 import java.io.OutputStreamWriter;
 import java.io.UnsupportedEncodingException;
+
+import java.text.NumberFormat;
+
 import org.jfree.chart.ChartRenderingInfo;
 
 import javax.imageio.ImageIO;
@@ -47,6 +50,8 @@
 
 import org.dive4elements.artifacts.common.utils.XMLUtils;
 
+import org.dive4elements.river.utils.Formatter;
+
 
 /**
  * This class is a helper class which supports some methods to export charts
@@ -75,7 +80,7 @@
     public static final String  DEFAULT_ENCODING  = "UTF-8";
 
     /** The default separator for the CSV export. */
-    public static final char DEFAULT_CSV_SEPARATOR = ',';
+    public static final char DEFAULT_CSV_SEPARATOR = ';';
 
 
     /**
@@ -300,6 +305,9 @@
             log.warn("Wrong encoding for CSV export.");
             return;
         }
+
+        NumberFormat format = Formatter.getCSVFormatter(context);
+
         XYPlot plot = chart.getXYPlot();
         int count = plot.getDatasetCount();
         for (int i = 0; i < count; i++) {
@@ -309,7 +317,7 @@
                 Comparable seriesKey = data.getSeriesKey(j);
                 log.debug("series key: " + seriesKey.toString());
                 writeCSVHeader(writer, seriesKey.toString());
-                writeCSVData(writer, data);
+                writeCSVData(writer, data, format);
             }
         }
         try {
@@ -329,15 +337,41 @@
     }
 
 
-    protected static void writeCSVData(CSVWriter writer, XYDataset data) {
+    /** Get x/y data from axis set and write it, on pair per line. */
+    protected static void writeCSVData(
+        CSVWriter writer, XYDataset data, NumberFormat format) {
         int series = data.getSeriesCount();
         for (int i = 0; i < series; i++) {
             int items = data.getItemCount(i);
             for (int j = 0; j < items; j++) {
                 log.debug("write data: " + data.getX(i, j) + ", " + data.getY(i, j));
+
+                /*
+                // Skip (NaN,NaN) datapoints.
+                if (java.lang.Double.isNaN(data.getYValue(i,j))
+                    && java.lang.Double.isNaN(data.getXValue(i,j))) {
+                    continue;
+                }
+                */
+
+                String xString;
+                String yString;
+
+                try {
+                    xString = java.lang.Double.isNaN(data.getXValue(i,j))
+                        ? ""
+                        : format.format(data.getX(i, j));
+                    yString = java.lang.Double.isNaN(data.getYValue(i, j))
+                        ? ""
+                        : format.format(data.getY(i, j));
+                }
+                catch (NumberFormatException nfe) {
+                    xString = data.getX(i, j).toString();
+                    yString = data.getY(i, j).toString();
+                }
                 writer.writeNext(new String[] {
-                    data.getX(i, j).toString(),
-                    data.getY(i, j).toString()});
+                    xString,
+                    yString});
             }
         }
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -30,12 +30,12 @@
 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;
-import org.dive4elements.river.themes.ThemeAccess;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
-import org.dive4elements.river.utils.ThemeUtil;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -137,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.
@@ -150,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
-
 
 
     /**
@@ -179,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.
@@ -198,7 +184,7 @@
         ChartArea area,
         LineStyle lineStyle,
         TextStyle textStyle,
-        Document theme
+        ThemeDocument theme
     ) {
         // OPTIMIZE pre-calculate area-related values
         final float TEXT_OFF = 0.03f;
@@ -249,7 +235,7 @@
                     area2, annotation.getPos(), lineStyle);
                 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
                     // New line annotation to hit curve.
-                    if (ThemeUtil.parseShowVerticalLine(theme)) {
+                    if (theme.parseShowVerticalLine()) {
                         XYLineAnnotation hitLineAnnotation =
                             createStickyLineAnnotation(
                                 StickyAxisAnnotation.SimpleAxis.X_AXIS,
@@ -258,7 +244,7 @@
                         plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
                             org.jfree.ui.Layer.BACKGROUND);
                     }
-                    if (ThemeUtil.parseShowHorizontalLine(theme)) {
+                    if (theme.parseShowHorizontalLine()) {
                         XYLineAnnotation lineBackAnnotation =
                             createStickyLineAnnotation(
                                 StickyAxisAnnotation.SimpleAxis.Y_AXIS2,
@@ -277,7 +263,7 @@
                 lineAnnotation = createLeftStickAnnotation(area, annotation.getPos(), lineStyle);
                 if (!Float.isNaN(annotation.getHitPoint()) && theme != null) {
                     // New line annotation to hit curve.
-                    if (ThemeUtil.parseShowHorizontalLine(theme)) {
+                    if (theme.parseShowHorizontalLine()) {
                         XYLineAnnotation hitLineAnnotation =
                             createStickyLineAnnotation(
                                 StickyAxisAnnotation.SimpleAxis.Y_AXIS,
@@ -286,7 +272,7 @@
                         plot.getRenderer(rendererIndex).addAnnotation(hitLineAnnotation,
                             org.jfree.ui.Layer.BACKGROUND);
                     }
-                    if (ThemeUtil.parseShowVerticalLine(theme)) {
+                    if (theme.parseShowVerticalLine()) {
                         XYLineAnnotation lineBackAnnotation =
                             createStickyLineAnnotation(
                                 StickyAxisAnnotation.SimpleAxis.X_AXIS,
@@ -452,15 +438,14 @@
         for (RiverAnnotation fa: annotations) {
 
             // Access text styling, if any.
-            Document theme = fa.getTheme();
+            ThemeDocument theme = fa.getTheme();
             TextStyle textStyle = null;
             LineStyle lineStyle = null;
 
             // Get Themeing information and add legend item.
             if (theme != null) {
-                ThemeAccess themeAccess = new ThemeAccess(theme);
-                textStyle = themeAccess.parseTextStyle();
-                lineStyle = themeAccess.parseLineStyle();
+                textStyle = theme.parseComplexTextStyle();
+                lineStyle = theme.parseComplexLineStyle();
                 if (fa.getLabel() != null) {
                     LegendItemCollection lic = new LegendItemCollection();
                     LegendItemCollection old = plot.getFixedLegendItems();
@@ -509,7 +494,7 @@
     @Override
     public abstract void doOut(
         ArtifactAndFacet bundle,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible);
 
 
@@ -619,10 +604,10 @@
      * @param theme   Theme document for given annotations.
      * @param visible The visibility of the annotations.
      */
-    protected void doAnnotations(
+    public void doAnnotations(
         RiverAnnotation annotations,
         ArtifactAndFacet aandf,
-        Document theme,
+        ThemeDocument theme,
         boolean visible
     ){
         logger.debug("doAnnotations");
@@ -701,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;
@@ -1834,11 +1820,14 @@
      *
      * @return a new LegendItem instance.
      */
-    public LegendItem createLegendItem(Document theme, String name) {
+    public LegendItem createLegendItem(ThemeDocument theme, String name) {
         // OPTIMIZE Pass font, parsed Theme items.
-        ThemeAccess themeAccess = new ThemeAccess(theme);
 
-        Color      color       = themeAccess.parseLineColorField();
+        Color color = theme.parseLineColorField();
+        if (color == null) {
+            color = Color.BLACK;
+        }
+
         LegendItem legendItem  = new LegendItem(name, color);
 
         legendItem.setLabelFont(createLegendLabelFont());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartGenerator2.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ChartInfoGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,6 +10,7 @@
 
 import org.dive4elements.river.collections.D4EArtifactCollection;
 import org.dive4elements.river.java2d.NOPGraphics2D;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.io.IOException;
 import java.io.OutputStream;
@@ -68,6 +69,10 @@
         this.generator = generator;
     }
 
+    public void setup(Object config) {
+        logger.debug("ChartInfoGenerator.setup");
+    }
+
 
     /**
      * Dispatches the operation to the instantiated generator.
@@ -76,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);
     }
 
 
@@ -106,9 +111,10 @@
     /**
      * Dispatches the operation to the instantiated generator.
      */
+    @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         generator.doOut(artifactFacet, attr, visible);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveExporter.java	Fri Sep 27 17:36:50 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/ComputedDischargeCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ComputedDischargeCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,10 +16,12 @@
 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.process.DischargeProcessor;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StickyAxisAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 import java.awt.Font;
@@ -32,11 +34,10 @@
 
 import org.apache.log4j.Logger;
 
-import org.w3c.dom.Document;
-
 
 /**
- * An OutGenerator that generates discharge curves.
+ * An OutGenerator that generates discharge curves, also at locations
+ * not at a gauge.
  *
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
@@ -104,6 +105,7 @@
      * Create Y (range) axis for given index, here with a special axis
      * that depends on other axis (does translation and scaling for
      * special case at gauge in cm).
+     * @return A NumberAxis, possibly scaled.
      */
     @Override
     protected NumberAxis createYAxis(int index) {
@@ -138,7 +140,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String name = artifactFacet.getFacetName();
@@ -150,9 +152,11 @@
             return;
         }
 
-        //XXX DEAD CODE // Facet facet = artifactFacet.getFacet();
-
-        if (name.equals(COMPUTED_DISCHARGE_Q)) {
+        DischargeProcessor dProcessor = new DischargeProcessor(getRange()[0]);
+        if (dProcessor.canHandle(name)) {
+            dProcessor.doOut(this, artifactFacet, attr, visible, YAXIS.W.idx);
+        }
+        else if (name.equals(COMPUTED_DISCHARGE_Q)) {
             doDischargeQOut((WQKms) artifactFacet.getData(context), artifactFacet, attr, visible);
         }
         else if (name.equals(STATIC_WQ)) {
@@ -165,18 +169,9 @@
                 attr,
                 visible);
         }
-        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
-                || name.equals(MAINVALUES_Q)
-                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                || name.equals(MAINVALUES_W)
-        ) {
-            RiverAnnotation mainValues = (RiverAnnotation) artifactFacet.getData(context);
-            translateRiverAnnotation(mainValues);
-            doAnnotations(
-                mainValues,
-                artifactFacet, attr, visible);
-        }
-        else if (name.equals(STATIC_WKMS_INTERPOL) || name.equals(HEIGHTMARKS_POINTS)) {
+        else if (STATIC_WKMS_INTERPOL.equals(name) ||
+            HEIGHTMARKS_POINTS.equals(name) ||
+            STATIC_WQKMS_W.equals(name)) {
             doWAnnotations(
                 artifactFacet.getData(context),
                 artifactFacet,
@@ -212,16 +207,16 @@
 
     /**
      * Add WQ Data to plot.
-     * @param wqkms data as double[][]
+     * @param wq data as double[][]
      */
     protected void doWQOut(
-        Object           wqkms,
+        Object           wq,
         ArtifactAndFacet aaf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("ComputedDischargeCurveGenerator: doWQOut");
-        double [][] data = (double [][]) wqkms;
+        double [][] data = (double [][]) wq;
 
         XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
         StyledSeriesBuilder.addPoints(series, data, true);
@@ -238,7 +233,7 @@
     protected void doDischargeQOut(
         WQKms            wqkms,
         ArtifactAndFacet aaf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("ComputedDischargeCurveGenerator: doDischargeQOut");
@@ -270,7 +265,7 @@
     protected void doQOut(
         WQKms            wqkms,
         ArtifactAndFacet aaf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("ComputedDischargeCurveGenerator: doQOut (add W/Q data).");
@@ -289,7 +284,7 @@
     protected void doWQAnnotations(
         Object   wqkms,
         ArtifactAndFacet aandf,
-        Document theme,
+        ThemeDocument theme,
         boolean  visible
     ) {
         List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
@@ -315,7 +310,7 @@
     protected void doWAnnotations(
         Object   wqkms,
         ArtifactAndFacet aandf,
-        Document theme,
+        ThemeDocument theme,
         boolean  visible
     ) {
         Facet facet = aandf.getFacet();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/CrossSectionGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -21,7 +21,6 @@
 import org.jfree.chart.annotations.XYTextAnnotation;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.DataProvider;
@@ -36,10 +35,9 @@
 import org.dive4elements.river.model.FastCrossSectionLine;
 import org.dive4elements.river.themes.LineStyle;
 import org.dive4elements.river.themes.TextStyle;
-import org.dive4elements.river.themes.ThemeAccess;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 import org.dive4elements.river.utils.Formatter;
-import org.dive4elements.river.utils.ThemeUtil;
 
 
 /**
@@ -72,7 +70,6 @@
 
     /** Trivial Constructor. */
     public CrossSectionGenerator() {
-        super();
     }
 
 
@@ -185,14 +182,13 @@
         for(RiverAnnotation fa : this.annotations) {
 
             // Access text styling, if any.
-            Document theme = fa.getTheme();
+            ThemeDocument theme = fa.getTheme();
             TextStyle textStyle = null;
             // XXX: DEAD CODE // LineStyle lineStyle = null;
 
             // Get Themeing information and add legend item.
             if (theme != null) {
-                ThemeAccess themeAccess = new ThemeAccess(theme);
-                textStyle = themeAccess.parseTextStyle();
+                textStyle = theme.parseComplexTextStyle();
                 // XXX: DEAD CODE // lineStyle = themeAccess.parseLineStyle();
                 if (fa.getLabel() != null) {
                     LegendItemCollection lic = new LegendItemCollection();
@@ -258,7 +254,7 @@
     @Override
     public void doOut(
             ArtifactAndFacet artifactFacet,
-            Document         attr,
+            ThemeDocument    attr,
             boolean          visible
             ) {
         String name = artifactFacet.getFacetName();
@@ -332,7 +328,7 @@
     protected void doCrossSectionWaterLineOut(
             Object   o,
             String   seriesName,
-            Document theme,
+            ThemeDocument theme,
             boolean  visible
             ) {
         logger.debug("CrossSectionGenerator.doCrossSectionWaterLineOut");
@@ -341,10 +337,10 @@
         // DO NOT SORT DATA! This destroys the gaps indicated by NaNs.
         StyledXYSeries series = new StyledXYSeries(seriesName, false, theme);
 
-        if (!ThemeUtil.parseShowLineLabel(theme)) {
+        if (!theme.parseShowLineLabel()) {
             series.setLabel("");
         }
-        if (ThemeUtil.parseShowWidth(theme)) {
+        if (theme.parseShowWidth()) {
             NumberFormat nf = Formatter.getMeterFormat(this.context);
             String labelAdd = "b=" + nf.format(lines.width) + "m";
             if (series.getLabel().length() == 0) {
@@ -354,7 +350,7 @@
                 series.setLabel(series.getLabel() + ", " + labelAdd);
             }
         }
-        if (ThemeUtil.parseShowLevel(theme) && lines.points.length > 1
+        if (theme.parseShowLevel() && lines.points.length > 1
                 && lines.points[1].length > 0) {
             NumberFormat nf = Formatter.getMeterFormat(this.context);
             D4EArtifact flys = (D4EArtifact) master;
@@ -369,7 +365,7 @@
                 series.setLabel(series.getLabel() + ", " + labelAdd);
             }
         }
-        if (ThemeUtil.parseShowMiddleHeight(theme) && lines.width != 0) {
+        if (theme.parseShowMiddleHeight() && lines.width != 0) {
             NumberFormat nf = Formatter.getMeterFormat(this.context);
             String labelAdd = "T=" + nf.format(lines.area / lines.width) + "m";
             // : " + lines.area + "/" + lines.width);
@@ -391,7 +387,7 @@
     protected void doHyk(
             Object   o,
             String   seriesName,
-            Document theme,
+            ThemeDocument theme,
             boolean  visible
             ) {
         logger.debug("CrossSectionGenerator.doHyk");
@@ -419,7 +415,7 @@
     protected void doCrossSectionOut(
             Object   o,
             String   seriesName,
-            Document theme,
+            ThemeDocument theme,
             boolean  visible
             ) {
         logger.debug("CrossSectionGenerator.doCrossSectionOut");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DiagramAttributes.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,532 @@
+/* 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 interface Evaluator {
+        Object evaluate(D4EArtifact artifact, CallContext context);
+    } // interface Evaluator
+
+    public static final Evaluator TRUE = new Evaluator() {
+        @Override
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            return Boolean.TRUE;
+        }
+    };
+
+    public static final Evaluator FALSE = new Evaluator() {
+        @Override
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            return Boolean.FALSE;
+        }
+    };
+
+    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 List<Processor> getProcessorsForAxisName(String axisName) {
+            List<Processor> retval = new ArrayList<Processor>(5);
+            for (Processor pr: processors) {
+                String aName = pr.getAxisName();
+                if (aName != null && axisName.equals(aName)) {
+                    retval.add(pr);
+                }
+            }
+            return retval;
+        }
+
+        public List<Processor> getProcessors() {
+            return processors;
+        }
+
+        public Title getTitle() {
+            return DiagramAttributes.this.getTitle();
+        }
+
+        public Title getSubtitle() {
+            return DiagramAttributes.this.getSubtitle();
+        }
+
+        public DomainAxisAttributes getDomainAxis() {
+            return DiagramAttributes.this.getDomainAxis();
+        }
+
+        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; // TODO: Remove!
+        private boolean forceAlign;  // TODO: Remove!
+        private boolean includeZero; // TODO: Use Evaluator
+
+        private Evaluator isInverted;
+
+        public AxisAttributes() {
+        }
+
+        public AxisAttributes(
+            String    name,
+            boolean   isLeftAlign,
+            boolean   forceAlign,
+            boolean   includeZero,
+            Evaluator isInverted
+        ) {
+            this.name        = name;
+            this.isLeftAlign = isLeftAlign;
+            this.forceAlign  = forceAlign;
+            this.includeZero = includeZero;
+            this.isInverted  = isInverted;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public boolean isLeftAlign() {
+            return isLeftAlign;
+        }
+
+        public boolean forceAlign() {
+            return forceAlign;
+        }
+
+        public boolean includeZero() {
+            return includeZero;
+        }
+
+        public Evaluator isInverted() {
+            return isInverted;
+        }
+    } // class AxisAttributes
+
+    public class DomainAxisAttributes extends AxisAttributes {
+
+        private Title title;
+
+        public DomainAxisAttributes() {
+        }
+
+        public DomainAxisAttributes(
+            String    name,
+            boolean   isLeftAlign,
+            boolean   forceAlign,
+            boolean   includeZero,
+            Evaluator isInverted,
+            Title     title
+        ) {
+            super(name, isLeftAlign, forceAlign, includeZero, isInverted);
+            this.title = title;
+        }
+
+        public Title getTitle() {
+            return title;
+        }
+    } // class DomainAxisAttributes
+
+    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 abstract static class ConvertEvaluator
+    implements Evaluator
+    {
+        protected String key;
+        protected String type;
+
+        public ConvertEvaluator() {
+        }
+
+        public ConvertEvaluator(String key, String type) {
+            this.key  = key;
+            this.type = type;
+        }
+
+        protected Object convert(Object value) {
+            if (value == null || type == null || type.isEmpty()) {
+                return value;
+            }
+            if (value instanceof String) {
+                String v = (String)value;
+                if ("double".equals(type)) {
+                    return Double.valueOf(v);
+                }
+                if ("int".equals(type)) {
+                    return Integer.valueOf(v);
+                }
+                if ("string".equals(type)) {
+                    return v;
+                }
+            }
+            // TODO: Support more types
+            return value;
+        }
+    } // class ConvertEvaluator
+
+    public static class ContextEvaluator extends ConvertEvaluator {
+
+        public ContextEvaluator() {
+        }
+
+        public ContextEvaluator(String key, String type) {
+            super(key, type);
+        }
+
+        @Override
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            return convert(context.getContextValue(key));
+        }
+    } // class ContextEvaluator
+
+    public static class ArtifactEvaluator extends ConvertEvaluator {
+
+        public ArtifactEvaluator() {
+        }
+
+        public ArtifactEvaluator(String key, String type) {
+            super(key, type);
+        }
+
+        @Override
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            return convert(artifact.getDataAsString(key));
+        }
+    } // class ContextEvaluator
+
+    public static class StringEvaluator implements Evaluator {
+
+        private String value;
+
+        public StringEvaluator() {
+        }
+        public StringEvaluator(String value) {
+            this.value = value;
+        }
+
+        @Override
+        public Object evaluate(D4EArtifact artifact, CallContext context) {
+            return value;
+        }
+    } // class StringEvaluator
+
+    public static class Title {
+
+        private String key;
+        private String def;
+        private List<Evaluator> arguments;
+
+        public Title() {
+            arguments = new ArrayList<Evaluator>(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(Evaluator 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 DomainAxisAttributes domainAxis;
+
+    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);
+        parseDomainAxis(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();
+
+            String isInverted = axisElement.getAttribute("inverted");
+
+            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;
+            }
+
+            Evaluator isInvertedE = parseEvaluator(isInverted, FALSE);
+
+            axesAttrs.add(new AxisAttributes(
+                name, isleftAlign, forceAlign,
+                includeZero.equals("true"),
+                isInvertedE));
+        }
+    }
+
+    private static Evaluator createDynamicEvaluator(
+        String    className,
+        Evaluator def
+    ) {
+        try {
+            Class<Evaluator> clazz = (Class<Evaluator>)Class.forName(className);
+            return clazz.newInstance();
+        }
+        catch (ClassNotFoundException cnfe) {
+            log.error(cnfe, cnfe);
+        }
+        catch (InstantiationException ie) {
+            log.error(ie, ie);
+        }
+        catch (IllegalAccessException iae) {
+            log.error(iae, iae);
+        }
+        return def;
+    }
+
+    private static Evaluator parseEvaluator(String s, Evaluator def) {
+        if ((s = s.trim()).isEmpty()) return def;
+        if ("true".equals(s)) return TRUE;
+        if ("false".equals(s)) return FALSE;
+        if (s.endsWith("()")) {
+            return createDynamicEvaluator(
+                s.substring(0, s.length()-2).trim(),
+                def);
+        }
+        return def;
+    }
+
+    public List<AxisProcessor> getAxesProcessors() {
+        return axesProcessors;
+    }
+
+    public Title getTitle() {
+        return title;
+    }
+
+    public Title getSubtitle() {
+        return subtitle;
+    }
+
+    public DomainAxisAttributes getDomainAxis() {
+        return domainAxis;
+    }
+
+    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 parseDomainAxis(Element config) {
+        Title title = extractTitle(config, "domain-axis");
+        String includeZero = config.getAttribute("include-zero");
+        String isInverted = config.getAttribute("inverted");
+
+        domainAxis = new DomainAxisAttributes(
+            "X",
+            false,
+            false,
+            includeZero.equals("true"),
+            parseEvaluator(isInverted, FALSE),
+            title);
+    }
+
+    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");
+
+            Evaluator ev = new StringEvaluator(expression);
+
+            if (expression.endsWith("()")) {
+                ev = createDynamicEvaluator(
+                    expression.substring(0, expression.length()-2).trim(),
+                    ev);
+            }
+            else if (expression.startsWith("artifact.")) {
+                ev = new ArtifactEvaluator(
+                    expression.substring("artifact.".length()), type);
+            }
+            else if (expression.startsWith("context.")) {
+                ev = new ContextEvaluator(
+                    expression.substring("context.".length()), type);
+            }
+
+            title.addArgument(ev);
+        }
+        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	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,1167 @@
+/* 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 java.io.OutputStream;
+
+import javax.swing.ImageIcon;
+
+import org.apache.log4j.Logger;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifactdatabase.state.Facet;
+
+import org.dive4elements.artifacts.CallContext;
+
+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.dive4elements.river.utils.RiverUtils;
+
+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;
+
+import org.w3c.dom.Document;
+
+
+/**
+ * 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();
+    }
+
+    @Override
+    public void init(
+        String       outName,
+        Document     request,
+        OutputStream out,
+        CallContext  context
+    ) {
+        super.init(outName, request, out, context);
+
+        RiverUtils.setKMFromRequestInContext(request, context);
+
+        setInvertedFromConfig();
+    }
+
+    private void setInvertedFromConfig() {
+        DiagramAttributes.DomainAxisAttributes dx =
+            diagramAttributes.getDomainAxis();
+
+        if (dx != null) {
+            inverted = (Boolean)dx.isInverted()
+                .evaluate((D4EArtifact)getMaster(), context);
+            logger.debug("setInvertedFromConfig: " + inverted);
+        } else {
+            logger.debug("setInvertedFromConfig no domain axis found?");
+        }
+    }
+
+    /**
+     * 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);
+    }
+
+    public void addAxisDataset(XYDataset dataset, String axisName, boolean visible) {
+        addAxisDataset(dataset, 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) {
+            /* Subtitle is optional */
+            return null;
+        }
+
+        return dTitle.evaluate((D4EArtifact)getMaster(), context);
+    }
+
+    /**
+     * Get internationalized label for the x axis.
+     */
+    @Override
+    protected String getDefaultXAxisLabel() {
+        DiagramAttributes.DomainAxisAttributes dx =
+            diagramAttributes.getDomainAxis();
+
+        if (dx != null) {
+            DiagramAttributes.Title t = dx.getTitle();
+            if (t != null) {
+                return t.evaluate((D4EArtifact)getMaster(), context);
+            }
+        }
+        return "Domain Axis Title not configured in conf.xml";
+    }
+
+    @Override
+    protected String getDefaultYAxisLabel(String axisName) {
+        String label;
+        for (Processor pr: diagramAttributes.getProcessorsForAxisName(axisName)) {
+            label = pr.getAxisLabel(this);
+            if (label != null) {
+                return label;
+            }
+        }
+        return "No configured axis label";
+    }
+
+
+    /**
+     * 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/DischargeCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DischargeCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,16 +8,22 @@
 
 package org.dive4elements.river.exports;
 
+import java.util.ArrayList;
+import java.util.List;
+
 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.WQKms;
+import org.dive4elements.river.exports.process.DischargeProcessor;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StickyAxisAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.model.River;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 import org.apache.log4j.Logger;
@@ -26,7 +32,6 @@
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.Range;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 
 /**
@@ -38,6 +43,7 @@
 extends      XYChartGenerator
 implements   FacetTypes {
 
+    /** Beware, in this implementation, the W axis is also in cm! */
     public static enum YAXIS {
         WCm(0),
         W(1);
@@ -76,6 +82,10 @@
         // Get gauge which is defined for km
         Gauge gauge =
             RiverUtils.getRiver(artifact).determineGauge(km-0.1d, km+0.1d);
+        if (gauge == null) {
+            logger.error("No Gauge could be found at station " + km + "!");
+            return 0d;
+        }
         double subtractPNP = 0d;
         // Compare to km.
         if (Math.abs(km - gauge.getStation().doubleValue()) < tolerance) {
@@ -85,6 +95,7 @@
     }
 
 
+    /** Get the current Gauge datum with default distance tolerance. */
     public double getCurrentGaugeDatum() {
         return getCurrentGaugeDatum(getRange()[0],
             (D4EArtifact) getMaster(), 1e-4);
@@ -178,13 +189,18 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         String name = artifactFacet.getFacetName();
         logger.debug("DischargeCurveGenerator.doOut: " + name);
 
-        if (name.equals(DISCHARGE_CURVE)
+        DischargeProcessor dProcessor = new DischargeProcessor(getRange()[0]);
+        if (dProcessor.canHandle(name)) {
+            // In Base DischargeCurveGenerator, always at gauge, use WCm axis.
+            dProcessor.doOut(this, artifactFacet, theme, visible, YAXIS.WCm.idx);
+        }
+        else if (name.equals(DISCHARGE_CURVE)
                 || name.equals(GAUGE_DISCHARGE_CURVE)) {
             doDischargeOut(
                 (D4EArtifact)artifactFacet.getArtifact(),
@@ -193,22 +209,17 @@
                 theme,
                 visible);
         }
-        else if (name.equals(COMPUTED_DISCHARGE_MAINVALUES_Q)
-                || name.equals(MAINVALUES_Q)
-                || name.equals(COMPUTED_DISCHARGE_MAINVALUES_W)
-                || name.equals(MAINVALUES_W))
-        {
-            RiverAnnotation mainValues = (RiverAnnotation) artifactFacet.getData(context);
-            translateRiverAnnotation(mainValues);
-            doAnnotations(
-                mainValues,
-                artifactFacet, theme, visible);
-        }
         else if (FacetTypes.IS.MANUALPOINTS(name)) {
             doPoints(artifactFacet.getData(context),
                 artifactFacet,
                 theme, visible, YAXIS.W.idx);
         }
+        else if (STATIC_WQ.equals(name)) {
+            doWQOut(artifactFacet.getData(context),
+                artifactFacet,
+                theme,
+                visible);
+        }
         else {
            logger.warn("DischargeCurveGenerator.doOut: Unknown facet name: " + name);
            return;
@@ -223,9 +234,10 @@
         D4EArtifact artifact,
         Object        o,
         String        description,
-        Document      theme,
+        ThemeDocument theme,
         boolean       visible)
     {
+        logger.debug("DischargeCurveGenerator.doDischargeOut");
         WQKms wqkms = (WQKms) o;
 
         String gaugeName = wqkms.getName();
@@ -250,5 +262,134 @@
 
         addAxisSeries(series, YAXIS.W.idx, visible);
     }
+
+    /**
+     * Add W/Q-Series to plot.
+     * @param wqkms actual data
+     * @param theme theme to use.
+     */
+    protected void doQOut(
+        Object           wqkms,
+        ArtifactAndFacet aaf,
+        ThemeDocument    theme,
+        boolean          visible
+    ) {
+        logger.debug("DischargeCurveGenerator: doQOut (add W/Q data).");
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+
+        StyledSeriesBuilder.addPointsQW(series, (WQKms) wqkms);
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+    }
+
+
+    /** Add a point annotation at given x and y coordinates. */
+    protected void addPointTextAnnotation(
+        String title,
+        double x,
+        double y,
+        ThemeDocument theme
+    ) {
+        List<XYTextAnnotation> textAnnos =
+            new ArrayList<XYTextAnnotation>();
+        XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                title,
+                x,
+                y);
+        textAnnos.add(anno);
+        RiverAnnotation flysAnno = new RiverAnnotation(
+            null, null, null, theme);
+        flysAnno.setTextAnnotations(textAnnos);
+        addAnnotations(flysAnno);
+    }
+
+
+    /**
+     * Return true if all values in data[0] are smaller than zero
+     * (in imported data they are set to -1 symbolically).
+     * Return false if data is null or empty
+     */
+    private boolean hasNoDischarge(double[][] data) {
+        if (data == null || data.length == 0) {
+            return false;
+        }
+
+        double[] qs = data[0];
+        for (double q: qs) {
+            if (q > 0d) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+
+    /**
+     * Add WQ Data to plot.
+     * @param wq data as double[][]
+     */
+    protected void doWQOut(
+        Object           wq,
+        ArtifactAndFacet aaf,
+        ThemeDocument    theme,
+        boolean          visible
+    ) {
+        logger.debug("DischargeCurveGenerator: doWQOut");
+        double [][] data = (double [][]) wq;
+        String title = aaf.getFacetDescription();
+
+        double translate = getCurrentGaugeDatum();
+
+        // If no Q values (i.e. all -1) found, add annotations.
+        if (hasNoDischarge(data)) {
+            List<StickyAxisAnnotation> xy = new ArrayList<StickyAxisAnnotation>();
+
+            for (double y: data[1]) {
+                if (translate != 0d) {
+                    y = (y-translate)*100d;
+                }
+
+                xy.add(new StickyAxisAnnotation(
+                    title,
+                    (float) y,
+                    StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+            }
+
+            doAnnotations(
+                new RiverAnnotation(title, xy),
+                aaf, theme, visible);
+            return;
+        }
+
+        // Otherwise add points.
+        XYSeries series = new StyledXYSeries(title, theme);
+
+        if (translate != 0d) {
+            StyledSeriesBuilder.addPointsQW(series, data, -translate, 100d);
+            addAxisSeries(series, YAXIS.W.idx, visible);
+        }
+        else {
+            StyledSeriesBuilder.addPoints(series, data, true);
+            addAxisSeries(series, YAXIS.W.idx, visible);
+        }
+
+        if (visible && theme.parseShowPointLabel()
+            && data != null && data.length != 0) {
+
+            double[] xs = data[0];
+            double[] ys = data[1];
+            for (int i = 0; i < xs.length; i++) {
+                double x = xs[i];
+                double y = ys[i];
+
+                if (translate != 0d) {
+                    y = (y-translate)*100d;
+                }
+
+                addPointTextAnnotation(title, x, y, theme);
+            }
+        }
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/DischargeLongitudinalSectionGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +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.w3c.dom.Document;
-
-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.artifacts.model.WQKms;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.exports.process.WOutProcessor;
-
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledXYSeries;
-
-
-
-/**
- * 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,
-        Document         attr,
-        boolean          visible
-    ) {
-        logger.debug("DischargeLongitudinalSectionGenerator.doOut");
-
-        String name = artifactFacet.getFacetName();
-
-        if (name == null) {
-            return;
-        }
-
-        Facet facet = artifactFacet.getFacet();
-
-        if (name.contains(DISCHARGE_LONGITUDINAL_Q)) {
-            doQOut(
-                (WQKms) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else 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 if (name.equals(STATIC_WQKMS_Q)) {
-            doQOut(
-                (WQKms) artifactFacet.getData(context),
-                artifactFacet,
-                attr,
-                visible);
-        }
-        else {
-            Processor processor = new WOutProcessor();
-            if (processor.canHandle(name)) {
-                processor.doOut(this, artifactFacet, attr, visible, YAXIS.W.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,
-        Document 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	Fri Sep 13 18:29:01 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveExporter.java	Fri Sep 27 17:36:50 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/DurationCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/DurationCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -14,6 +14,7 @@
 import org.dive4elements.river.jfree.Bounds;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.awt.Font;
 import java.awt.geom.Point2D;
@@ -24,7 +25,6 @@
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.Range;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 
 /**
@@ -173,7 +173,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String name = artifactFacet.getFacetName();
@@ -238,7 +238,7 @@
     protected void doWOut(
         WQDay            wqdays,
         ArtifactAndFacet aaf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("DurationCurveGenerator.doWOut");
@@ -257,10 +257,10 @@
     }
 
     protected void doPointOut(
-        Point2D point,
+        Point2D          point,
         ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
+        ThemeDocument    theme,
+        boolean          visible
     ){
         logger.debug("DurationCurveGenerator.doPointOut");
 
@@ -281,7 +281,7 @@
     protected void doQOut(
         WQDay            wqdays,
         ArtifactAndFacet aaf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("DurationCurveGenerator.doQOut");
--- a/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityExporter.java	Fri Sep 27 17:36:50 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/FlowVelocityGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/FlowVelocityGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -14,8 +14,6 @@
 
 import org.jfree.data.xy.XYSeries;
 
-import org.w3c.dom.Document;
-
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
 
@@ -33,6 +31,7 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 
@@ -209,7 +208,7 @@
      */
     public void doOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String name = artifactAndFacet.getFacetName();
@@ -370,7 +369,7 @@
     private void doBedQualityLoadDiameter(
         BedloadDiameterResult data,
         ArtifactAndFacet aandf,
-        Document attr,
+        ThemeDocument attr,
         boolean visible) {
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
         StyledSeriesBuilder.addPoints(series, data.getDiameterData(), true);
@@ -382,7 +381,7 @@
     private void doBedQualityTopLayerOut(
         BedDiameterResult data,
         ArtifactAndFacet aandf,
-        Document attr,
+        ThemeDocument attr,
         boolean visible) {
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), attr);
         StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
@@ -393,7 +392,7 @@
     private void doBedQualitySubLayerOut(
         BedDiameterResult data,
         ArtifactAndFacet aandf,
-        Document attr,
+        ThemeDocument attr,
         boolean visible
     ) {
         logger.debug("Do beddiametersubout");
@@ -416,7 +415,7 @@
     protected void doMainChannelOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("FlowVelocityGenerator.doMainChannelOut");
@@ -433,7 +432,7 @@
     protected void doVPointOut (
         Object data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("FlowVelocityGenerator.doVPointOut");
@@ -456,7 +455,7 @@
     protected void doTotalChannelOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("FlowVelocityGenerator.doTotalChannelOut");
@@ -486,10 +485,10 @@
     protected void doQOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
-        logger.debug("FlowVelocityGenerator.doTauOut");
+        logger.debug("FlowVelocityGenerator.doQOut");
 
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
 
@@ -509,7 +508,7 @@
     protected void doTauOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         logger.debug("FlowVelocityGenerator.doTauOut");
@@ -543,10 +542,10 @@
      * @param visible whether or not visible.
      */
     protected void doArea(
-        Object     o,
+        Object           o,
         ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible
+        ThemeDocument    theme,
+        boolean          visible
     ) {
         logger.debug("FlowVelocityGenerator.doArea");
         logger.warn("TODO: Implement FlowVelocityGenerator.doArea");
--- a/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveExporter.java	Fri Sep 27 17:36:50 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/HistoricalDischargeCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -25,6 +25,7 @@
 
 import org.dive4elements.river.jfree.StyledTimeSeries;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 import org.jfree.chart.plot.XYPlot;
@@ -36,8 +37,6 @@
 import org.jfree.data.time.TimeSeries;
 import org.jfree.data.time.TimeSeriesCollection;
 
-import org.w3c.dom.Document;
-
 
 /**
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
@@ -127,7 +126,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
+    public void doOut(ArtifactAndFacet artifactFacet, ThemeDocument theme,
         boolean visible) {
         String name = artifactFacet.getFacetName();
         logger.debug("HistoricalDischargeCurveGenerator.doOut: " + name);
@@ -169,7 +168,7 @@
     }
 
     protected void doHistoricalDischargeOutQ(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         logger.debug("doHistoricalDischargeOut(): description = " + desc);
 
         WQTimerange wqt = (WQTimerange) data;
@@ -181,7 +180,7 @@
     }
 
     protected void doHistoricalDischargeOutW(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         logger.debug("doHistoricalDischargeOut(): description = " + desc);
 
         WQTimerange wqt = (WQTimerange) data;
@@ -193,7 +192,7 @@
     }
 
     protected void doHistoricalDischargeDifferenceOutQ(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
 
         HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
@@ -205,7 +204,7 @@
     }
 
     protected void doHistoricalDischargeDifferenceOutW(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         logger.debug("doHistoricalDischargeDifferenceOut: desc = " + desc);
 
         HistoricalWQTimerange wqt = (HistoricalWQTimerange) data;
@@ -223,7 +222,7 @@
      * looks like a "step chart".
      */
     protected TimeSeriesCollection newTimeSeriesCollection(
-        Timerange[] timeranges, double[] values, Document theme, String desc) {
+        Timerange[] timeranges, double[] values, ThemeDocument theme, String desc) {
         logger.debug("Create new TimeSeriesCollection for: " + desc);
 
         TimeSeriesCollection tsc = new TimeSeriesCollection();
--- a/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/HistoricalDischargeWQCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,7 +10,6 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.river.artifacts.D4EArtifact;
@@ -22,6 +21,7 @@
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledValueMarker;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 
@@ -105,7 +105,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet artifactFacet, Document theme,
+    public void doOut(ArtifactAndFacet artifactFacet, ThemeDocument theme,
         boolean visible) {
         String name = artifactFacet.getFacetName();
         logger.debug("HistoricalDischargeWQCurveGenerator.doOut: " + name);
@@ -124,7 +124,9 @@
                 artifactFacet.getData(context),
                 artifactFacet.getFacetDescription(), theme, visible);
         }
-        else if (name.equals(HISTORICAL_DISCHARGE_WQ_CURVE)) {
+        else if (name.equals(HISTORICAL_DISCHARGE_WQ_CURVE) ||
+                 name.equals(DISCHARGE_CURVE) ||
+                 name.equals(GAUGE_DISCHARGE_CURVE)) {
             doHistoricalDischargeCurveOut(
                 (D4EArtifact) artifactFacet.getArtifact(),
                 artifactFacet.getData(context),
@@ -148,19 +150,19 @@
     }
 
     protected void doHistoricalDischargeOutQ(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         double value = Double.valueOf(data.toString());
         addDomainMarker(new StyledValueMarker(value, theme), visible);
     }
 
     protected void doHistoricalDischargeOutW(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         double value = Double.valueOf(data.toString());
         addValueMarker(new StyledValueMarker(value, theme), visible);
     }
 
     protected void doHistoricalDischargeCurveOut(D4EArtifact artifact,
-        Object data, String desc, Document theme, boolean visible) {
+        Object data, String desc, ThemeDocument theme, boolean visible) {
         XYSeries series = new StyledXYSeries(desc, theme);
         StyledSeriesBuilder.addPointsQW(series, (WQKms) data);
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/InfoGeneratorHelper.java	Fri Sep 27 17:36:50 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	Fri Sep 27 17:36:50 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 :
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/IsKmUpEvaluator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,32 @@
+/* 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.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.model.RiverFactory;
+import org.dive4elements.river.model.River;
+
+public class IsKmUpEvaluator
+implements   DiagramAttributes.Evaluator
+{
+    public IsKmUpEvaluator() {
+    }
+
+    @Override
+    public Object evaluate(D4EArtifact artifact, CallContext context) {
+        RiverAccess access = new RiverAccess(artifact);
+        River river = RiverFactory.getRiver(access.getRiver());
+        return river == null
+            ? Boolean.FALSE
+            : river.getKmUp();
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -20,20 +20,20 @@
 import org.dive4elements.river.exports.process.Processor;
 import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
 import org.dive4elements.river.exports.process.BedDiffYearProcessor;
-import org.dive4elements.river.exports.process.BedheightProcessor;
+import org.dive4elements.river.exports.process.BedHeightSoundingProcessor;
+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.utils.DataUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 import org.apache.log4j.Logger;
 import org.jfree.chart.axis.NumberAxis;
 import org.jfree.chart.axis.ValueAxis;
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 
 /**
@@ -321,7 +321,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String name = artifactAndFacet.getFacetName();
@@ -339,14 +339,19 @@
             return;
         }
 
-        WOutProcessor wProcessor = new WOutProcessor();
-        Processor bedp = new BedheightProcessor();
+        Processor wProcessor = new WOutProcessor();
+        Processor qProcessor = new QOutProcessor();
+        Processor bedp = new BedHeightSoundingProcessor();
         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);
         }
+        if (qProcessor.canHandle(name)) {
+            qProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.Q.idx);
+        }
         else if (bedp.canHandle(name)) {
            bedp.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
         }
@@ -356,26 +361,8 @@
         else if (bdhyProcessor.canHandle(name)) {
            bdhyProcessor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
         }
-        else if (name.equals(LONGITUDINAL_Q)) {
-            doQOut(
-                (WQKms) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (name.equals(STATIC_WQKMS_Q)) {
-            doQOut(
-                (WQKms) 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(
@@ -411,7 +398,7 @@
     protected void doWDifferencesOut(
         WKms       wkms,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument   theme,
         boolean    visible
     ) {
         logger.debug("WDifferencesCurveGenerator.doWDifferencesOut");
@@ -434,68 +421,6 @@
         StyledSeriesBuilder.addPoints(series, wkms);
 
         addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
-            setInverted(true);
-        }
-    }
-
-
-    /**
-     * Process the output for Q facets in a longitudinal section curve.
-     *
-     * @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.
-     */
-    protected void doQOut(
-        WQKms    wqkms,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean  visible
-    ) {
-        logger.debug("LongitudinalSectionGenerator.doQOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
-
-        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;
     }
 
 
@@ -536,7 +461,7 @@
     protected void doArea(
         Object     o,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument   theme,
         boolean    visible
     ) {
         logger.debug("LongitudinalSectionGenerator.doArea");
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/LongitudinalSectionGenerator2.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,92 @@
+/* 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.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.river.artifacts.access.RangeAccess;
+import org.dive4elements.river.jfree.Bounds;
+import org.dive4elements.river.jfree.DoubleBounds;
+import org.dive4elements.river.themes.ThemeDocument;
+
+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";
+
+    @Override
+    public String getDefaultChartSubtitle() {
+        double[] dist = getRange();
+
+        if (dist == null || dist.length != 2 ||
+                Double.isNaN(dist[0]) || Double.isNaN(dist[1])) {
+            Object [] args = new Object[] {getRiverName()};
+            return msg(I18N_CHART_SHORT_SUBTITLE, "", args);
+        }
+
+        if (Math.abs(dist[0] - dist[1]) < 1E-5) {
+            Object [] args = new Object[] {getRiverName(), dist[1]};
+            return msg(I18N_CHART_LOCATION_SUBTITLE, "", args);
+        }
+
+        return super.getDefaultChartSubtitle();
+    }
+
+    /* We override doOut here to save the startkm and endkm in the
+     * context. Some facets will deliver different data because of
+     * that setting. It is mainly used in MINFO where it causes
+     * adaptive smoothing on the data if you are zoomed out do
+     * reduce the static in the curve. */
+    @Override
+    public void doOut(
+        ArtifactAndFacet bundle,
+        ThemeDocument    theme,
+        boolean          visible
+    ) {
+        /* Aheinecke (25.09.2013): I do not understand why this has to be
+         * done so difficult and if it really must be done for every
+         * facet. At least it has to be done _before_ the super class
+         * actually does the output and accesses the facet data.
+         */
+        D4EArtifact artifact = (D4EArtifact)bundle.getArtifact();
+
+        if (getXBounds(0) != null && getDomainAxisRange() != null) {
+            Bounds bounds =
+                calculateZoom(getXBounds(0), getDomainAxisRange());
+            context.putContextValue("startkm", bounds.getLower());
+            context.putContextValue("endkm", bounds.getUpper());
+        }
+        else if (getXBounds(0) != null && getDomainAxisRange() == null) {
+            context.putContextValue("startkm", getXBounds(0).getLower());
+            context.putContextValue("endkm", getXBounds(0).getUpper());
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() == null) {
+            RangeAccess access = new RangeAccess(artifact);
+            if (access.hasFrom() && access.hasTo()) {
+                context.putContextValue("startkm", access.getFrom());
+                context.putContextValue("endkm", access.getTo());
+            }
+        }
+        else if (getXBounds(0) == null && getDomainAxisRange() != null){
+            RangeAccess access = new RangeAccess(artifact);
+            if (access.hasFrom() && access.hasTo()) {
+                Bounds b = new DoubleBounds(access.getFrom(), access.getTo());
+                Bounds bounds =
+                    calculateZoom(b, getDomainAxisRange());
+                context.putContextValue("startkm", bounds.getLower());
+                context.putContextValue("endkm", bounds.getUpper());
+            }
+        }
+        super.doOut(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/LongitudinalSectionInfoGenerator.java	Fri Sep 13 18:29:01 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/MapGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -24,9 +24,9 @@
 import org.dive4elements.river.artifacts.model.map.WSPLGENLayerFacet;
 import org.dive4elements.river.artifacts.states.WaterlevelGroundDifferences;
 import org.dive4elements.river.collections.D4EArtifactCollection;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.ArtifactMapfileGenerator;
 import org.dive4elements.river.utils.GeometryUtils;
-import org.dive4elements.river.utils.ThemeUtil;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -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;
@@ -96,7 +102,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible)
     {
         String name = artifactFacet.getFacetName();
@@ -120,9 +126,11 @@
                 setInitialExtent(extent);
                 createWSPLGENLayer(flys, wms, attr);
             }
-            else if (FLOODMAP_USERSHAPE.equals(name)) {
-                createUserShapeLayer(flys, wms);
-            }
+            // FIXME: Already generated by HWSBarrierState
+            // wms has a wrong SRID which would break that layer
+            //else if (FLOODMAP_USERSHAPE.equals(name)) {
+            //    createUserShapeLayer(flys, wms);
+            //}
             else {
                 logger.debug("doOut: createDatabaseLayer for facet name: " + name);
                 createDatabaseLayer(flys, wms, attr);
@@ -135,9 +143,9 @@
 
 
     protected void createWSPLGENLayer(
-        D4EArtifact  flys,
+        D4EArtifact   flys,
         WMSLayerFacet wms,
-        Document      attr
+        ThemeDocument attr
     ) {
         try {
             if(wms instanceof WSPLGENLayerFacet) {
@@ -158,7 +166,8 @@
                 mfg.createUeskLayer(
                     flys,
                     (WSPLGENLayerFacet) wms,
-                    ThemeUtil.createDynamicMapserverStyle(attr, from, to, step, context.getMeta()),
+                    attr.createDynamicMapserverStyle(
+                        from, to, step, context.getMeta()),
                     context);
             }
             else {
@@ -188,9 +197,9 @@
 
 
     protected void createDatabaseLayer(
-        D4EArtifact  flys,
+        D4EArtifact   flys,
         WMSLayerFacet wms,
-        Document      attr
+        ThemeDocument attr
     ) {
         logger.debug("createDatabaseLayer for facet: " + wms.getName());
 
@@ -209,7 +218,7 @@
                 mfg.createDatabaseLayer(
                         flys,
                         (WMSDBLayerFacet) wms,
-                        ThemeUtil.createMapserverStyle(attr));
+                        attr.createMapserverStyle());
             }
             else {
                 logger.warn("Cannot create DB layer from: " + wms.getClass());
--- a/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightExporter.java	Fri Sep 27 17:36:50 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/MiddleBedHeightGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,295 +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.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Facet;
-import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.model.FacetTypes;
-import org.dive4elements.river.artifacts.model.MiddleBedHeightData;
-import org.dive4elements.river.exports.process.BedheightProcessor;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.exports.process.WOutProcessor;
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.utils.RiverUtils;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-// TODO Move class to org.dive4elements.river.exports.minfo
-/**
- * An OutGenerator that generates middle bed height charts.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MiddleBedHeightGenerator
-extends      XYChartGenerator
-implements   FacetTypes
-{
-    public enum YAXIS {
-        H(0), W(1), P(2);
-        protected int idx;
-        private YAXIS(int c) {
-           idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(MiddleBedHeightGenerator.class);
-
-    /** Key to look up internationalized String for annotations label. */
-    public static final String I18N_ANNOTATIONS_LABEL =
-        "chart.bedheight_middle.annotations.label";
-
-    public static final String I18N_CHART_TITLE =
-        "chart.bedheight_middle.section.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-        "chart.bedheight_middle.section.subtitle";
-
-    public static final String I18N_CHART_SHORT_SUBTITLE =
-        "chart.bedheight_middle.section.shortsubtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-        "chart.bedheight_middle.section.xaxis.label";
-
-    public static final String I18N_YAXIS_LABEL =
-        "chart.bedheight_middle.section.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  = "Mittlere Sohlhöhe";
-    public static final String I18N_XAXIS_LABEL_DEFAULT  = "km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT  = "mittlere Sohlhöhen [müNN]";
-    public static final String I18N_W_YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.label";
-    public static final String I18N_W_YAXIS_LABEL_DEFAULT  = "W [NN + m]";
-    public static final String I18N_P_YAXIS_LABEL_DEFAULT  = "Gepeilte Breite [m]";
-    public static final String I18N_P_YAXIS_LABEL =
-        "chart.bedheight_middle.sounding.yaxis.label";
-
-    @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();
-            }
-        };
-    }
-
-
-    /**
-     * Returns the default title for this chart.
-     *
-     * @return the default title for this chart.
-     */
-    @Override
-    public String getDefaultChartTitle() {
-        Object[] args = new Object[] {
-            getRiverName()
-        };
-
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT, args);
-    }
-
-
-    /**
-     * Get internationalized label for the x axis.
-     */
-    @Override
-    protected String getDefaultXAxisLabel() {
-        D4EArtifact flys = (D4EArtifact) master;
-
-        return msg(
-            I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] { RiverUtils.getRiver(flys).getName() });
-    }
-
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        String label = "default";
-
-        if (index == YAXIS.H.idx) {
-            label = getHAxisLabel();
-        }
-        else if (index == YAXIS.W.idx) {
-           D4EArtifact flys = (D4EArtifact) master;
-           String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
-
-           label = msg(
-                I18N_W_YAXIS_LABEL,
-                I18N_W_YAXIS_LABEL_DEFAULT,
-                new Object[] { unit });
-        }
-        else if (index == YAXIS.P.idx) {
-            label = msg(I18N_P_YAXIS_LABEL, I18N_P_YAXIS_LABEL_DEFAULT);
-        }
-
-        return label;
-    }
-
-
-    /**
-     * Get internationalized label for the y axis.
-     */
-    protected String getHAxisLabel() {
-        D4EArtifact flys = (D4EArtifact) master;
-
-        String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
-
-        return msg(I18N_YAXIS_LABEL,
-            I18N_YAXIS_LABEL_DEFAULT,
-            new Object[] { unit });
-    }
-
-
-    /**
-     * Produce output.
-     * @param artifactAndFacet current facet.
-     * @param attr  theme for facet
-     */
-    @Override
-    public void doOut(
-        ArtifactAndFacet artifactAndFacet,
-        Document         attr,
-        boolean          visible
-    ) {
-        String name = artifactAndFacet.getFacetName();
-
-        logger.debug("MiddleBedHeightGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        Facet facet = artifactAndFacet.getFacet();
-
-        if (facet == null) {
-            return;
-        }
-
-        Processor woutp = new WOutProcessor();
-        Processor bedp = new BedheightProcessor();
-        WOutProcessor processor = new WOutProcessor();
-        if (processor.canHandle(name)) {
-            processor.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
-        }
-        if (name.equals(MIDDLE_BED_HEIGHT_SINGLE) || name.equals(MIDDLE_BED_HEIGHT_EPOCH)) {
-            doHeightOut(
-                (MiddleBedHeightData) artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (name.equals(MIDDLE_BED_HEIGHT_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (FacetTypes.IS.AREA(name)) {
-            doArea(
-                artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(
-                artifactAndFacet.getData(context),
-                artifactAndFacet,
-                attr,
-                visible,
-                YAXIS.H.idx);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (bedp.canHandle(name)) {
-            bedp.doOut(this, artifactAndFacet, attr, visible, YAXIS.P.idx);
-        }
-        else if (woutp.canHandle(name)) {
-            woutp.doOut(this, artifactAndFacet, attr, visible, YAXIS.W.idx);
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-        }
-    }
-
-
-    /**
-     * @param data A data object
-     * @param aandf The artifact and facet. 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.
-     */
-    protected void doHeightOut(
-        MiddleBedHeightData data,
-        ArtifactAndFacet    aandf,
-        Document            theme,
-        boolean             visible
-    ) {
-        logger.debug("MiddleBedHeightGenerator.doMainChannelOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getMiddleHeightsPoints(), false, 0.110d);
-
-        addAxisSeries(series, YAXIS.H.idx, visible);
-    }
-
-
-    /** Look up the axis identifier for a given facet type. */
-    public int axisIdxForFacet(String facetName) {
-        if (FacetTypes.IS.H(facetName)) {
-            return YAXIS.H.idx;
-        }
-        else if (FacetTypes.IS.W(facetName)) {
-            return YAXIS.W.idx;
-        }
-        else {
-            logger.warn("Could not find axis for facet " + facetName);
-            return YAXIS.H.idx;
-        }
-    }
-
-
-    /**
-     * Do Area out.
-     * @param theme styling information.
-     * @param visible whether or not visible.
-     */
-    protected void doArea(
-        Object     o,
-        ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible
-    ) {
-        logger.debug("FlowVelocityGenerator.doArea");
-        logger.warn("TODO: Implement FlowVelocityGenerator.doArea");
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/MiddleBedHeightInfoGenerator.java	Fri Sep 13 18:29:01 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
- * middle bed height curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class MiddleBedHeightInfoGenerator
-extends      ChartInfoGenerator
-{
-    public MiddleBedHeightInfoGenerator() {
-        super(new MiddleBedHeightGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/OutGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -18,6 +18,7 @@
 import org.dive4elements.artifacts.Artifact;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.collections.D4EArtifactCollection;
+import org.dive4elements.river.themes.ThemeDocument;
 
 
 /**
@@ -28,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
@@ -63,7 +70,7 @@
      * producing the output.
      * @param visible Specifies, if this output should be visible or not.
      */
-    void doOut(ArtifactAndFacet bundle, Document attr, boolean visible);
+    void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible);
 
     /**
      * Writes the collected output of all artifacts specified in the
--- a/artifacts/src/main/java/org/dive4elements/river/exports/OutputHelper.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/OutputHelper.java	Fri Sep 27 17:36:50 2013 +0200
@@ -40,6 +40,7 @@
 import org.dive4elements.river.artifacts.model.ManagedDomFacet;
 import org.dive4elements.river.artifacts.model.ManagedFacet;
 import org.dive4elements.river.themes.Theme;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.themes.ThemeFactory;
 
 public class OutputHelper {
@@ -76,6 +77,8 @@
 
         ThemeList themeList = new ThemeList(attributes);
 
+        ThemeDocument themeDoc = new ThemeDocument(attributes);
+
         List<ArtifactAndFacet> dataProviders =
             doBlackboardPass(themeList, context, outName);
 
@@ -109,7 +112,7 @@
                 if (outName.equals("sq_overview")) {
                     generator.doOut(
                         dataProviders.get(i),
-                        attributes,
+                        themeDoc,
                         theme.getActive() == 1);
                 }
                 else {
@@ -141,7 +144,7 @@
      *
      * @return an attribute in form of a document.
      */
-    protected Document getFacetThemeFromAttribute(
+    protected ThemeDocument getFacetThemeFromAttribute(
         String      uuid,
         String      outName,
         String      facet,
@@ -219,7 +222,7 @@
         Document doc = XMLUtils.newDocument();
         doc.appendChild(doc.importNode(theme, true));
 
-        return doc;
+        return new ThemeDocument(doc);
     }
     /**
      * Adds the theme of a facet to a CollectionItem's attribute.
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveExporter.java	Fri Sep 27 17:36:50 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/ReferenceCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReferenceCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -15,6 +15,7 @@
 import org.dive4elements.river.artifacts.model.WWAxisTypes;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.Formatter;
 
 import java.awt.geom.Point2D;
@@ -25,7 +26,6 @@
 import org.jfree.chart.axis.TickUnits;
 import org.jfree.chart.axis.ValueAxis;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 /**
  * An OutGenerator that generates reference curves.
@@ -137,7 +137,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         String name = artifactFacet.getFacetName();
@@ -187,9 +187,9 @@
 
     /** Register DataSeries with (maybe transformed) points. */
     public void doReferenceOut(
-        Object   data,
-        Document theme,
-        boolean  visible
+        Object        data,
+        ThemeDocument theme,
+        boolean       visible
     ) {
         WW ww = (WW)data;
 
@@ -216,10 +216,10 @@
 
     // TODO resolve duplicate in DurationCurveGenerator
     protected void doPointOut(
-        Point2D point,
+        Point2D          point,
         ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
+        ThemeDocument    theme,
+        boolean          visible
     ){
         logger.debug("ReferenceCurveGenerator.doPointOut");
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ReportGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -24,6 +24,7 @@
 
 import org.dive4elements.river.artifacts.model.Calculation;
 import org.dive4elements.river.collections.D4EArtifactCollection;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import org.w3c.dom.Document;
 
@@ -35,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;
@@ -60,7 +68,7 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         logger.debug("doOut");
--- a/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/ShapeExporter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -14,6 +14,7 @@
 import org.dive4elements.artifacts.common.utils.FileTools;
 import org.dive4elements.artifacts.common.utils.XMLUtils;
 import org.dive4elements.river.collections.D4EArtifactCollection;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.w3c.dom.Document;
 
 
@@ -28,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;
@@ -47,7 +55,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
+    public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) {
         String name = bundle.getFacetName();
 
         if (!isFacetValid(name)) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/StyledSeriesBuilder.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/StyledSeriesBuilder.java	Fri Sep 27 17:36:50 2013 +0200
@@ -40,14 +40,63 @@
 
 
     /**
+     * Add points to series, create gaps if certain distance between
+     * points is met and scale the Y value.
+     *
+     * @param series Series to add points to.
+     * @param points Points to add to series, points[0] to 1st dim, points[1]
+     *               to 2nd dim.
+     * @param skipNANs if true, skip NAN values in points parameter. Otherwise,
+     *                 the NaNs lead to gaps in graph.
+     * @param distance if two consecutive entries in points[0] are more
+     *                 than distance apart, create a NaN value to skip in display.
+     *                 Still, create a line segment.
+     * @param factor   Factor by which to scale the y value (points[1]).
+     */
+    public static void addPointsFactorY(XYSeries series,
+        double[][] points,
+        boolean skipNANs,
+        double distance,
+        double factor
+    ) {
+        if (points == null || points.length <= 1) {
+            return;
+        }
+        double [] xPoints = points[0];
+        double [] yPoints = points[1];
+        for (int i = 0; i < xPoints.length; i++) {
+            if (skipNANs &&
+                (Double.isNaN(xPoints[i]) || Double.isNaN(yPoints[i]))) {
+                logger.warn ("Skipping NaN in StyledSeriesBuilder.");
+                continue;
+            }
+            // Create gap if distance >= distance.
+            if (i != 0 && Math.abs(xPoints[i-1] - xPoints[i]) >= distance) {
+                // Create at least a small segment for last point.
+                if (!Double.isNaN(yPoints[i-1])) {
+                    series.add(xPoints[i-1]+0.99d*(distance)/2.d, yPoints[i-1]*factor, false);
+                }
+
+                if (!Double.isNaN(yPoints[i-1]) && !Double.isNaN(yPoints[i])) {
+                    series.add((xPoints[i-1]+xPoints[i])/2.d, Double.NaN, false);
+                }
+            }
+            series.add(xPoints[i], yPoints[i]*factor, false);
+        }
+    }
+
+
+    /**
      * Add points to series, create gaps if certain distance between points is met.
      *
      * @param series Series to add points to.
      * @param points Points to add to series, points[0] to 1st dim, points[1]
      *               to 2nd dim.
-     * @param skipNANs if true, skip NAN values in points parameter.
+     * @param skipNANs if true, skip NAN values in points parameter. Otherwise,
+     *                 the NaNs lead to gaps in graph.
      * @param distance if two consecutive entries in points[0] are more
      *                 than distance apart, create a NaN value to skip in display.
+     *                 Still, create a line segment.
      */
     public static void addPoints(XYSeries series, double[][] points, boolean skipNANs, double distance) {
         if (points == null || points.length <= 1) {
@@ -63,6 +112,11 @@
             }
             // Create gap if distance >= distance.
             if (i != 0 && Math.abs(xPoints[i-1] - xPoints[i]) >= distance) {
+                // Create at least a small segment for last point.
+                if (!Double.isNaN(yPoints[i-1])) {
+                    series.add(xPoints[i-1]+0.99d*(distance)/2.d, yPoints[i-1], false);
+                }
+
                 if (!Double.isNaN(yPoints[i-1]) && !Double.isNaN(yPoints[i])) {
                     series.add((xPoints[i-1]+xPoints[i])/2.d, Double.NaN, false);
                 }
@@ -250,6 +304,27 @@
      * W values and scaling it with wScale.
      *
      * @param series Series to add points to.
+     * @param qws to add to series.
+     * @param wAdd Value to add to each Q while adding to series.
+     * @param wScale multiply with
+     */
+    public static void addPointsQW(XYSeries series, double[][] qws, double wTrans, double wScale) {
+        if (qws == null || qws.length == 0) {
+            return;
+        }
+
+        double x[] = qws[0];
+        double y[] = qws[1];
+
+        for (int i = 0; i < x.length; i++) {
+            series.add(x[i], wScale * (y[i] + wTrans), false);
+        }
+    }
+    /**
+     * Add points to series (q to 1st dim, w to 2nd dim), adding wTrans to the
+     * W values and scaling it with wScale.
+     *
+     * @param series Series to add points to.
      * @param wqkms WQKms to add to series.
      * @param wAdd Value to add to each Q while adding to series.
      * @param wScale multiply with
--- a/artifacts/src/main/java/org/dive4elements/river/exports/SyncNumberAxis.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/SyncNumberAxis.java	Fri Sep 27 17:36:50 2013 +0200
@@ -18,6 +18,7 @@
 /**
  * Axis which is to be registered with other axis and tries
  * to clone its range. The cloned range is transformed.
+ * Keeps in sync via AxisChangedEvents.
  */
 public class SyncNumberAxis extends IdentifiableNumberAxis
         implements AxisChangeListener
--- a/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/TimeseriesChartGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,6 +16,8 @@
 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;
 import java.awt.Font;
@@ -51,133 +53,13 @@
 import org.jfree.ui.Layer;
 import org.json.JSONArray;
 import org.json.JSONException;
-import org.w3c.dom.Document;
 
 /**
+ * Generator for diagrams with time on x axis.
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
-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
+public abstract class TimeseriesChartGenerator
+extends               ChartGenerator {
 
     protected List<Marker> domainMarker;
 
@@ -388,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);
     }
 
 
@@ -691,7 +573,7 @@
     protected void doPoints(
         Object     o,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument   theme,
         boolean    visible,
         int        axisIndex
     ) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesCurveGenerator.java	Fri Sep 13 18:29:01 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.jfree.chart.JFreeChart;
-import org.jfree.chart.plot.XYPlot;
-import org.w3c.dom.Document;
-
-
-/**
- * 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, Document 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	Fri Sep 13 18:29:01 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/WDifferencesExporter.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/WaterlevelExporter.java	Fri Sep 27 17:36:50 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);
-
-        this.data = new ArrayList<WQKms[]>();
+    public WaterlevelExporter() {
+        data = new ArrayList<WQKms[]>();
     }
 
-
     @Override
     public void generate()
     throws IOException
@@ -594,12 +586,11 @@
         writer.writeNext(new String[] {
             kmf.format(wqkm[2]),
             wf.format(wqkm[0]),
-            qf.format(wqkm[1]),
+            qf.format(RiverUtils.roundQ(wqkm[1])),
             RiverUtils.getLocationDescription(flys, wqkm[2])
         });
     }
 
-
     /** Write an csv-row at gauge location. */
     private void writeRow6(CSVWriter writer, double wqkm[], String wOrQDesc,
         D4EArtifact flys, String gaugeName) {
@@ -610,7 +601,7 @@
         writer.writeNext(new String[] {
             kmf.format(wqkm[2]),
             wf.format(wqkm[0]),
-            qf.format(wqkm[1]),
+            qf.format(RiverUtils.roundQ(wqkm[1])),
             wOrQDesc,
             RiverUtils.getLocationDescription(flys, wqkm[2]),
             gaugeName
@@ -913,12 +904,14 @@
 
         Locale locale = Resources.getLocale(meta);
         DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT, locale);
+        NumberFormat kmf = getKmFormatter();
 
         source.addMetaData("date", df.format(new Date()));
 
         RangeAccess rangeAccess = new RangeAccess(flys);
         double[] kms = rangeAccess.getKmRange();
-        source.addMetaData("range", kms[0] + " - " + kms[kms.length-1]);
+        source.addMetaData("range",
+                kmf.format(kms[0]) + " - " + kmf.format(kms[kms.length-1]));
 
         source.addMetaData("gauge", RiverUtils.getGaugename(flys));
 
@@ -969,7 +962,7 @@
                 source.addData(new String[] {
                     kmf.format(result[2]),
                     wf.format(result[0]),
-                    qf.format(result[1]),
+                    qf.format(RiverUtils.roundQ(result[1])),
                     desc,
                     RiverUtils.getLocationDescription(flys, result[2]),
                     result[2] >= a && result[2] <= b
@@ -981,7 +974,7 @@
                 source.addData(new String[] {
                     kmf.format(result[2]),
                     wf.format(result[0]),
-                    qf.format(result[1]),
+                    qf.format(RiverUtils.roundQ(result[1])),
                     desc,
                     RiverUtils.getLocationDescription(flys, result[2]),
                     result[2] >= a && result[2] <= b
--- a/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/XYChartGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -38,7 +38,6 @@
 import org.jfree.data.xy.XYSeriesCollection;
 import org.json.JSONArray;
 import org.json.JSONException;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.river.jfree.Bounds;
@@ -47,6 +46,9 @@
 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 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);
-        }
     }
 
 
@@ -991,7 +878,7 @@
     protected void doPoints(
         Object     o,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument theme,
         boolean    visible,
         int        axisIndex
     ) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/extreme/ExtremeWQCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -17,7 +17,6 @@
 import org.jfree.chart.plot.XYPlot;
 import org.jfree.chart.title.TextTitle;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
@@ -33,8 +32,8 @@
 import org.dive4elements.river.jfree.JFreeUtil;
 import org.dive4elements.river.jfree.StyledXYSeries;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
-import org.dive4elements.river.utils.ThemeUtil;
 
 
 /**
@@ -76,7 +75,7 @@
 
     /** First, ask parent to add data, then handle extreme_wq_curve(_base) data.*/
     @Override
-    public boolean prepareChartData(ArtifactAndFacet aaf, Document theme, boolean visible) {
+    public boolean prepareChartData(ArtifactAndFacet aaf, ThemeDocument theme, boolean visible) {
         if (super.prepareChartData(aaf, theme, visible)) {
             return true;
         }
@@ -104,7 +103,7 @@
     }
 
     /** Do Extreme Curve nonextrapolated points out. */
-    protected void doExtremeCurveBaseOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+    protected void doExtremeCurveBaseOut(ArtifactAndFacet aaf, ThemeDocument theme, boolean visible) {
         logger.debug("doExtremeCurveBaseOut");
         ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
         Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
@@ -136,7 +135,7 @@
 
 
     /** Do Extreme Curve out */
-    protected void doExtremeCurveOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+    protected void doExtremeCurveOut(ArtifactAndFacet aaf, ThemeDocument theme, boolean visible) {
         logger.debug("doExtremeCurveOut");
         ExtremeCurveFacet facet = (ExtremeCurveFacet) aaf.getFacet();
         Curve curve = (Curve) facet.getData(aaf.getArtifact(), context);
@@ -159,7 +158,7 @@
                 maxQ); // end
 
         // Add marker from where on its extrapolated.
-        if (ThemeUtil.parseShowExtraMark(theme)) {
+        if (theme.parseShowExtraMark()) {
             double[] qs = curve.getQs();
             double extrapolateFrom = qs[qs.length-1];
 
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/DeltaWtExporter.java	Fri Sep 27 17:36:50 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/FixATExport.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixATExport.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,6 +16,7 @@
 
 import org.dive4elements.river.artifacts.access.FixAccess;
 
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 import org.dive4elements.river.artifacts.math.fitting.Function;
@@ -37,7 +38,6 @@
 
 import org.apache.log4j.Logger;
 
-import org.w3c.dom.Document;
 import org.w3c.dom.NodeList;
 
 /** Export result of fixation analysis. */
@@ -52,7 +52,7 @@
 
 
     @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
+    public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) {
         logger.debug("AT Export doOut().");
         Object data = bundle.getData(context);
         if (data instanceof CalculationResult) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixChartGenerator.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDeltaWtGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -23,7 +23,7 @@
 import org.dive4elements.river.jfree.StyledDomainMarker;
 import org.dive4elements.river.jfree.StyledTimeSeries;
 import org.dive4elements.river.jfree.StyledValueMarker;
-import org.dive4elements.river.utils.ThemeUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.io.OutputStream;
 import java.text.NumberFormat;
@@ -35,13 +35,14 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.chart.annotations.XYTextAnnotation;
-import org.jfree.data.time.Day;
 import org.jfree.data.time.RegularTimePeriod;
 import org.jfree.data.time.FixedMillisecond;
 import org.jfree.data.time.TimeSeries;
 import org.jfree.data.time.TimeSeriesCollection;
 import org.w3c.dom.Document;
 
+import gnu.trove.TLongHashSet;
+
 
 /**
  * Generator for Delta W(t) charts.
@@ -80,7 +81,10 @@
     }
 
 
-    private D4EArtifact artifact = null;
+    private D4EArtifact artifact;
+
+    // Used to make the dates collision free.
+    private TLongHashSet uniqueDates = new TLongHashSet();
 
 
     @Override
@@ -156,7 +160,7 @@
     @Override
     public void doOut(
             ArtifactAndFacet artifactFacet,
-            Document         theme,
+            ThemeDocument    theme,
             boolean          visible
             ) {
         String name = artifactFacet.getFacetName();
@@ -226,11 +230,11 @@
 
 
     protected void doReferencePeriodsOut(
-            D4EArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
+            D4EArtifact   artifact,
+            Object        data,
+            String        desc,
+            ThemeDocument theme,
+            boolean       visible)
     {
         logger.debug("doReferencePeriodsOut()");
 
@@ -248,13 +252,19 @@
         }
     }
 
+    private long uniqueDate(long date) {
+        return uniqueDates.add(date)
+            ? date
+            : uniqueDate(date+30L*1000L); // add 30secs.
+    }
+
 
     protected void doSectorAverageOut(
-            D4EArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
+            D4EArtifact   artifact,
+            Object        data,
+            String        desc,
+            ThemeDocument theme,
+            boolean       visible)
     {
         logger.debug("doSectorAverageOut(): description = " + desc);
 
@@ -270,16 +280,16 @@
 
         // Draw a line spanning the analysis time.
         series.add(rtp, value);
-        rtp = new Day(qwd.dateRange.getFrom());
+        rtp = new FixedMillisecond(qwd.dateRange.getFrom());
         series.addOrUpdate(rtp, value);
-        rtp = new Day(qwd.dateRange.getTo());
+        rtp = new FixedMillisecond(qwd.dateRange.getTo());
         series.addOrUpdate(rtp, value);
 
         tsc.addSeries(series);
 
         addAxisDataset(tsc, 0, visible);
 
-        if (visible && ThemeUtil.parseShowLineLabel(theme)) {
+        if (visible && theme.parseShowLineLabel()) {
             List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
             XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
                     "\u0394 W(t) [cm] " + (float)Math.round(qwd.qwd.getDeltaW() * 10000) / 10000,
@@ -298,7 +308,7 @@
             D4EArtifact artifact,
             Object       data,
             String       desc,
-            Document     theme,
+            ThemeDocument theme,
             boolean      visible
             ) {
         logger.debug("doAnalysisEventsOut: desc = " + desc);
@@ -308,7 +318,7 @@
     }
 
 
-    protected void doQWDEventsOut(QWD qwd, String desc, Document theme, boolean visible)
+    protected void doQWDEventsOut(QWD qwd, String desc, ThemeDocument theme, boolean visible)
     {
         TimeSeriesCollection tsc = new TimeSeriesCollection();
 
@@ -324,7 +334,8 @@
 
         int idxInterpol = 0;
         int idxRegular = 0;
-        RegularTimePeriod rtp = new FixedMillisecond(qwd.getDate());
+        long time = uniqueDate(qwd.getDate().getTime());
+        RegularTimePeriod rtp = new FixedMillisecond(time);
         double value =  qwd.getDeltaW();
         boolean interpolate = qwd.getInterpolated();
         if (interpolate) {
@@ -358,11 +369,11 @@
      * @param annoIdxMap map of index in qwds to series/data item indices in tsc.
      */
     protected void doQWDTextAnnotations(Map<Integer, int[]> annoIdxMap,
-            TimeSeriesCollection tsc, QWD qwd, Document theme,
+            TimeSeriesCollection tsc, QWD qwd, ThemeDocument theme,
             boolean visible) {
         logger.debug("doQWDTextAnnotation()");
 
-        if (!visible || !ThemeUtil.parseShowPointLabel(theme)) {
+        if (!visible || !theme.parseShowPointLabel()) {
             logger.debug("doQWDTextAnnotation: annotation not visible");
             return;
         }
@@ -390,11 +401,11 @@
 
 
     protected void doReferenceEventsOut(
-            D4EArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible
+            D4EArtifact  artifact,
+            Object        data,
+            String        desc,
+            ThemeDocument theme,
+            boolean       visible
             ) {
         logger.debug("doReferenceEventsOut: desc = " + desc);
 
@@ -404,11 +415,11 @@
 
 
     protected void doDeviationOut(
-            D4EArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible
+            D4EArtifact   artifact,
+            Object        data,
+            String        desc,
+            ThemeDocument theme,
+            boolean       visible
             ) {
         logger.debug("doDeviationOut: desc = " + desc);
 
@@ -427,10 +438,10 @@
 
     protected void doAnalysisPeriodsOut(
             D4EArtifact artifact,
-            Object       data,
-            String       desc,
-            Document     theme,
-            boolean      visible)
+            Object        data,
+            String        desc,
+            ThemeDocument theme,
+            boolean       visible)
     {
         DateRange[] ranges = (DateRange[]) data;
         if (ranges == null || !visible) {
@@ -451,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);
 
@@ -462,7 +473,9 @@
 
         context.putContextValue("currentKm", currentKm);
 
-        StyledValueMarker marker = new StyledValueMarker(0, request);
+        // XXX: This looks hackish!
+        StyledValueMarker marker =
+            new StyledValueMarker(0, new ThemeDocument(request));
         valueMarker.add(marker);
     }
 }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixDerivedCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,7 +9,6 @@
 package org.dive4elements.river.exports.fixings;
 
 import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.river.artifacts.model.FacetTypes;
@@ -19,6 +18,7 @@
 import org.dive4elements.river.exports.ChartGenerator;
 import org.dive4elements.river.jfree.JFreeUtil;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
 
 /**
  * Generator for fixation derived function curve.
@@ -65,7 +65,7 @@
 
 
     @Override
-    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    public void doOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doOut");
 
         if (FacetTypes.IS.MANUALPOINTS(aaf.getFacetName())) {
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,319 +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.fixings;
-
-import java.awt.BasicStroke;
-import java.awt.Color;
-
-import org.apache.log4j.Logger;
-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;
-import org.dive4elements.river.exports.ChartGenerator;
-import org.dive4elements.river.exports.StyledSeriesBuilder;
-import org.dive4elements.river.exports.process.KMIndexProcessor;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.exports.process.WOutProcessor;
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
-import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.utils.DataUtil;
-import org.dive4elements.river.utils.KMIndex;
-import org.dive4elements.river.utils.RiverUtils;
-import org.jfree.chart.plot.Marker;
-import org.jfree.chart.plot.ValueMarker;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-public class FixLongitudinalSectionGenerator
-extends FixChartGenerator
-implements FacetTypes
-{
-    private static Logger logger =
-            Logger.getLogger(FixLongitudinalSectionGenerator.class);
-
-    public static final String I18N_CHART_TITLE =
-            "chart.fixings.longitudinalsection.title";
-
-    public static final String I18N_CHART_SUBTITLE =
-            "chart.fixings.longitudinalsection.subtitle";
-
-    public static final String I18N_XAXIS_LABEL =
-            "chart.fixings.longitudinalsection.xaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT  =
-            "Fixierungsanalyse";
-
-    public static final String I18N_XAXIS_LABEL_DEFAULT  =
-            "[km]";
-
-    public static final String I18N_DW_YAXIS_LABEL_DEFAULT  =
-            "delta W [cm]";
-
-    public static final String I18N_DW_YAXIS_LABEL =
-            "chart.fixings.longitudinalsection.yaxis.label";
-
-    public static final String I18N_W_YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.label";
-
-    public static final String I18N_Q_YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.second.label";
-
-    public static final String I18N_W_YAXIS_LABEL_DEFAULT  = "W [NN + m]";
-    public static final String I18N_Q_YAXIS_LABEL_DEFAULT = "Q [m\u00b3/s]";
-
-    public static enum YAXIS {
-        dW(0), W(1), Q(2);
-        public int idx;
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    @Override
-    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
-        String name = aaf.getFacetName();
-        logger.debug("FixLongitudinalSectionGenerator: doOut: " + name);
-
-        Processor processor = new KMIndexProcessor();
-        Processor wProcessor = new WOutProcessor();
-        if (name.contains(FIX_SECTOR_AVERAGE_LS_DEVIATION)) {
-            doSectorAverageDeviationOut(aaf, doc, visible);
-        }
-        else if (processor.canHandle(name)) {
-            processor.doOut(this, aaf, doc, visible, YAXIS.dW.idx);
-        }
-        else if (wProcessor.canHandle(name)) {
-            wProcessor.doOut(this, aaf, doc, visible, YAXIS.W.idx);
-        }
-        else if (name.equals(STATIC_WQKMS_Q)) {
-            doQOut(
-                (WQKms) aaf.getData(context),
-                aaf,
-                doc,
-                visible);
-        }
-        else if (name.equals(FIX_DEVIATION_LS)) {
-            doReferenceDeviationOut(aaf, doc, visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                    (RiverAnnotation) aaf.getData(context),
-                    aaf,
-                    doc,
-                    visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints (aaf.getData(context),
-                    aaf,
-                    doc, visible, YAXIS.dW.idx);
-        }
-        else {
-            logger.warn("Unknown facet name " + name);
-        }
-    }
-
-    /**
-     * Process the output for Q facets in a longitudinal section curve.
-     *
-     * @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.
-     */
-    protected void doQOut(
-        WQKms    wqkms,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean  visible
-    ) {
-        logger.debug("LongitudinalSectionGenerator.doQOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
-
-        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;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void doSectorAverageDeviationOut(
-            ArtifactAndFacet aaf,
-            Document doc,
-            boolean visible)
-    {
-        logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex());
-
-        int index = aaf.getFacet().getIndex();
-        int sectorNdx = index & 3;
-
-        KMIndex<AnalysisPeriod> kms =
-                (KMIndex<AnalysisPeriod>)aaf.getData(context);
-
-        if(kms == null) {
-            return;
-        }
-
-        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(doc);
-        XYSeries upper =
-                new StyledXYSeries(aaf.getFacetDescription(), false, doc);
-        XYSeries lower =
-                new StyledXYSeries(aaf.getFacetDescription() + " ", false, doc);
-
-        for (KMIndex.Entry<AnalysisPeriod> entry: kms) {
-            double km = entry.getKm();
-            AnalysisPeriod ap = entry.getValue();
-            QWD qwd = ap.getQSectorAverages()[sectorNdx];
-            double dev = ap.getQSectorStdDev(sectorNdx);
-            logger.debug("std-dev: " + dev);
-            if (qwd == null) {
-                continue;
-            }
-            double deltaW = qwd.getDeltaW();
-            double up = deltaW + dev;
-            double lo = deltaW - dev;
-            upper.add(km, up);
-            lower.add(km, lo);
-        }
-        area.addSeries(upper);
-        area.addSeries(lower);
-
-        addAreaSeries(area, 0, visible);
-    }
-
-
-    @SuppressWarnings("unchecked")
-    protected void doReferenceDeviationOut(
-            ArtifactAndFacet aaf,
-            Document doc,
-            boolean visible)
-    {
-        logger.debug("doReferenceOut");
-
-        KMIndex<double[]> kms =
-                (KMIndex<double[]>)aaf.getData(context);
-
-        if(kms == null) {
-            return;
-        }
-
-        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(doc);
-        XYSeries upper =
-                new StyledXYSeries(aaf.getFacetDescription(), false, doc);
-        XYSeries lower =
-                new StyledXYSeries(aaf.getFacetDescription() + " ", false, doc);
-
-
-        for (KMIndex.Entry<double[]> entry: kms) {
-            double km = entry.getKm();
-            double[] devArray = entry.getValue();
-            if (devArray == null) {
-                continue;
-            }
-            double dev = devArray[0];
-            double up = dev;
-            double lo = -dev;
-            upper.add(km, up, false);
-            lower.add(km, lo, false);
-        }
-        area.addSeries(upper);
-        area.addSeries(lower);
-
-        Marker marker = new ValueMarker(0);
-        marker.setStroke(new BasicStroke(2));
-        marker.setPaint(Color.BLACK);
-        addValueMarker(marker);
-        addAreaSeries(area, 0, visible);
-    }
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL, I18N_XAXIS_LABEL_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        if (pos == YAXIS.dW.idx) {
-            return msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.W.idx) {
-            D4EArtifact flys = (D4EArtifact) master;
-            String unit = RiverUtils.getRiver(flys).getWstUnit().getName();
-            return msg(
-                I18N_W_YAXIS_LABEL,
-                I18N_W_YAXIS_LABEL_DEFAULT,
-                new Object[] { unit });
-        }
-        else if (pos == YAXIS.Q.idx) {
-            return msg(I18N_Q_YAXIS_LABEL, I18N_Q_YAXIS_LABEL_DEFAULT);
-        }
-        return "";
-    }
-
-    @Override
-    protected ChartGenerator.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();
-            }
-        };
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixLongitudinalSectionInfoGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,19 +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.fixings;
-
-import org.dive4elements.river.exports.ChartInfoGenerator;
-
-public class FixLongitudinalSectionInfoGenerator extends ChartInfoGenerator {
-
-    public FixLongitudinalSectionInfoGenerator() {
-        super(new FixLongitudinalSectionGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/FixWQCurveGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -27,7 +27,6 @@
 import org.jfree.ui.RectangleAnchor;
 import org.jfree.ui.RectangleInsets;
 import org.jfree.ui.TextAnchor;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
@@ -57,8 +56,8 @@
 import org.dive4elements.river.jfree.StyledXYSeries;
 import org.dive4elements.river.model.Gauge;
 import org.dive4elements.river.model.River;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
-import org.dive4elements.river.utils.ThemeUtil;
 
 /**
  * Generator for WQ fixing charts.
@@ -175,7 +174,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    public void doOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doOut: " + aaf.getFacetName());
         if (!prepareChartData(aaf, doc, visible)) {
             logger.warn("Unknown facet, name " + aaf.getFacetName());
@@ -186,7 +185,7 @@
      * Return true if data could be handled,
      * to be overridden to add more handled data.
      */
-    public boolean prepareChartData(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    public boolean prepareChartData(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         String name = aaf.getFacetName();
 
         this.artifact = (D4EArtifact) aaf.getArtifact();
@@ -257,7 +256,7 @@
 
 
     /** Add sector average points to chart. */
-    protected void doSectorAverageOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doSectorAverageOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doSectorAverageOut");
 
         QWDDateRange qwdd = (QWDDateRange) aaf.getData(context);
@@ -272,7 +271,7 @@
     }
 
     /** Add analysis event points to chart. */
-    protected void doAnalysisEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doAnalysisEventsOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doAnalysisEventsOut");
 
         QWD qwd = (QWD)aaf.getData(context);
@@ -292,7 +291,7 @@
             textAnnos.add(anno);
 
             addAxisSeries(series, YAXIS.W.idx, visible);
-            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
+            if(visible && doc.parseShowPointLabel()) {
                 RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, doc);
                 flysAnno.setTextAnnotations(textAnnos);
                 addAnnotations(flysAnno);
@@ -305,42 +304,42 @@
 
 
     /** Add reference event points to chart. */
-    protected void doReferenceEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doReferenceEventsOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doReferenceEventsOut");
 
         QWI qwd = (QWI)aaf.getData(context);
-        if(qwd != null) {
-            XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), doc);
-            List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
-
-            DateFormat dateFormat = DateFormat.getDateInstance(
-                    DateFormat.SHORT);
-
-            series.add(qwd.getQ(), qwd.getW());
+        if (qwd == null) {
+            logger.debug("doReferenceEventsOut: qwds == null");
+            return;
+        }
 
-            XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
-                    dateFormat.format(qwd.getDate()),
-                    qwd.getQ(),
-                    qwd.getW());
-            textAnnos.add(anno);
+        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, true, doc);
+        List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
 
-            addAxisSeries(series, YAXIS.W.idx, visible);
-            if(visible && ThemeUtil.parseShowPointLabel(doc)) {
-                RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, doc);
-                flysAnno.setTextAnnotations(textAnnos);
-                addAnnotations(flysAnno);
-            }
-        }
-        else {
-            logger.debug("doReferenceEventsOut: qwds == null");
+        DateFormat dateFormat = DateFormat.getDateInstance(
+                DateFormat.SHORT);
+
+        series.add(qwd.getQ(), qwd.getW(), false);
+
+        XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                dateFormat.format(qwd.getDate()),
+                qwd.getQ(),
+                qwd.getW());
+        textAnnos.add(anno);
+
+        addAxisSeries(series, YAXIS.W.idx, visible);
+        if(visible && doc.parseShowPointLabel()) {
+            RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, doc);
+            flysAnno.setTextAnnotations(textAnnos);
+            addAnnotations(flysAnno);
         }
     }
 
 
     private void addPointFromWQKms(WQKms wqkms,
-        String title,
-        Document theme,
-        boolean visible
+        String        title,
+        ThemeDocument theme,
+        boolean       visible
     ) {
         XYSeries series = new StyledXYSeries(title, theme);
         Double ckm = (Double) context.getContextValue(CURRENT_KM);
@@ -351,9 +350,9 @@
         double[] kms = wqkms.getKms();
         for (int i = 0 ; i< kms.length; i++) {
             if (Math.abs(kms[i] - ckm) <= EPSILON) {
-                series.add(wqkms.getQ(i), wqkms.getW(i));
+                series.add(wqkms.getQ(i), wqkms.getW(i), false);
                 addAxisSeries(series, YAXIS.W.idx, visible);
-                if(visible && ThemeUtil.parseShowPointLabel(theme)) {
+                if(visible && theme.parseShowPointLabel()) {
                     List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
                     XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
                             title,
@@ -369,7 +368,7 @@
         }
     }
 
-    protected void doEventsOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doEventsOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doEventsOut");
         // Find W/Q at km.
         addPointFromWQKms((WQKms) aaf.getData(context),
@@ -377,7 +376,7 @@
     }
 
 
-    protected void doWQCurveOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doWQCurveOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doWQCurveOut");
 
         FixWQCurveFacet facet = (FixWQCurveFacet)aaf.getFacet();
@@ -428,7 +427,7 @@
         }
     }
 
-    protected void doOutlierOut(ArtifactAndFacet aaf, Document doc, boolean visible) {
+    protected void doOutlierOut(ArtifactAndFacet aaf, ThemeDocument doc, boolean visible) {
         logger.debug("doOutlierOut");
 
         QWI[] qws = (QWI[])aaf.getData(context);
@@ -437,7 +436,7 @@
 
 
     /** Add markers for q sectors. */
-    protected void doQSectorOut(ArtifactAndFacet aaf, Document theme, boolean visible) {
+    protected void doQSectorOut(ArtifactAndFacet aaf, ThemeDocument theme, boolean visible) {
         logger.debug("doQSectorOut");
         if (!visible) {
             return;
@@ -465,8 +464,8 @@
             Marker m = new ValueMarker(qsector.getValue());
             m.setPaint(Color.black);
 
-            float[] dashes = ThemeUtil.parseLineStyle(theme);
-            int size       = ThemeUtil.parseLineWidth(theme);
+            float[] dashes = theme.parseLineStyle();
+            int size       = theme.parseLineWidth();
             BasicStroke stroke;
             if (dashes.length <= 1) {
                 stroke = new BasicStroke(size);
@@ -481,12 +480,12 @@
             }
             m.setStroke(stroke);
 
-            if (ThemeUtil.parseShowLineLabel(theme)) {
+            if (theme.parseShowLineLabel()) {
                 m.setLabel(qsector.getName());
-                m.setPaint(ThemeUtil.parseTextColor(theme));
-                m.setLabelFont(ThemeUtil.parseTextFont(theme));
+                m.setPaint(theme.parseTextColor());
+                m.setLabelFont(theme.parseTextFont());
             }
-            Color paint = ThemeUtil.parseLineColorField(theme);
+            Color paint = theme.parseLineColorField();
             if (paint != null) {
                 m.setPaint(paint);
             }
@@ -504,10 +503,10 @@
      * @param theme theme to use.
      */
     protected void doWAnnotations(
-            Object   wqkms,
+            Object           wqkms,
             ArtifactAndFacet aandf,
-            Document theme,
-            boolean  visible
+            ThemeDocument    theme,
+            boolean          visible
             ) {
         Facet facet = aandf.getFacet();
 
@@ -549,7 +548,7 @@
             WINFOArtifact artifact,
             Object        o,
             String        description,
-            Document      theme,
+            ThemeDocument theme,
             boolean       visible)
     {
         WQKms wqkms = (WQKms) o;
@@ -583,7 +582,7 @@
     protected void doWQOut(
             Object           wqkms,
             ArtifactAndFacet aaf,
-            Document         theme,
+            ThemeDocument    theme,
             boolean          visible
             ) {
         logger.debug("FixWQCurveGenerator: doWQOut");
@@ -598,7 +597,7 @@
             logger.debug("FixWQCurveGenerator: doWQOut: double[][]");
             double [][] data = (double [][]) wqkms;
 
-            XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+            XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), false, true, theme);
             StyledSeriesBuilder.addPoints(series, data, true);
 
             addAxisSeries(series, YAXIS.W.idx, visible);
@@ -609,14 +608,18 @@
     protected void addQWSeries(
             QWI []           qws,
             ArtifactAndFacet aaf,
-            Document         theme,
+            ThemeDocument    theme,
             boolean          visible
             ) {
         if (qws == null) {
             return;
         }
 
-        XYSeries series = new StyledXYSeries(aaf.getFacetDescription(), theme);
+        XYSeries series = new StyledXYSeries(
+            aaf.getFacetDescription(),
+            false, true,
+            theme);
+
         List<XYTextAnnotation> textAnnos =
                 new ArrayList<XYTextAnnotation>(qws.length);
 
@@ -624,7 +627,7 @@
                 DateFormat.SHORT);
 
         for (QWI qw: qws) {
-            series.add(qw.getQ(), qw.getW());
+            series.add(qw.getQ(), qw.getW(), false);
 
             XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
                     dateFormat.format(qw.getDate()),
@@ -634,7 +637,7 @@
         }
 
         addAxisSeries(series, YAXIS.W.idx, visible);
-        if (visible && ThemeUtil.parseShowPointLabel(theme)) {
+        if (visible && theme.parseShowPointLabel()) {
             RiverAnnotation flysAnno =
                     new RiverAnnotation(null, null, null, theme);
             flysAnno.setTextAnnotations(textAnnos);
--- a/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/fixings/ParametersExporter.java	Fri Sep 27 17:36:50 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/BedDiffHeightYearGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,134 +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.minfo;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-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.minfo.BedDifferencesResult;
-import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
-import org.dive4elements.river.exports.process.KMIndexProcessor;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.jfree.RiverAnnotation;
-
-
-public class BedDiffHeightYearGenerator
-extends BedDiffBaseGenerator
-implements FacetTypes
-{
-    public enum YAXIS {
-        D(0), dW(1);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(BedDiffHeightYearGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.beddifference.height.title";
-    public static final String I18N_XAXIS_LABEL = "chart.beddifference.height.xaxis.label";
-    public static final String I18N_YAXIS_LABEL = "chart.beddifference.height.yaxis.label";
-
-    public static final String I18N_CHART_TITLE_DEFAULT = "Sohlenhöhen Differenz";
-    public static final String I18N_XAXIS_LABEL_DEFAULT = "Fluss-Km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT = "delta S [cm / Jahr]";
-    public static final String I18N_DW_YAXIS_LABEL_DEFAULT  =
-            "delta W [cm]";
-    public static final String I18N_DW_YAXIS_LABEL =
-            "chart.fixings.longitudinalsection.yaxis.label";
-
-    @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();
-            }
-        };
-    }
-
-    @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean 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;
-        }
-
-        if (bundle.getData(context) instanceof BedDifferencesResult) {
-            setContextBounds(bundle);
-        }
-
-        Processor processor = new KMIndexProcessor();
-        Processor bdyProcessor = new BedDiffHeightYearProcessor();
-        if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        else if (processor.canHandle(name)) {
-            processor.doOut(this, bundle, attr, visible, YAXIS.dW.idx);
-        }
-        else if (bdyProcessor.canHandle(name)) {
-            bdyProcessor.doOut(this, bundle, attr, visible, YAXIS.D.idx);
-        }
-        else {
-            logger.warn("Unknown facet name " + name);
-        }
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] { getRiverName() });
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        if (pos == YAXIS.D.idx) {
-            return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.dW.idx) {
-            return msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
-        }
-        return "default";
-    }
-}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffHeightYearInfoGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +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.minfo;
-
-import org.dive4elements.river.exports.ChartInfoGenerator;
-
-
-public class BedDiffHeightYearInfoGenerator
-extends ChartInfoGenerator
-{
-    public BedDiffHeightYearInfoGenerator() {
-        super (new BedDiffHeightYearGenerator());
-    }
-}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDiffYearInfoGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +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.minfo;
-
-import org.dive4elements.river.exports.ChartInfoGenerator;
-
-
-public class BedDiffYearInfoGenerator
-extends ChartInfoGenerator
-{
-    public BedDiffYearInfoGenerator() {
-        super(new BedDifferenceYearGenerator());
-    }
-}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceEpochGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,7 +10,6 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
@@ -28,7 +27,7 @@
 import org.dive4elements.river.jfree.DoubleBounds;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 import org.dive4elements.river.utils.RiverUtils;
 
 
@@ -47,7 +46,7 @@
     }
 
     /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(BedQualityGenerator.class);
+    private static Logger logger = Logger.getLogger(BedDifferenceEpochGenerator.class);
 
     public static final String I18N_CHART_TITLE = "chart.beddifference.epoch.title";
     public static final String I18N_XAXIS_LABEL = "chart.beddifference.xaxis.label";
@@ -86,7 +85,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
+    public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) {
         String name = bundle.getFacetName();
 
         logger.debug("doOut: " + name);
@@ -215,7 +214,7 @@
     }
 
     protected void doBedDifferenceEpochOut(BedDiffEpochResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
+        ArtifactAndFacet aandf, ThemeDocument theme, boolean visible) {
 
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
         StyledSeriesBuilder.addPoints(series, data.getDifferencesData(), true);
@@ -226,7 +225,7 @@
     private void doBedDifferenceHeightsOut(
         BedDiffEpochResult data,
         ArtifactAndFacet bundle,
-        Document attr,
+        ThemeDocument attr,
         boolean visible,
         int idx) {
          logger.debug("doBedDifferenceHeightOut()");
@@ -245,7 +244,7 @@
     protected void doWDifferencesOut(
         WKms       wkms,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument   theme,
         boolean    visible
     ) {
         if (wkms == null) {
@@ -257,8 +256,9 @@
         StyledSeriesBuilder.addPoints(series, wkms);
 
         addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
+        if (wkms.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/minfo/BedDifferenceExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedDifferenceExporter.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,242 +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.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifactdatabase.state.Facet;
-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.minfo.BedDiffYearResult;
-import org.dive4elements.river.artifacts.model.minfo.BedDifferencesResult;
-import org.dive4elements.river.artifacts.model.minfo.MorphologicWidth;
-import org.dive4elements.river.exports.StyledSeriesBuilder;
-import org.dive4elements.river.exports.process.BedDiffYearProcessor;
-import org.dive4elements.river.exports.process.BedDiffHeightYearProcessor;
-import org.dive4elements.river.exports.process.KMIndexProcessor;
-import org.dive4elements.river.exports.process.Processor;
-import org.dive4elements.river.exports.process.WOutProcessor;
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.utils.DataUtil;
-import org.dive4elements.river.utils.RiverUtils;
-
-
-public class BedDifferenceYearGenerator
-extends BedDiffBaseGenerator
-implements FacetTypes
-{
-    public enum YAXIS {
-        D(0), M(1), H(2), dW(3), W(4);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(BedDifferenceYearGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.beddifference.year.title";
-    public static final String I18N_XAXIS_LABEL = "chart.beddifference.xaxis.label";
-    public static final String I18N_YAXIS_LABEL = "chart.beddifference.yaxis.label.diff";
-    public static final String I18N_SECOND_YAXIS_LABEL = "chart.beddifference.yaxis.label.morph";
-    public static final String I18N_THIRD_YAXIS_LABEL = "chart.beddifference.yaxis.label.heights";
-
-    public static final String I18N_CHART_TITLE_DEFAULT = "Sohlenhöhen Differenz";
-    public static final String I18N_XAXIS_LABEL_DEFAULT = "Fluss-Km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT = "delta S [m]";
-    public static final String I18N_SECOND_YAXIS_LABEL_DEFAULT = "Breite [m]";
-    public static final String I18N_THIRD_YAXIS_LABEL_DEFAULT = "Höhe [m]";
-    public static final String I18N_DW_YAXIS_LABEL_DEFAULT  =
-            "delta W [cm]";
-    public static final String I18N_DW_YAXIS_LABEL =
-            "chart.fixings.longitudinalsection.yaxis.label";
-    private static final String I18N_W_YAXIS_LABEL =
-        "chart.longitudinal.section.yaxis.label";
-    private static final String I18N_W_YAXIS_LABEL_DEFAULT = "W [NN + m]";
-
-    @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();
-            }
-        };
-    }
-
-
-    @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean 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;
-        }
-
-        if (bundle.getData(context) instanceof BedDifferencesResult) {
-            setContextBounds(bundle);
-        }
-
-        Processor processor = new KMIndexProcessor();
-        Processor woutp = new WOutProcessor();
-        Processor bdhyProcessor = new BedDiffHeightYearProcessor();
-        Processor bdyProcessor = new BedDiffYearProcessor();
-        if (name.equals(BED_DIFFERENCE_MORPH_WIDTH)) {
-            doBedDifferenceMorphWidthOut(
-                (BedDiffYearResult) bundle.getData(context),
-                bundle, attr, visible);
-        }
-        else if (name.equals(MORPHOLOGIC_WIDTH)) {
-            doMorphologicWidthOut(
-                (MorphologicWidth)bundle.getData(context),
-                bundle,
-                attr,
-                visible,
-                0);
-        }
-        else if (processor.canHandle(name)) {
-            processor.doOut(this, bundle, attr, visible, YAXIS.dW.idx);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) bundle.getData(context),
-                 bundle,
-                 attr,
-                 visible);
-        }
-        else if (bdyProcessor.canHandle(name)) {
-            bdyProcessor.doOut(this, bundle, attr, visible, YAXIS.H.idx);
-        }
-        else if (bdhyProcessor.canHandle(name)) {
-            bdhyProcessor.doOut(this, bundle, attr, visible, YAXIS.D.idx);
-        }
-        else if (woutp.canHandle(name)) {
-            woutp.doOut(this, bundle, attr, visible, YAXIS.W.idx);
-        }
-        else if (name.equals(W_DIFFERENCES)) {
-            doWDifferencesOut(
-                (WKms) bundle.getData(context),
-                bundle,
-                attr,
-                visible);
-        }
-        else {
-            logger.warn("Unknown facet name " + name);
-        }
-    }
-
-    private void doMorphologicWidthOut(
-        MorphologicWidth data,
-        ArtifactAndFacet bundle,
-        Document attr,
-        boolean visible,
-        int i) {
-        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
-        StyledSeriesBuilder.addPoints(series, data.getAsArray(), true);
-
-        addAxisSeries(series, YAXIS.M.idx, visible);
-    }
-
-
-    @Override
-    protected String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL,
-                   I18N_XAXIS_LABEL_DEFAULT,
-                   new Object[] { getRiverName() });
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int pos) {
-        String label = "default";
-        D4EArtifact flys = (D4EArtifact) master;
-        if (pos == YAXIS.D.idx) {
-            label = msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.M.idx) {
-            label = msg(I18N_SECOND_YAXIS_LABEL, I18N_SECOND_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.H.idx) {
-            label = msg(I18N_THIRD_YAXIS_LABEL,
-                I18N_THIRD_YAXIS_LABEL_DEFAULT,
-                new Object[]
-                    { RiverUtils.getRiver(flys).getWstUnit().getName() });
-        }
-        else if (pos == YAXIS.dW.idx) {
-            label = msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
-        }
-        else if (pos == YAXIS.W.idx) {
-            return msg(I18N_W_YAXIS_LABEL, I18N_W_YAXIS_LABEL_DEFAULT,
-                new Object[]
-                    { RiverUtils.getRiver(flys).getWstUnit().getName() });
-        }
-
-        return label;
-    }
-
-
-    protected void doBedDifferenceMorphWidthOut(BedDiffYearResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("doBedDifferencesMorphWidthOut()");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getMorphWidthData(), true);
-
-        addAxisSeries(series, YAXIS.M.idx, visible);
-    }
-
-    protected void doWDifferencesOut(
-        WKms       wkms,
-        ArtifactAndFacet aandf,
-        Document   theme,
-        boolean    visible
-    ) {
-        if (wkms == null) {
-            logger.warn("No data to add to WDifferencesChart.");
-            return;
-         }
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, wkms);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-        if (DataUtil.guessWaterIncreasing(wkms.allWs())) {
-            setInverted(true);
-        }
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityExporter.java	Fri Sep 27 17:36:50 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/BedQualityGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,311 +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.minfo;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-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.minfo.BedDiameterData;
-import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult;
-import org.dive4elements.river.artifacts.model.minfo.BedParametersResult;
-import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterResult;
-import org.dive4elements.river.exports.StyledSeriesBuilder;
-import org.dive4elements.river.exports.XYChartGenerator;
-import org.dive4elements.river.jfree.RiverAnnotation;
-import org.dive4elements.river.jfree.StyledXYSeries;
-
-
-/**
- * An OutGenerator that generates bed quality charts.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedQualityGenerator extends XYChartGenerator implements FacetTypes {
-
-    public enum YAXIS {
-        W(0), P(1), D(2);
-
-        protected int idx;
-
-        private YAXIS(int c) {
-            idx = c;
-        }
-    }
-
-    /** The logger that is used in this generator. */
-    private static Logger logger = Logger.getLogger(BedQualityGenerator.class);
-
-    public static final String I18N_CHART_TITLE = "chart.bedquality.title";
-    public static final String I18N_XAXIS_LABEL = "chart.bedquality.xaxis.label";
-    public static final String I18N_YAXIS_LABEL = "chart.bedquality.yaxis.label";
-    public static final String I18N_SECOND_YAXIS_LABEL = "chart.bedquality.yaxis.label.porosity";
-    public static final String I18N_THIRD_YAXIS_LABEL = "chart.bedquality.yaxis.label.diameter";
-
-    public static final String I18N_CHART_TITLE_DEFAULT = "Sohlen Längsschnitt";
-    public static final String I18N_XAXIS_LABEL_DEFAULT = "Fluss-Km";
-    public static final String I18N_YAXIS_LABEL_DEFAULT = "Durchmesser [mm]";
-    public static final String I18N_SECOND_YAXIS_LABEL_DEFAULT = "Porosität [%]";
-    public static final String I18N_THIRD_YAXIS_LABEL_DEFAULT = "Dichte [t/m^3]";
-
-    @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();
-            }
-        };
-    }
-
-    /**
-     * Returns the default title for this chart.
-     *
-     * @return the default title for this chart.
-     */
-    @Override
-    public String getDefaultChartTitle() {
-        return msg(I18N_CHART_TITLE, I18N_CHART_TITLE_DEFAULT);
-    }
-
-    /**
-     * Get internationalized label for the x axis.
-     */
-    @Override
-    protected String getDefaultXAxisLabel() {
-        return msg(I18N_XAXIS_LABEL,
-            I18N_XAXIS_LABEL_DEFAULT,
-            new Object[] {getRiverName()});
-    }
-
-    @Override
-    protected String getDefaultYAxisLabel(int index) {
-        String label = "default";
-
-        if (index == YAXIS.W.idx) {
-            label = getWAxisLabel();
-        }
-        else if (index == YAXIS.P.idx) {
-            label = getPAxisLabel();
-        }
-        else if (index == YAXIS.D.idx) {
-            label = getDAxisLabel();
-        }
-
-        return label;
-    }
-
-    /**
-     * Get internationalized label for the y axis displaying the diameter.
-     */
-    protected String getWAxisLabel() {
-        return msg(I18N_YAXIS_LABEL, I18N_YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Get internationalized label for the y axis displaying the porosity.
-     */
-    protected String getPAxisLabel() {
-        return msg(I18N_SECOND_YAXIS_LABEL, I18N_SECOND_YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Get internationalized label for the y axis displaying the density.
-     */
-    protected String getDAxisLabel() {
-        return msg(I18N_THIRD_YAXIS_LABEL, I18N_THIRD_YAXIS_LABEL_DEFAULT);
-    }
-
-    /**
-     * Produce output.
-     *
-     * @param artifactAndFacet
-     *            current facet.
-     * @param attr
-     *            theme for facet
-     */
-    public void doOut(ArtifactAndFacet artifactAndFacet, Document attr,
-        boolean visible) {
-        String name = artifactAndFacet.getFacetName();
-
-        logger.debug("BedQualityGenerator.doOut: " + name);
-
-        if (name == null) {
-            logger.error("No facet name for doOut(). No output generated!");
-            return;
-        }
-
-        Facet facet = artifactAndFacet.getFacet();
-
-        if (facet == null) {
-            return;
-        }
-
-        // TODO BED_QUALITY_BED_DIAMETER_TOPLAYER
-        if (name.equals(BED_QUALITY_BED_DIAMETER_TOPLAYER)) {
-            doBedDiameterTopLayerOut(
-                (BedDiameterResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_QUALITY_BED_DIAMETER_SUBLAYER)) {
-            doBedDiameterSubLayerOut(
-                (BedDiameterResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        // TODO BED_QUALITY_BED_DIAMETER_SUBLAYER
-        else if (name.equals(BED_QUALITY_BEDLOAD_DIAMETER)) {
-            doBedLoadDiameterOut(
-                (BedloadDiameterResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_QUALITY_POROSITY_TOPLAYER)) {
-            doPorosityTopLayerOut(
-                (BedParametersResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_QUALITY_POROSITY_SUBLAYER)) {
-            doPorositySubLayerOut(
-                (BedParametersResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER)) {
-            doDensityTopLayerOut(
-                (BedParametersResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER)) {
-            doDensitySubLayerOut(
-                (BedParametersResult) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(BED_DIAMETER_DATA_TOP) ||
-                 name.equals(BED_DIAMETER_DATA_SUB) ||
-                 name.equals(BEDLOAD_DIAMETER_DATA)) {
-            doBedDiameterDataOut(
-                (BedDiameterData) artifactAndFacet.getData(context),
-                artifactAndFacet, attr, visible);
-        }
-        else if (name.equals(LONGITUDINAL_ANNOTATION)) {
-            doAnnotations(
-                (RiverAnnotation) artifactAndFacet.getData(context),
-                 artifactAndFacet,
-                 attr,
-                 visible);
-        }
-        else if (FacetTypes.IS.MANUALPOINTS(name)) {
-            doPoints(artifactAndFacet.getData(context), artifactAndFacet, attr,
-                visible, YAXIS.W.idx);
-        }
-        else {
-            logger.warn("Unknown facet name: " + name);
-            return;
-        }
-    }
-
-    private void doBedDiameterDataOut(
-        BedDiameterData data,
-        ArtifactAndFacet aandf,
-        Document theme,
-        boolean visible
-    ) {
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterData(), true);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    protected void doBedDiameterTopLayerOut(BedDiameterResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doBedDiameterTopLayerOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterCapData(), true);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    protected void doBedDiameterSubLayerOut(BedDiameterResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doBedDiameterSubLayerOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterSubData(), true);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    protected void doBedLoadDiameterOut(BedloadDiameterResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doBedLoadDiameterOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getDiameterData(), true);
-
-        addAxisSeries(series, YAXIS.W.idx, visible);
-    }
-
-    protected void doPorosityTopLayerOut(BedParametersResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doPorosityTopLayerOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getPorosityCapData(),
-            true);
-
-        addAxisSeries(series, YAXIS.P.idx, visible);
-    }
-
-    protected void doPorositySubLayerOut(BedParametersResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doPorositySubLayerOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getPorositySubData(),
-            true);
-
-        addAxisSeries(series, YAXIS.P.idx, visible);
-    }
-
-    protected void doDensityTopLayerOut(BedParametersResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doDensityOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getDensityCapData(),
-            true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-
-    protected void doDensitySubLayerOut(BedParametersResult data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
-        logger.debug("BedQuality.doDensityOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-
-        StyledSeriesBuilder.addPoints(series, data.getDensitySubData(),
-            true);
-
-        addAxisSeries(series, YAXIS.D.idx, visible);
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/BedQualityInfoGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,26 +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.minfo;
-
-import org.dive4elements.river.exports.ChartInfoGenerator;
-
-
-/**
- * A ChartInfoGenerator that generates meta information for specific computed
- * bed quality curves.
- *
- * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
- */
-public class BedQualityInfoGenerator extends ChartInfoGenerator {
-
-    public BedQualityInfoGenerator() {
-        super(new BedQualityGenerator());
-    }
-}
-// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadExporter.java	Fri Sep 27 17:36:50 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;
@@ -35,7 +31,8 @@
 import au.com.bytecode.opencsv.CSVWriter;
 
 
-/** Do CSV export for sediment load calculations (will also be shown in
+/**
+ * Do CSV export for sediment load calculations (will also be shown in
  * client). */
 public class SedimentLoadExporter
 extends      AbstractExporter
@@ -62,6 +59,9 @@
     public static final String CSV_SUSP_SAND =
         "export.sedimentload_ls.csv.header.suspsand";
 
+    public static final String CSV_SUSP_SAND_BB =
+        "export.sedimentload_ls.csv.header.suspsandbb";
+
     public static final String CSV_SUSP_SEDIMENT =
         "export.sedimentload_ls.csv.header.suspsediment";
 
@@ -74,48 +74,64 @@
 
     /** 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 {
         writeCSVHeader(writer);
 
         for (SedimentLoadResult result: results) {
+            String years = (result.getEndYear() == 0)
+                    ? result.getStartYear() + " "
+                    : result.getStartYear() + "-" + result.getEndYear();
             SedimentLoad load = result.getLoad();
             // Put load.getName()+load.getDescription()}); somewhere?
             for (double km: new TreeSet<Double>(load.getKms())) {
                 SedimentLoadFraction fraction = load.getFraction(km);
-                writeRecord(writer, km, result.getStartYear(), result.getEndYear(), fraction);
+                writeRecord(writer, km, years, fraction);
             }
         }
     }
 
+
+    /** Return space when val is NaN, apply NumberFormat otherwise. */
+    private String numberToString(NumberFormat valf, double val) {
+        if (Double.isNaN(val)) {
+            return " ";
+        }
+        return valf.format(val);
+    }
+
+    /** Return space when val is NaN or zero, apply NumberFormat otherwise. */
+    private String nonZeroToString(NumberFormat valf, double val) {
+        if (Double.isNaN(val) || val == 0d) {
+            return " ";
+        }
+        return valf.format(val);
+    }
+
     /** Write a line. */
-    private void writeRecord(CSVWriter writer, double km, int fromYear, int toYear, SedimentLoadFraction fraction) {
+    private void writeRecord(
+        CSVWriter writer,
+        double km,
+        String years,
+        SedimentLoadFraction fraction
+    ) {
         // year, total, susp sed, susp sandbed suspsand, sand, finemiddle, coarse
-        String years = (toYear == 0) ? fromYear+"" : fromYear + "-" + toYear;
         NumberFormat kmf = Formatter.getCalculationKm(context.getMeta());
         NumberFormat valf = Formatter.getFormatter(context.getMeta(), 0, 2);
         writer.writeNext(new String[] {
             kmf.format(km),
             years,
-            valf.format(fraction.getTotal()),
-            valf.format(fraction.getSand()),
-            valf.format(fraction.getFineMiddle()),
-            valf.format(fraction.getCoarse()),
-            valf.format(fraction.getSuspSand()),
-            //valf.format(fraction.getSuspSandBed()),
-            valf.format(fraction.getSuspSediment())
+            numberToString(valf, fraction.getSuspSediment()),
+            numberToString(valf, fraction.getSuspSand()),
+            numberToString(valf, fraction.getSuspSandBed()),
+            numberToString(valf, fraction.getSand()),
+            numberToString(valf, fraction.getFineMiddle()),
+            numberToString(valf, fraction.getCoarse()),
+            nonZeroToString(valf, fraction.getTotal())
         });
     }
 
@@ -125,15 +141,15 @@
 
         List<String> header = new LinkedList<String>();
         if (results != null)  {
-            header.add(msg(CSV_KM, "km"));
-            header.add(msg(CSV_YEAR, "Jahr"));
-            header.add(msg(CSV_TOTAL, "Gesamt"));
-            header.add(msg(CSV_SAND, "Sand"));
-            header.add(msg(CSV_FINEMIDDLE,"Fein"));
-            header.add(msg(CSV_COARSE,    "Grob"));
-            header.add(msg(CSV_SUSP_SAND, "Su.Sand"));
+            header.add(msg(CSV_KM,          "km"));
+            header.add(msg(CSV_YEAR,        "Jahr"));
             header.add(msg(CSV_SUSP_SEDIMENT, "Schwebst."));
-            //header.add("Susp.Sand Bett");
+            header.add(msg(CSV_SUSP_SAND,   "Susp.Sand"));
+            header.add(msg(CSV_SUSP_SAND_BB, "Susp.Sand(BB)"));
+            header.add(msg(CSV_SAND,        "Sand"));
+            header.add(msg(CSV_FINEMIDDLE,  "Kies(f+m)"));
+            header.add(msg(CSV_COARSE,      "Kies(g)"));
+            header.add(msg(CSV_TOTAL,       "Gesamt"));
         }
         writer.writeNext(header.toArray(new String[header.size()]));
     }
--- a/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/minfo/SedimentLoadLSGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -13,7 +13,6 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
@@ -33,7 +32,7 @@
 import org.dive4elements.river.jfree.DoubleBounds;
 import org.dive4elements.river.jfree.RiverAnnotation;
 import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.utils.DataUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 
 
 /** Generator for Longitudinal Sections of SedimentLoad-Calculations. */
@@ -94,7 +93,7 @@
     }
 
     @Override
-    public void doOut(ArtifactAndFacet bundle, Document attr, boolean visible) {
+    public void doOut(ArtifactAndFacet bundle, ThemeDocument attr, boolean visible) {
         String name = bundle.getFacetName();
 
         logger.debug("doOut: " + name);
@@ -259,7 +258,7 @@
     }
 
     protected void doSedimentLoadOut(double[][] data,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
+        ArtifactAndFacet aandf, ThemeDocument theme, boolean visible) {
 
         // Allow for gaps (NaNs).
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), false, theme);
@@ -269,7 +268,7 @@
     }
 
     protected void doSedimentLoadUnknownOut(SedimentLoad load,
-        ArtifactAndFacet aandf, Document theme, boolean visible) {
+        ArtifactAndFacet aandf, ThemeDocument theme, boolean visible) {
 
         Set<Double> kms = load.getKms();
         double[][] data = new double[2][kms.size()];
@@ -289,7 +288,7 @@
     protected void doFlowVelocityMainOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
@@ -301,7 +300,7 @@
     protected void doFlowVelocityTotalOut(
         FlowVelocityData data,
         ArtifactAndFacet aandf,
-        Document         theme,
+        ThemeDocument    theme,
         boolean          visible
     ) {
         if (data == null) {
@@ -318,7 +317,7 @@
     protected void doBedDifferenceYearOut(
         BedDiffYearResult data,
         ArtifactAndFacet aandf,
-        Document theme,
+        ThemeDocument theme,
         boolean visible
     ) {
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
@@ -330,7 +329,7 @@
     protected void doBedDifferenceEpochOut(
         BedDiffEpochResult data,
         ArtifactAndFacet aandf,
-        Document theme,
+        ThemeDocument theme,
         boolean visible
     ) {
         XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
@@ -342,7 +341,7 @@
     protected void doWDifferencesOut(
         WKms       wkms,
         ArtifactAndFacet aandf,
-        Document   theme,
+        ThemeDocument   theme,
         boolean    visible
     ) {
         if (wkms == null) {
@@ -355,7 +354,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/SedimentLoadLSInfoGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,20 +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.minfo;
-
-import org.dive4elements.river.exports.ChartInfoGenerator;
-
-
-public class SedimentLoadLSInfoGenerator
-extends ChartInfoGenerator
-{
-   public SedimentLoadLSInfoGenerator() {
-        super(new SedimentLoadLSGenerator());
-   }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AnnotationProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,86 @@
+/* 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) ||
+            facetType.equals(FacetTypes.MIDDLE_BED_HEIGHT_ANNOTATION) ||
+            facetType.equals(FacetTypes.FLOW_VELOCITY_ANNOTATION);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/AreaProcessor.java	Fri Sep 27 17:36:50 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffHeightYearProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,39 +10,70 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
 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);
 
+    protected static double GAP_TOLERANCE = 0.101d;
+
+    public static final String I18N_AXIS_LABEL =
+        "chart.beddifference.height.yaxis.label";
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "delta S [cm / Jahr]";
+
+    @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,
-            Document theme,
+            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");
     }
 
@@ -56,15 +87,23 @@
 
     protected void doBedDifferenceYearOut(XYChartGenerator generator,
         BedDiffYearResult data,
-        ArtifactAndFacet aandf,
-        Document theme,
+        ArtifactAndFacet bundle,
+        ThemeDocument theme,
         boolean visible,
         int axidx) {
 
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(), theme);
-        StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), true);
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), theme);
+        StyledSeriesBuilder.addPoints(series, data.getHeightPerYearData(), false, GAP_TOLERANCE);
 
         generator.addAxisSeries(series, axidx, visible);
     }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_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/BedDiffYearProcessor.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedDiffYearProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,7 +10,6 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
@@ -18,33 +17,85 @@
 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);
 
+    protected static double GAP_TOLERANCE = 0.101d;
+
+    public static final String I18N_AXIS_LABEL =
+        "chart.beddifference.yaxis.label.diff";
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "delta S [cm]";
+
+    @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,
-            Document theme,
+            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");
@@ -59,25 +110,11 @@
             || BED_DIFFERENCE_YEAR_HEIGHT2_FILTERED.equals(facetType);
     }
 
-    private void doBedDifferenceHeightsOut(
-        XYChartGenerator generator,
-        BedDiffYearResult data,
-        ArtifactAndFacet bundle,
-        Document attr,
-        boolean visible,
-        int idx,
-        int axidx) {
-         logger.debug("doBedDifferenceHeightsOut()");
-
-        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), attr);
-        if (idx == 0) {
-            StyledSeriesBuilder.addPoints(series, data.getHeights1Data(), true);
-        }
-        else {
-            StyledSeriesBuilder.addPoints(series, data.getHeights2Data(), true);
-        }
-
-        generator.addAxisSeries(series, axidx, visible);
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
     }
 }
 // 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/BedHeightSoundingProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,114 @@
+/* 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.process;
+
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+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 BedHeightSoundingProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(BedHeightSoundingProcessor.class);
+
+    private final static String BEDHEIGHT = "bedheight";
+
+    public static final String I18N_AXIS_LABEL_DEFAULT
+        = "Gepeilte Breite [m]";
+    public static final String I18N_AXIS_LABEL =
+        "chart.bedheight_middle.sounding.yaxis.label";
+
+    @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 bundle,
+            ThemeDocument theme,
+            boolean visible,
+            int index
+    ) {
+        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, index, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return BEDHEIGHT.equals(facettype);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDensityProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,72 @@
+/* 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.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.minfo.BedParametersResult;
+
+public class BedQualityDensityProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(BedQualityDensityProcessor.class);
+
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Dichte [t/m^3]";
+    public static final String I18N_AXIS_LABEL =
+        "chart.bedquality.yaxis.label.density";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        Object data = bundle.getData(context);
+        String facetName = bundle.getFacetName();
+        double [][] points;
+
+        if (facetName.equals(FacetTypes.BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER)) {
+            points = ((BedParametersResult) data).getDensityCapData();
+        } else if (facetName.equals(FacetTypes.BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER)) {
+            points = ((BedParametersResult) data).getDensitySubData();
+        } else {
+            logger.error("Unknown facet name: " + facetName);
+            return;
+        }
+        StyledSeriesBuilder.addPoints(series, points, true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.BED_QUALITY_SEDIMENT_DENSITY_SUBLAYER) ||
+            facettype.equals(FacetTypes.BED_QUALITY_SEDIMENT_DENSITY_TOPLAYER);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityDiameterProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,85 @@
+/* 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.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.minfo.BedDiameterData;
+import org.dive4elements.river.artifacts.model.minfo.BedDiameterResult;
+import org.dive4elements.river.artifacts.model.minfo.BedloadDiameterResult;
+
+public class BedQualityDiameterProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(BedQualityDiameterProcessor.class);
+
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Durchmesser [mm]";
+    public static final String I18N_AXIS_LABEL =
+        "chart.bedquality.yaxis.label.diameter";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        Object data = bundle.getData(context);
+        String facetName = bundle.getFacetName();
+        double [][] points;
+
+        if (facetName.equals(FacetTypes.BED_QUALITY_BED_DIAMETER_TOPLAYER)) {
+            points = ((BedDiameterResult) data).getDiameterCapData();
+        } else if (facetName.equals(FacetTypes.BED_QUALITY_BED_DIAMETER_SUBLAYER)) {
+            points = ((BedDiameterResult) data).getDiameterSubData();
+        } else if (facetName.equals(FacetTypes.BED_QUALITY_BEDLOAD_DIAMETER)) {
+            points = ((BedloadDiameterResult) data).getDiameterData();
+        } else if (facetName.equals(FacetTypes.BED_DIAMETER_DATA_TOP) ||
+                 facetName.equals(FacetTypes.BED_DIAMETER_DATA_SUB) ||
+                 facetName.equals(FacetTypes.BEDLOAD_DIAMETER_DATA)) {
+            points = ((BedDiameterData) data).getDiameterData();
+        } else {
+            logger.error("Unknown facet name: " + facetName);
+            return;
+        }
+        StyledSeriesBuilder.addPoints(series, points, true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.BED_QUALITY_BED_DIAMETER_TOPLAYER) ||
+            facettype.equals(FacetTypes.BED_QUALITY_BED_DIAMETER_SUBLAYER) ||
+            facettype.equals(FacetTypes.BED_QUALITY_BEDLOAD_DIAMETER) ||
+            facettype.equals(FacetTypes.BED_DIAMETER_DATA_TOP) ||
+            facettype.equals(FacetTypes.BED_DIAMETER_DATA_SUB) ||
+            facettype.equals(FacetTypes.BEDLOAD_DIAMETER_DATA);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedQualityPorosityProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,74 @@
+/* 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.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.minfo.BedParametersResult;
+
+public class BedQualityPorosityProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(BedQualityPorosityProcessor.class);
+
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Porosität [%]";
+    public static final String I18N_AXIS_LABEL =
+        "chart.bedquality.yaxis.label.porosity";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        Object data = bundle.getData(context);
+        String facetName = bundle.getFacetName();
+        double [][] points;
+
+        if (facetName.equals(FacetTypes.BED_QUALITY_POROSITY_TOPLAYER)) {
+            points = ((BedParametersResult) data).getPorosityCapData();
+        } else if (facetName.equals(FacetTypes.BED_QUALITY_BED_DIAMETER_SUBLAYER)) {
+            points = ((BedParametersResult) data).getPorositySubData();
+        } else {
+            logger.error("Unknown facet name: " + facetName);
+            return;
+        }
+        StyledSeriesBuilder.addPoints(series, points, true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.BED_QUALITY_POROSITY_TOPLAYER) ||
+            facettype.equals(FacetTypes.BED_QUALITY_POROSITY_SUBLAYER);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/BedWidthProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,71 @@
+/* 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.process;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.minfo.BedDiffYearResult;
+import org.dive4elements.river.artifacts.model.minfo.MorphologicWidth;
+
+public class BedWidthProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(BedWidthProcessor.class);
+
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Breite [m]";
+    public static final String I18N_AXIS_LABEL =
+        "chart.beddifference.yaxis.label.morph";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        Object data = bundle.getData(context);
+
+        if (data instanceof BedDiffYearResult) {
+            BedDiffYearResult bData = (BedDiffYearResult) data;
+            StyledSeriesBuilder.addPoints(series, bData.getMorphWidthData(), true);
+        } else if (data instanceof MorphologicWidth) {
+            MorphologicWidth bData = (MorphologicWidth) data;
+            StyledSeriesBuilder.addPoints(series, bData.getAsArray(), true);
+        } else {
+            logger.error("Unknown data for facet: " + bundle.getFacetName());
+        }
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.BED_DIFFERENCE_MORPH_WIDTH) ||
+            facettype.equals(FacetTypes.MORPHOLOGIC_WIDTH);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/BedheightProcessor.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +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.process;
-
-import java.util.List;
-
-import org.apache.log4j.Logger;
-import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
-
-import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
-import org.dive4elements.artifacts.CallContext;
-import org.dive4elements.river.artifacts.model.minfo.BedHeightSingle;
-import org.dive4elements.river.exports.XYChartGenerator;
-import org.dive4elements.river.jfree.StyledXYSeries;
-import org.dive4elements.river.model.BedHeightSingleValue;
-
-public class BedheightProcessor implements Processor {
-
-    private final static Logger logger =
-            Logger.getLogger(BedheightProcessor.class);
-
-    private final static String BEDHEIGHT = "bedheight";
-
-    @Override
-    public void doOut(
-            XYChartGenerator generator,
-            ArtifactAndFacet aandf,
-            Document theme,
-            boolean visible,
-            int index
-    ) {
-        CallContext context = generator.getCallContext();
-        Object data = aandf.getData(context);
-        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");
-    }
-
-    private void doBedheightSingeValuesOut(XYChartGenerator generator,
-            ArtifactAndFacet aandf, Document theme, int index, boolean visible,
-            List<BedHeightSingleValue> data) {
-        logger.debug("doBedheightSingleOut");
-
-        XYSeries series = new StyledXYSeries(aandf.getFacetDescription(),
-                theme);
-        for(BedHeightSingleValue bvalue: data) {
-            series.add(bvalue.getStation(), bvalue.getSoundingWidth());
-        }
-        generator.addAxisSeries(series, index, visible);
-    }
-
-    @Override
-    public boolean canHandle(String facettype) {
-        return BEDHEIGHT.equals(facettype);
-    }
-
-    public void doBedheightSingleOut(XYChartGenerator generator,
-            ArtifactAndFacet aandf, Document 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	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,74 @@
+/* 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 null;
+    }
+
+
+    /**
+     * 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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/DeltaWProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,263 @@
+/* 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.process;
+
+import org.apache.log4j.Logger;
+
+import java.awt.BasicStroke;
+import java.awt.Color;
+
+import org.jfree.data.xy.XYSeries;
+import org.jfree.data.xy.XYSeriesCollection;
+
+import org.jfree.chart.plot.Marker;
+import org.jfree.chart.plot.ValueMarker;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.fixings.AnalysisPeriod;
+import org.dive4elements.river.artifacts.model.fixings.QWD;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.jfree.StyledAreaSeriesCollection;
+import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.utils.KMIndex;
+
+public class DeltaWProcessor extends DefaultProcessor {
+    /* This is basically a collection of different processors. The
+     * historic reason for this is that they have in common that they
+     * work on deltaW data from the fixing analysis. */
+
+    private static final Logger logger = Logger.getLogger(DeltaWProcessor.class);
+
+    public static final String I18N_DW_YAXIS_LABEL_DEFAULT  =
+            "delta W [cm]";
+
+    public static final String I18N_DW_YAXIS_LABEL =
+            "chart.fixings.longitudinalsection.yaxis.label";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        String facettype = bundle.getFacetName();
+        if (!visible) {
+            return;
+        }
+        logger.debug("Doing out for: " + bundle.getFacetName());
+        if (facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)) {
+            doReferenceEventsOut(generator, bundle, theme, visible);
+        } else if (facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)) {
+            doAnalysisEventsOut(generator, bundle, theme, visible);
+        } else if (facettype.startsWith(FacetTypes.FIX_SECTOR_AVERAGE_LS_DEVIATION)) {
+            doSectorAverageDeviationOut(generator, bundle, theme, visible);
+        } else if (facettype.equals(FacetTypes.FIX_DEVIATION_LS)) {
+            doReferenceDeviationOut(generator, bundle, theme, visible);
+        } else if (facettype.startsWith(FacetTypes.FIX_SECTOR_AVERAGE_LS)) {
+            doSectorAverageOut(generator, bundle, theme, visible);
+        } else {
+            logger.error("Could not handle: " + facettype);
+        }
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        if (facettype == null) {
+            return false;
+        }
+
+        if (facettype.startsWith(FacetTypes.FIX_SECTOR_AVERAGE_LS)
+                || facettype.equals(FacetTypes.FIX_REFERENCE_EVENTS_LS)
+                || facettype.equals(FacetTypes.FIX_ANALYSIS_EVENTS_LS)
+                || facettype.equals(FacetTypes.FIX_DEVIATION_LS)) {
+            return true;
+        }
+        return false;
+    }
+
+    private void doSectorAverageOut(DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument doc, boolean visible) {
+        CallContext context = generator.getCallContext();
+        int index = bundle.getFacet().getIndex();
+        int sectorNdx = index & 3;
+
+        KMIndex<AnalysisPeriod> kms =
+                (KMIndex<AnalysisPeriod>)bundle.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(), doc);
+
+        for (KMIndex.Entry<AnalysisPeriod> entry: kms) {
+            double km = entry.getKm();
+            AnalysisPeriod ap = entry.getValue();
+            QWD qwd = ap.getQSectorAverages()[sectorNdx];
+            if (qwd == null) {
+                continue;
+            }
+            double deltaW = qwd.getDeltaW();
+            series.add(km, deltaW);
+        }
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    private void doReferenceEventsOut(DiagramGenerator generator,
+            ArtifactAndFacet bundle, ThemeDocument doc, boolean visible) {
+        CallContext context = generator.getCallContext();
+
+        KMIndex<QWD> kms =
+                (KMIndex<QWD>)bundle.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeriesCollection col = new XYSeriesCollection();
+
+        StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false,
+                doc);
+
+        for (KMIndex.Entry<QWD> entry: kms) {
+            double km = entry.getKm();
+            QWD qwd = entry.getValue();
+
+            series.add(km, qwd.getDeltaW());
+        }
+        col.addSeries(series);
+
+        generator.addAxisDataset(col, axisName, visible);
+    }
+
+    private void doAnalysisEventsOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument doc,
+            boolean visible) {
+        CallContext context = generator.getCallContext();
+
+        KMIndex<QWD> kms =
+                (KMIndex<QWD>)bundle.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        XYSeriesCollection col = new XYSeriesCollection();
+
+        StyledXYSeries series = new StyledXYSeries(bundle.getFacetDescription(), false, doc);
+
+        for (KMIndex.Entry<QWD> entry: kms) {
+            double km = entry.getKm();
+            QWD qwd = entry.getValue();
+
+            series.add(km, qwd.getDeltaW());
+        }
+        col.addSeries(series);
+
+        generator.addAxisDataset(col, axisName, visible);
+    }
+
+    protected void doSectorAverageDeviationOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument doc,
+            boolean visible) {
+        CallContext context = generator.getCallContext();
+
+        int index = bundle.getFacet().getIndex();
+        int sectorNdx = index & 3;
+
+        KMIndex<AnalysisPeriod> kms =
+                (KMIndex<AnalysisPeriod>)bundle.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(doc);
+        XYSeries upper =
+                new StyledXYSeries(bundle.getFacetDescription(), false, doc);
+        XYSeries lower =
+                new StyledXYSeries(bundle.getFacetDescription() + " ", false, doc);
+
+        for (KMIndex.Entry<AnalysisPeriod> entry: kms) {
+            double km = entry.getKm();
+            AnalysisPeriod ap = entry.getValue();
+            QWD qwd = ap.getQSectorAverages()[sectorNdx];
+            double dev = ap.getQSectorStdDev(sectorNdx);
+            if (qwd == null) {
+                continue;
+            }
+            double deltaW = qwd.getDeltaW();
+            double up = deltaW + dev;
+            double lo = deltaW - dev;
+            upper.add(km, up);
+            lower.add(km, lo);
+        }
+        area.addSeries(upper);
+        area.addSeries(lower);
+
+        generator.addAreaSeries(area, axisName, visible);
+    }
+
+    protected void doReferenceDeviationOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument doc,
+            boolean visible) {
+        CallContext context = generator.getCallContext();
+
+        KMIndex<double[]> kms =
+                (KMIndex<double[]>)bundle.getData(context);
+
+        if(kms == null) {
+            return;
+        }
+
+        StyledAreaSeriesCollection area = new StyledAreaSeriesCollection(doc);
+        XYSeries upper =
+                new StyledXYSeries(bundle.getFacetDescription(), false, doc);
+        XYSeries lower =
+                new StyledXYSeries(bundle.getFacetDescription() + " ", false, doc);
+
+        for (KMIndex.Entry<double[]> entry: kms) {
+            double km = entry.getKm();
+            double[] devArray = entry.getValue();
+            if (devArray == null) {
+                continue;
+            }
+            double dev = devArray[0];
+            double up = dev;
+            double lo = -dev;
+            upper.add(km, up, false);
+            lower.add(km, lo, false);
+        }
+        area.addSeries(upper);
+        area.addSeries(lower);
+
+        Marker marker = new ValueMarker(0);
+        marker.setStroke(new BasicStroke(2));
+        marker.setPaint(Color.BLACK);
+        generator.addValueMarker(marker);
+        generator.addAreaSeries(area, axisName, visible);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(I18N_DW_YAXIS_LABEL, I18N_DW_YAXIS_LABEL_DEFAULT);
+    }
+}
+// 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/DischargeProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,201 @@
+/* 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.process;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.exports.DischargeCurveGenerator;
+import org.dive4elements.river.exports.XYChartGenerator;
+import org.dive4elements.river.jfree.CollisionFreeXYTextAnnotation;
+import org.dive4elements.river.jfree.RiverAnnotation;
+import org.dive4elements.river.jfree.StickyAxisAnnotation;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.jfree.chart.annotations.XYTextAnnotation;
+
+
+/** Helper for data handling in discharge diagrams. */
+public class DischargeProcessor
+extends DefaultProcessor implements FacetTypes {
+
+    private final static Logger logger =
+            Logger.getLogger(DischargeProcessor.class);
+
+    /** Station for which the diagram is shown. */
+    private double km;
+
+    /** Tolerance for comparison of kilometers. */
+    public static final double KM_EPSILON = 0.001d;
+
+
+    /** This processor needs to be constructed with a given km. */
+    private DischargeProcessor() {
+        km = Double.NaN;
+    }
+
+
+    public DischargeProcessor(double km) {
+        this.km = km;
+    }
+
+
+    /** Process data, add it to plot. */
+    @Override
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument theme,
+            boolean visible,
+            int axisIndex
+    ) {
+        CallContext context = generator.getCallContext();
+        Object data = bundle.getData(context);
+        if (false && data instanceof WQKms) {
+            doWQKmsPointOut(
+                generator, (WQKms) data, bundle, theme, visible, axisIndex);
+            return;
+        }
+        else if (data instanceof RiverAnnotation) {
+            doRiverAnnotationOut(
+                generator, (RiverAnnotation) data, bundle, theme, visible);
+            return;
+        }
+        else if (data instanceof double[][]) {
+            doMarksOut(
+                generator, (double[][]) data, bundle, theme, visible);
+            return;
+        }
+        else {
+            logger.error("Can't process "
+                + data.getClass().getName() + " objects of facet "
+                + bundle.getFacetName());
+        }
+    }
+
+
+    /** True if this processor knows how to deal with facetType. */
+    @Override
+    public boolean canHandle(String facetType) {
+        return /*STATIC_WQKMS_W.equals(facetType)
+            ||*/ COMPUTED_DISCHARGE_MAINVALUES_Q.equals(facetType)
+            || MAINVALUES_Q.equals(facetType)
+            || COMPUTED_DISCHARGE_MAINVALUES_W.equals(facetType)
+            || MAINVALUES_W.equals(facetType)
+            || STATIC_W_INTERPOL.equals(facetType);
+    }
+
+
+    /** The station of the current calculation/view. */
+    protected double getKm() {
+        return km;
+    }
+
+
+    /** Handle WQKms data by finding w/q values at given km. */
+    protected void doWQKmsPointOut(XYChartGenerator generator,
+        WQKms wqkms,
+        ArtifactAndFacet bundle,
+        ThemeDocument theme,
+        boolean visible,
+        int axidx
+    ) {
+        logger.debug("doWQKmsPointOut");
+        String title = bundle.getFacetDescription();
+        XYSeries series = new StyledXYSeries(
+            title,
+            theme);
+
+        double[] kms = wqkms.getKms();
+
+        for (int i = 0 ; i< kms.length; i++) {
+            if (Math.abs(kms[i] - getKm()) <= KM_EPSILON) {
+                series.add(wqkms.getQ(i), wqkms.getW(i));
+                generator.addAxisSeries(series, axidx, visible);
+                if(visible && theme.parseShowPointLabel()) {
+                    List<XYTextAnnotation> textAnnos = new ArrayList<XYTextAnnotation>();
+                    XYTextAnnotation anno = new CollisionFreeXYTextAnnotation(
+                            title,
+                            wqkms.getQ(i),
+                            // TODO add a percentage to the extend of W axis
+                            wqkms.getW(i));
+                    textAnnos.add(anno);
+                    RiverAnnotation flysAnno = new RiverAnnotation(null, null, null, theme);
+                    flysAnno.setTextAnnotations(textAnnos);
+                    generator.addAnnotations(flysAnno);
+                }
+                return;
+            }
+        }
+
+        logger.warn("No WQ found for km " + getKm());
+    }
+
+    protected void doRiverAnnotationOut(XYChartGenerator generator,
+        RiverAnnotation annotations,
+        ArtifactAndFacet bundle,
+        ThemeDocument theme,
+        boolean visible
+    ) {
+        if (!(generator instanceof DischargeCurveGenerator)) {
+            logger.error("DischargeProcessor can only be used in " +
+                " in DischargeCurveGenerator-classes.");
+            return;
+        }
+        logger.debug("doRiverAnnotationOut");
+        DischargeCurveGenerator dGenerator =
+            (DischargeCurveGenerator) generator;
+
+        dGenerator.translateRiverAnnotation(annotations);
+        dGenerator.doAnnotations(
+            annotations,
+            bundle, theme, visible);
+    }
+
+
+    /**
+     * Put Sticky Axis Markers to Y-axis for each value.
+     * @param data [[-,y1],[-,y2],...] ('x'-coordinates ignored)
+     */
+    protected void doMarksOut(XYChartGenerator generator,
+        double[][] data,
+        ArtifactAndFacet bundle,
+        ThemeDocument theme,
+        boolean visible
+    ) {
+        logger.debug("doMarksOut");
+
+        if (!visible) {
+            return;
+        }
+
+        // TODO subtract gauge null point if at gauge.
+        String title = bundle.getFacetDescription();
+        List<StickyAxisAnnotation> yMarks = new ArrayList<StickyAxisAnnotation>();
+
+        for (double yPos: data[1]) {
+            yMarks.add(new StickyAxisAnnotation(
+                title,
+                (float) yPos,
+                StickyAxisAnnotation.SimpleAxis.Y_AXIS));
+        }
+
+        generator.doAnnotations(new RiverAnnotation(title, yMarks),
+            bundle, theme, visible);
+    }
+}
+// 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/FlowVelocityProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,90 @@
+/* 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.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.model.FlowVelocityMeasurementValue.FastFlowVelocityMeasurementValue;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+
+public class FlowVelocityProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(FlowVelocityProcessor.class);
+
+    public static final String I18N_AXIS_LABEL =
+        "chart.flow_velocity.section.yaxis.label";
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Geschwindigkeit v [m/s]";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        String facetName = bundle.getFacetName();
+        Object data = bundle.getData(context);
+        if (data == null) {
+            // Check has been here before so we keep it for security reasons
+            // this should never happen though.
+            logger.error("Data is null for facet: " + facetName);
+            return;
+        }
+        double [][] points;
+
+        if (facetName.equals(FacetTypes.FLOW_VELOCITY_TOTALCHANNEL) ||
+                facetName.equals(FacetTypes.FLOW_VELOCITY_TOTALCHANNEL_FILTERED)) {
+            FlowVelocityData fData = (FlowVelocityData) data;
+            points = fData.getTotalChannelPoints();
+        } else if (facetName.equals(FacetTypes.FLOW_VELOCITY_MAINCHANNEL) ||
+                facetName.equals(FacetTypes.FLOW_VELOCITY_MAINCHANNEL_FILTERED)) {
+            FlowVelocityData fData = (FlowVelocityData) data;
+            points = fData.getMainChannelPoints(); // I hate facets!
+        } else if (facetName.equals(FacetTypes.FLOW_VELOCITY_MEASUREMENT)) {
+            FastFlowVelocityMeasurementValue fData =
+                (FastFlowVelocityMeasurementValue) data;
+            points = new double[][] {{fData.getStation()},{fData.getV()}};
+        } else {
+            logger.error("Unknown facet name: " + facetName);
+            return;
+        }
+        StyledSeriesBuilder.addPoints(series, points, true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.FLOW_VELOCITY_MAINCHANNEL_FILTERED) ||
+            facettype.equals(FacetTypes.FLOW_VELOCITY_MAINCHANNEL) ||
+            facettype.equals(FacetTypes.FLOW_VELOCITY_TOTALCHANNEL_FILTERED) ||
+            facettype.equals(FacetTypes.FLOW_VELOCITY_TOTALCHANNEL) ||
+            facettype.equals(FacetTypes.FLOW_VELOCITY_MEASUREMENT);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/KMIndexProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,7 +11,6 @@
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
 import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
 
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifacts.CallContext;
@@ -20,24 +19,25 @@
 import org.dive4elements.river.artifacts.model.fixings.QWD;
 import org.dive4elements.river.exports.XYChartGenerator;
 import org.dive4elements.river.jfree.StyledXYSeries;
+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,
-            Document theme, boolean visible, int index) {
-        String facettype = aandf.getFacetName();
+    public void doOut(XYChartGenerator generator, ArtifactAndFacet bundle,
+            ThemeDocument theme, boolean visible, int index) {
+        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,
-            Document doc, boolean visible, int idx) {
-        logger.debug("doSectorAverageOut" + aaf.getFacet().getIndex());
+    private void doSectorAverageOut(XYChartGenerator generator, ArtifactAndFacet bundle,
+            ThemeDocument doc, boolean visible, int idx) {
+        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, Document 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, Document 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	Fri Sep 27 17:36:50 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);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/MiddleBedHeightProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,79 @@
+/* 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.jfree.data.xy.XYSeries;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+
+import org.dive4elements.river.artifacts.model.MiddleBedHeightData;
+import org.dive4elements.river.artifacts.D4EArtifact;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.themes.ThemeDocument;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.utils.RiverUtils;
+import org.dive4elements.river.jfree.StyledXYSeries;
+
+public class MiddleBedHeightProcessor extends DefaultProcessor {
+
+    /** Private logger. */
+    private static final Logger logger =
+            Logger.getLogger(WOutProcessor.class);
+
+    public static final String I18N_AXIS_LABEL =
+        "chart.bedheight_middle.section.yaxis.label";
+
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "mittlere Sohlhöhen [müNN]";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        logger.debug("Processing: " + bundle.getFacetName());
+        MiddleBedHeightData data =
+            (MiddleBedHeightData) bundle.getData(context);
+
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+
+        StyledSeriesBuilder.addPoints(series, data.getMiddleHeightsPoints(),
+                false, 0.110d);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @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 });
+    }
+
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+        return facetType.equals(FacetTypes.MIDDLE_BED_HEIGHT_EPOCH) ||
+            facetType.equals(FacetTypes.MIDDLE_BED_HEIGHT_SINGLE);
+    }
+}
--- a/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/Processor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,10 +8,10 @@
 
 package org.dive4elements.river.exports.process;
 
-import org.w3c.dom.Document;
-
 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
@@ -20,22 +20,58 @@
  * @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,
-            Document             theme,
-            boolean               visible,
-            int                         index);
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible,
+            int              index);
 
     /**
      * Returns true if the Processor class is able to generate output for a facet type
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/QOutProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,110 @@
+/* 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.process;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.artifacts.model.WQKms;
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+
+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;
+
+/**
+ * Add data to chart/generator.
+ *
+ * @author <a href="mailto:bjoern.ricks@intevation.de">Björn Ricks</a>
+ */
+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 =
+            Logger.getLogger(QOutProcessor.class);
+
+    @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);
+        String facetName = bundle.getFacetName();
+
+        if (facetName.equals(FacetTypes.FLOW_VELOCITY_DISCHARGE)) {
+            FlowVelocityData fData = (FlowVelocityData) data;
+            StyledSeriesBuilder.addPoints(series, fData.getQPoints(), true);
+        } else {
+            WQKms wqkms = (WQKms) data;
+            StyledSeriesBuilder.addStepPointsKmQ(series, wqkms);
+        }
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public void doOut(
+            XYChartGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible,
+            int              index)
+    {
+        CallContext context = generator.getCallContext();
+        WQKms wqkms = (WQKms) bundle.getData(context);
+
+        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);
+    }
+
+    /**
+     * Returns true if facettype is q-type.
+     */
+    @Override
+    public boolean canHandle(String facetType) {
+        if (facetType == null) {
+            return false;
+        }
+
+        if (facetType.equals(FacetTypes.STATIC_WQKMS_Q)
+            || facetType.equals(FacetTypes.LONGITUDINAL_Q)
+            || facetType.startsWith(FacetTypes.DISCHARGE_LONGITUDINAL_Q)
+            || facetType.startsWith(FacetTypes.FLOW_VELOCITY_DISCHARGE)) {
+            return true;
+        }
+        return false;
+    }
+}
+// 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/SedimentLoadProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,95 @@
+/* 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 java.util.Set;
+
+import org.apache.log4j.Logger;
+import org.jfree.data.xy.XYSeries;
+import org.dive4elements.river.artifacts.D4EArtifact;
+
+import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
+import org.dive4elements.artifacts.CallContext;
+import org.dive4elements.river.artifacts.access.SedimentLoadAccess;
+import org.dive4elements.river.artifacts.model.FacetTypes;
+import org.dive4elements.river.exports.DiagramGenerator;
+import org.dive4elements.river.exports.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoad;
+import org.dive4elements.river.artifacts.model.minfo.SedimentLoadFraction;
+
+public class SedimentLoadProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(SedimentLoadProcessor.class);
+
+    public static final String I18N_YAXIS_LABEL_1 =
+        "chart.sedimentload.ls.yaxis.label.tpera";
+    public static final String I18N_YAXIS_LABEL_2 =
+        "chart.sedimentload.ls.yaxis.label.m3pera";
+    public static final String I18N_YAXIS_LABEL_DEFAULT_1 = "[t/a]";
+    public static final String I18N_YAXIS_LABEL_DEFAULT_2 = "[m\u00b3/a]";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        logger.debug("doOut " + bundle.getFacetName());
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        Object data = bundle.getData(context);
+        String facetName = bundle.getFacetName();
+        double [][] points;
+
+        if (FacetTypes.IS.SEDIMENT_LOAD(facetName)) {
+            points = (double[][]) data;
+        } else if (FacetTypes.IS.SEDIMENT_LOAD_UNKNOWN(facetName)) {
+            SedimentLoad load = (SedimentLoad) data;
+            Set<Double> kms = load.getKms();
+            points = new double[2][kms.size()];
+            int counter = 0;
+            for (Double km: kms) {
+                SedimentLoadFraction fraction = load.getFraction(km);
+                points[0][counter] = km;
+                points[1][counter] = fraction.getUnknown();
+                counter++;
+            }
+        } else {
+            logger.error("Unknown facet name: " + facetName);
+            return;
+        }
+        StyledSeriesBuilder.addPoints(series, points, true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return FacetTypes.IS.SEDIMENT_LOAD(facettype) ||
+            FacetTypes.IS.SEDIMENT_LOAD_UNKNOWN(facettype);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        SedimentLoadAccess slaccess =
+            new SedimentLoadAccess((D4EArtifact) generator.getMaster());
+        String unit = slaccess.getUnit();
+        if (unit != null && unit.equals("m3_per_a")) {
+            return generator.msg(I18N_YAXIS_LABEL_2, I18N_YAXIS_LABEL_DEFAULT_2);
+        }
+        else {
+            return generator.msg(I18N_YAXIS_LABEL_1, I18N_YAXIS_LABEL_DEFAULT_1);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/ShearStressProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,64 @@
+/* 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.jfree.data.xy.XYSeries;
+
+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.StyledSeriesBuilder;
+import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
+
+import org.dive4elements.river.artifacts.model.FlowVelocityData;
+
+public class ShearStressProcessor extends DefaultProcessor {
+
+    private final static Logger logger =
+            Logger.getLogger(ShearStressProcessor.class);
+
+    public static final String I18N_AXIS_LABEL =
+        "chart.flow_velocity.section.yaxis.third.label";
+    public static final String I18N_AXIS_LABEL_DEFAULT =
+        "Schubspannung Tau [N]";
+
+    @Override
+    public void doOut(
+            DiagramGenerator generator,
+            ArtifactAndFacet bundle,
+            ThemeDocument    theme,
+            boolean          visible) {
+        CallContext context = generator.getCallContext();
+        XYSeries series = new StyledXYSeries(bundle.getFacetDescription(),
+                theme);
+        String facetName = bundle.getFacetName();
+        FlowVelocityData data = (FlowVelocityData) bundle.getData(context);
+
+        StyledSeriesBuilder.addPoints(series, data.getTauPoints(), true);
+
+        generator.addAxisSeries(series, axisName, visible);
+    }
+
+    @Override
+    public boolean canHandle(String facettype) {
+        return facettype.equals(FacetTypes.FLOW_VELOCITY_TAU) ||
+            facettype.equals(FacetTypes.FLOW_VELOCITY_TAU_FILTERED);
+    }
+
+    @Override
+    public String getAxisLabel(DiagramGenerator generator) {
+        return generator.msg(
+                I18N_AXIS_LABEL,
+                I18N_AXIS_LABEL_DEFAULT);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WDiffProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,36 @@
+/* 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.river.artifacts.model.FacetTypes;
+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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/process/WOutProcessor.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,55 +10,116 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.data.xy.XYSeries;
-import org.w3c.dom.Document;
 
 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.utils.DataUtil;
-import org.dive4elements.river.utils.ThemeUtil;
+import org.dive4elements.river.themes.ThemeDocument;
+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,
-            Document         theme,
+            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);
 
         // If a "band around the curve shall be drawn, add according area.
-        double bandWidth = ThemeUtil.parseBandWidth(theme);
+        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);
 
@@ -69,18 +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());
         }
     }
 
-    /**
-     * Returns true if facettype is longitutinal_section.w .
-     */
+    @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 });
+    }
+
     @Override
     public boolean canHandle(String facetType) {
         if (facetType == null) {
@@ -92,41 +162,11 @@
                 || 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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQOverviewGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -38,6 +38,7 @@
 import org.dive4elements.river.exports.ChartGenerator;
 import org.dive4elements.river.exports.OutGenerator;
 import org.dive4elements.river.exports.OutputHelper;
+import org.dive4elements.river.themes.ThemeDocument;
 
 public class SQOverviewGenerator
 implements OutGenerator
@@ -61,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.
@@ -69,11 +77,13 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         logger.debug("doOut()");
 
+        // TODO: Why not using outName for this.
+
         String name = artifactAndFacet.getData(context).toString();
         if(name != null) {
             logger.debug("name: " + name);
@@ -93,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();
@@ -110,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;
@@ -152,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	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationExporter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,8 +16,7 @@
 import java.util.Date;
 import java.util.Locale;
 import java.text.DateFormat;
-
-import org.w3c.dom.Document;
+import java.text.NumberFormat;
 
 import net.sf.jasperreports.engine.JasperExportManager;
 import net.sf.jasperreports.engine.JasperFillManager;
@@ -26,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;
@@ -99,8 +97,11 @@
     public static final String CSV_C_FERGUSON =
         "export.sqrelation.csv.header.c.ferguson";
 
-    public static final String CSV_VARIANCE =
-        "export.sqrelation.csv.header.variance";
+    public static final String CSV_QMAX =
+        "export.sqrelation.csv.header.qmax";
+
+    public static final String CSV_SD =
+        "export.sqrelation.csv.header.sd";
 
     public static final String PDF_TITLE=
         "export.sqrelation.pdf.title";
@@ -113,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) {
@@ -136,9 +134,13 @@
             msg(CSV_PARAMETER,  CSV_PARAMETER),
             msg(CSV_COEFF_A   , CSV_COEFF_A),
             msg(CSV_COEFF_B   , CSV_COEFF_B),
+            msg(CSV_SD, CSV_SD),
+            msg(CSV_QMAX, CSV_QMAX),
+            msg(CSV_COEFF_R, CSV_COEFF_R),
             msg(CSV_N_TOTAL   , CSV_N_TOTAL),
             msg(CSV_N_OUTLIERS, CSV_N_OUTLIERS),
-            msg(CSV_VARIANCE  , CSV_VARIANCE)
+            msg(CSV_C_DUAN, CSV_C_DUAN),
+            msg(CSV_C_FERGUSON, CSV_C_FERGUSON)
         });
     }
 
@@ -160,6 +162,12 @@
                     ).format(result.getKm());
         List<String[]> retval = new ArrayList<String[]>();
 
+        NumberFormat sqAFormatter = Formatter.getSQRelationA(context);
+        NumberFormat sqBFormatter = Formatter.getSQRelationB(context);
+        NumberFormat fThreeFormatter = Formatter.getFormatter(context, 3, 3);
+        NumberFormat fTwoFormatter = Formatter.getFormatter(context, 2, 2);
+        NumberFormat fZeroFormatter = Formatter.getFormatter(context, 0, 0);
+
         for (int i = 0; i < SQResult.NUMBER_FRACTIONS; ++i) {
             SQFractionResult fraction = result.getFraction(i);
 
@@ -171,10 +179,17 @@
                 continue;
             }
 
-            String a, b, sd, o, t;
-            a  = Formatter.getSQRelationA(context).format(parameters.getValue(0, "a"));
-            b  = Formatter.getSQRelationB(context).format(parameters.getValue(0, "b"));
-            sd = Formatter.getVariance(context).format(Math.sqrt(parameters.getValue(0, "std_dev")));
+            String a, b, sd, o, t, max_q, c_ferguson, c_duan, r2;
+            a  = sqAFormatter.format(parameters.getValue(0, "a"));
+            b  = sqBFormatter.format(parameters.getValue(0, "b"));
+
+            /* The std_dev parameter contains the standard error actually */
+            sd = fThreeFormatter.format(parameters.getValue(0, "std_dev"));
+            max_q = fZeroFormatter.format(parameters.getValue(0, "max_q"));
+            c_ferguson = fTwoFormatter.format(parameters.getValue(0, "c_ferguson"));
+            c_duan = fTwoFormatter.format(parameters.getValue(0, "c_duan"));
+            r2 = fTwoFormatter.format(parameters.getValue(0, "r2"));
+
 
             o  = String.valueOf(fraction.totalNumOutliers());
             t  = String.valueOf(fraction.numMeasurements() + o);
@@ -184,9 +199,13 @@
                 name,
                 a,
                 b,
-                t,
-                o,
-                sd
+                sd, // 4
+                max_q, // 5
+                r2, // 6
+                t, // 7
+                o, // 8
+                c_duan, // 9
+                c_ferguson // 10
             });
         }
         return retval;
@@ -229,12 +248,26 @@
         source.addMetaData("outliertest", Resources.getMsg(meta,
                     access.getOutlierMethod(),
                     access.getOutlierMethod()));
-        source.addMetaData("outliers", access.getOutliers().toString());
+        source.addMetaData("outliers", Formatter.getRawFormatter(context).format(
+                    access.getOutliers()));
 
         source.addMetaData("calculation", Resources.getMsg(
                                             locale,
                                             PDF_HEADER_MODE,
                                             "SQRelation"));
+
+        String measurementStationName = access.getMeasurementStationName();
+
+        if (measurementStationName != null) {
+            source.addMetaData("msName", measurementStationName);
+        }
+
+        String measurementStationGaugeName = access.getMeasurementStationGaugeName();
+
+        if (measurementStationGaugeName != null) {
+            source.addMetaData("msGauge", measurementStationGaugeName);
+        }
+
     }
 
     @Override
--- a/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/exports/sq/SQRelationGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,6 +11,10 @@
 import org.dive4elements.artifactdatabase.state.ArtifactAndFacet;
 import org.dive4elements.artifactdatabase.state.Facet;
 
+import org.dive4elements.river.artifacts.D4EArtifact;
+
+import org.dive4elements.river.artifacts.access.SQRelationAccess;
+
 import org.dive4elements.river.artifacts.model.FacetTypes;
 
 import org.dive4elements.river.artifacts.model.sq.SQ;
@@ -20,6 +24,7 @@
 
 import org.dive4elements.river.jfree.JFreeUtil;
 import org.dive4elements.river.jfree.StyledXYSeries;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import org.apache.log4j.Logger;
 
@@ -28,7 +33,6 @@
 
 import org.jfree.data.xy.XYSeries;
 
-import org.w3c.dom.Document;
 
 /**
  * An OutGenerator that generates charts for MINFO sq relation.
@@ -54,6 +58,11 @@
     public static final String I18N_YAXIS_LABEL =
         "chart.sq_relation.yaxis.label";
 
+    public static final String I18N_SUBTITLE =
+        "chart.computed.discharge.curve.subtitle";
+
+    /** Needed to access data to create subtitle. */
+    protected D4EArtifact artifact;
 
     /** The logger that is used in this generator. */
     private static Logger logger = Logger.getLogger(SQRelationGenerator.class);
@@ -75,6 +84,23 @@
         };
     }
 
+    /**
+     * Returns the default subtitle for this chart.
+     *
+     * @return the default subtitle for this chart.
+     */
+    @Override
+    protected String getDefaultChartSubtitle() {
+        SQRelationAccess sqAccess = new SQRelationAccess(artifact);
+        Object[] args = null;
+        args = new Object[] {
+            sqAccess.getRiver(),
+            sqAccess.getLocation()
+        };
+        return msg(I18N_SUBTITLE, "", args);
+    }
+
+
 
     @Override
     public String getDefaultChartTitle() {
@@ -109,11 +135,13 @@
     @Override
     public void doOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         logger.debug("doOut");
 
+        this.artifact = (D4EArtifact) artifactAndFacet.getArtifact();
+
         Facet  facet = artifactAndFacet.getFacet();
         String name  = facet != null ? facet.getName() : null;
 
@@ -144,7 +172,7 @@
 
     protected void doSQCurveOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String desc = artifactAndFacet.getFacetDescription();
@@ -180,7 +208,7 @@
 
     protected void doSQOut(
         ArtifactAndFacet artifactAndFacet,
-        Document         attr,
+        ThemeDocument    attr,
         boolean          visible
     ) {
         String desc = artifactAndFacet.getFacetDescription();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/AnnotationHelper.java	Fri Sep 27 17:36:50 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	Fri Sep 27 17:36:50 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/jfree/JFreeUtil.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/JFreeUtil.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,9 +16,9 @@
 import org.apache.log4j.Logger;
 import org.jfree.chart.entity.ChartEntity;
 import org.jfree.chart.entity.EntityCollection;
-import org.w3c.dom.Document;
 
 import org.dive4elements.river.artifacts.math.Function;
+import org.dive4elements.river.themes.ThemeDocument;
 
 public class JFreeUtil {
 
@@ -107,7 +107,7 @@
 
     public static StyledXYSeries sampleFunction2D(
         Function func,
-        Document theme,
+        ThemeDocument theme,
         String   seriesKey,
         int      samples,
         double   start,
@@ -127,7 +127,7 @@
 
     public static StyledXYSeries sampleFunction2DPositive(
         Function func,
-        Document theme,
+        ThemeDocument theme,
         String   seriesKey,
         int      samples,
         double   start,
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/RiverAnnotation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/RiverAnnotation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,12 +9,12 @@
 package org.dive4elements.river.jfree;
 
 import org.dive4elements.river.artifacts.model.HYKFactory;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.util.Collections;
 import java.util.List;
 
 import org.jfree.chart.annotations.XYTextAnnotation;
-import org.w3c.dom.Document;
 
 /**
  * List of Text- Annotations (Sticky to one axis or in space)
@@ -32,7 +32,7 @@
     protected List<HYKFactory.Zone> boxes;
 
     /** Styling information. */
-    protected Document theme;
+    protected ThemeDocument theme;
 
     /** Chart-legend information. */
     protected String label;
@@ -53,7 +53,7 @@
 
     /** Create annotations, parameter might be null. */
     public RiverAnnotation(String label, List<StickyAxisAnnotation> annotations,
-        List<HYKFactory.Zone> bAnnotations, Document theme
+        List<HYKFactory.Zone> bAnnotations, ThemeDocument theme
     ) {
         this.label = label;
         this.axisTextAnnotations = (annotations != null)
@@ -92,11 +92,11 @@
         return boxes;
     }
 
-    public void setTheme(Document theme) {
+    public void setTheme(ThemeDocument theme) {
         this.theme = theme;
     }
 
-    public Document getTheme() {
+    public ThemeDocument getTheme() {
         return theme;
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StickyAxisAnnotation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StickyAxisAnnotation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -70,7 +70,7 @@
      * Constructor with given explicit axis and axisSymbol
      * @param text       the text to display.
      * @param pos        the position at which to draw the text and mark.
-     * @param stickAxis the axis at which to stick (and to which 'pos' is
+     * @param stickAxis  the axis at which to stick (and to which 'pos' is
      *                   relative).
      */
     public StickyAxisAnnotation(String text, float pos, SimpleAxis stickAxis,
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StyledAreaSeriesCollection.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StyledAreaSeriesCollection.java	Fri Sep 27 17:36:50 2013 +0200
@@ -13,11 +13,8 @@
 import java.awt.Stroke;
 
 import org.jfree.data.xy.XYSeriesCollection;
-import org.w3c.dom.Document;
 
-import org.dive4elements.river.themes.ThemeAccess;
-import org.dive4elements.river.utils.ThemeUtil;
-
+import org.dive4elements.river.themes.ThemeDocument;
 
 /**
  * One or more dataseries to draw a polygon (either "open up/downwards", or
@@ -36,13 +33,13 @@
     protected FILL_MODE mode;
 
     /** The theme-document with attributes about actual visual representation. */
-    protected Document theme;
+    protected ThemeDocument theme;
 
 
     /**
      * @param theme the theme-document.
      */
-    public StyledAreaSeriesCollection(Document theme) {
+    public StyledAreaSeriesCollection(ThemeDocument theme) {
         this.theme = theme;
         this.mode = FILL_MODE.BETWEEN;
    }
@@ -73,7 +70,7 @@
         applyShowShape(renderer);
         applyOutlineColor(renderer);
         applyOutlineStyle(renderer);
-        applyShowArea(renderer);
+        applyShowAreaLabel(renderer);
         if (mode == FILL_MODE.UNDER) {
             renderer.setAreaCalculationMode(StableXYDifferenceRenderer.CALCULATE_NEGATIVE_AREA);
         }
@@ -85,16 +82,15 @@
         }
 
         // Apply text style.
-        new ThemeAccess(theme).parseTextStyle().apply(renderer);
+        theme.parseComplexTextStyle().apply(renderer);
         return renderer;
     }
 
 
     protected void applyFillColor(StableXYDifferenceRenderer renderer) {
-        Color paint = ThemeUtil.parseColor(
-                ThemeUtil.getAreaBackgroundColorString(theme));
+        Color paint = theme.parseAreaBackgroundColor();
 
-        int transparency = ThemeUtil.parseAreaTransparency(theme);
+        int transparency = theme.parseAreaTransparency();
         if (transparency > 0 && paint != null) {
             paint = new Color(
                         paint.getRed(),
@@ -121,34 +117,35 @@
 
 
     protected void applyShowShape(StableXYDifferenceRenderer renderer) {
-        boolean show = ThemeUtil.parseAreaShowBorder(theme);
+        boolean show = theme.parseAreaShowBorder();
         renderer.setDrawOutline(show);
     }
 
 
     protected void applyShowLine(StableXYDifferenceRenderer renderer) {
-        boolean show = ThemeUtil.parseShowLine(theme);
+        boolean show = theme.parseShowLine();
         renderer.setShapesVisible(show);
     }
 
 
     protected void applyOutlineColor(StableXYDifferenceRenderer renderer) {
-        Color c = ThemeUtil.parseLineColorField(theme);
+        Color c = theme.parseLineColorField();
         renderer.setOutlinePaint(c);
     }
 
     protected void applyOutlineWidth(StableXYDifferenceRenderer renderer) {
-        int size = ThemeUtil.parseLineWidth(theme);
+        // int size = theme.parseLineWidth();
+        // XXX: Why is this not set?
     }
 
     /** Inform renderer whether it should draw a label. */
-    protected void applyShowArea(StableXYDifferenceRenderer renderer) {
-        renderer.setLabelArea(ThemeUtil.parseShowArea(theme));
+    protected void applyShowAreaLabel(StableXYDifferenceRenderer renderer) {
+        renderer.setLabelArea(theme.parseShowAreaLabel());
     }
 
     protected void applyOutlineStyle(StableXYDifferenceRenderer renderer) {
-        float[] dashes = ThemeUtil.parseLineStyle(theme);
-        int size       = ThemeUtil.parseLineWidth(theme);
+        float[] dashes = theme.parseLineStyle();
+        int size       = theme.parseLineWidth();
 
         Stroke stroke = null;
 
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StyledDomainMarker.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StyledDomainMarker.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,9 +11,8 @@
 import java.awt.Color;
 
 import org.jfree.chart.plot.IntervalMarker;
-import org.w3c.dom.Document;
 
-import org.dive4elements.river.utils.ThemeUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 
 /**
  * Marker that represents a highlighted interval.
@@ -26,19 +25,17 @@
 
     private final Color backgroundColor, backgroundColor2;
 
-    public StyledDomainMarker(double start, double end, Document theme) {
+    public StyledDomainMarker(double start, double end, ThemeDocument theme) {
         super(start, end);
 
-        backgroundColor = ThemeUtil.parseColor(
-                ThemeUtil.getAreaBackgroundColorString(theme));
+        backgroundColor = theme.parseAreaBackgroundColor();
         backgroundColor2 = new Color(
             255 - backgroundColor.getRed(),
             255 - backgroundColor.getGreen(),
             255 - backgroundColor.getBlue());
         useSecondColor(false);
 
-        int alpha = 100 - ThemeUtil.parseInteger(
-                ThemeUtil.getAreaTransparencyString(theme), 50);
+        int alpha = 100 - theme.parseAreaTransparency(50);
         setAlpha(alpha / 100.0f);
     }
 
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StyledTimeSeries.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StyledTimeSeries.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,8 +9,7 @@
 package org.dive4elements.river.jfree;
 
 import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import org.jfree.data.time.TimeSeries;
 
@@ -27,7 +26,7 @@
     protected Style style;
 
 
-    public StyledTimeSeries(String key, Document theme) {
+    public StyledTimeSeries(String key, ThemeDocument theme) {
         super(key);
         setStyle(new XYStyle(theme));
     }
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StyledValueMarker.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StyledValueMarker.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,13 +8,12 @@
 
 package org.dive4elements.river.jfree;
 
-import org.dive4elements.river.utils.ThemeUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
 
 import org.jfree.chart.plot.ValueMarker;
-import org.w3c.dom.Document;
 
 /**
  * Marker that represents a single value.
@@ -24,16 +23,16 @@
 
     private static final long serialVersionUID = -3607777705307785140L;
 
-    public StyledValueMarker(double value, Document theme) {
+    public StyledValueMarker(double value, ThemeDocument theme) {
         super(value);
 
-        Color color = ThemeUtil.parseAreaBackgroundColor(theme);
+        Color color = theme.parseAreaBackgroundColor();
         if(color == null) {
             color = Color.BLACK;
         }
         this.setPaint(color);
 
-        int size = ThemeUtil.parsePointWidth(theme);
+        int size = theme.parsePointWidth();
         setStroke(new BasicStroke(size));
     }
 }
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/StyledXYSeries.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/StyledXYSeries.java	Fri Sep 27 17:36:50 2013 +0200
@@ -11,8 +11,7 @@
 import java.util.List;
 
 import org.apache.log4j.Logger;
-
-import org.w3c.dom.Document;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import org.jfree.data.xy.XYDataItem;
 import org.jfree.data.xy.XYSeries;
@@ -30,13 +29,13 @@
     protected String label;
 
 
-    public StyledXYSeries(String key, Document theme) {
+    public StyledXYSeries(String key, ThemeDocument theme) {
         this(key, true, theme);
         this.label = key.toString();
     }
 
 
-    public StyledXYSeries(String key, Document theme, XYSeries unstyledSeries) {
+    public StyledXYSeries(String key, ThemeDocument theme, XYSeries unstyledSeries) {
         this(key, theme);
         add(unstyledSeries);
     }
@@ -47,12 +46,24 @@
      *               to one extrema which can cause problems in certain
      *               algorithms.
      */
-    public StyledXYSeries(String key, boolean sorted, Document theme) {
+    public StyledXYSeries(String key, boolean sorted, ThemeDocument theme) {
         super(key, sorted);
         setStyle(new XYStyle(theme));
         this.label = key.toString();
     }
 
+    public StyledXYSeries(
+        String  key,
+        boolean sorted,
+        boolean allowDuplicateXValues,
+        ThemeDocument theme
+    ) {
+        super(key, sorted, allowDuplicateXValues);
+        setStyle(new XYStyle(theme));
+        this.label = key.toString();
+    }
+
+
 
     @Override
     public void setStyle(Style style) {
--- a/artifacts/src/main/java/org/dive4elements/river/jfree/XYStyle.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/jfree/XYStyle.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,7 +8,7 @@
 
 package org.dive4elements.river.jfree;
 
-import org.dive4elements.river.utils.ThemeUtil;
+import org.dive4elements.river.themes.ThemeDocument;
 
 import java.awt.BasicStroke;
 import java.awt.Color;
@@ -16,7 +16,6 @@
 
 import org.apache.log4j.Logger;
 import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
-import org.w3c.dom.Document;
 
 
 /**
@@ -27,14 +26,13 @@
 
     private static Logger logger = Logger.getLogger(XYStyle.class);
 
-    protected Document theme;
+    protected ThemeDocument theme;
 
     protected XYLineAndShapeRenderer renderer;
 
 
-    public XYStyle(Document theme) {
+    public XYStyle(ThemeDocument theme) {
         this.theme = theme;
-        this.renderer = null;
     }
 
 
@@ -43,7 +41,11 @@
      * whether to draw lines and/or points.
      */
     @Override
-    public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx){
+    public XYLineAndShapeRenderer applyTheme(XYLineAndShapeRenderer r, int idx) {
+        if (theme == null) {
+            // Hurray we already applied nothing :)
+            return r;
+        }
         this.renderer = r;
         applyLineColor(r, idx);
         applyLineSize(r, idx);
@@ -76,7 +78,7 @@
 
     /** Set line color to renderer. */
     protected void applyLineColor(XYLineAndShapeRenderer r, int idx) {
-        Color c = ThemeUtil.parseLineColorField(theme);
+        Color c = theme.parseLineColorField();
         if(c != null) {
             logger.debug("applyLineColor " + c.toString());
             r.setSeriesPaint(idx, c);
@@ -92,10 +94,10 @@
         if (!(r instanceof EnhancedLineAndShapeRenderer)) {
             return;
         }
-        boolean showLabelLine = ThemeUtil.parseShowLineLabel(theme);
-        boolean anyLabel = showLabelLine || ThemeUtil.parseShowWidth(theme) ||
-                           ThemeUtil.parseShowLevel(theme) ||
-                           ThemeUtil.parseShowMiddleHeight(theme);
+        boolean showLabelLine = theme.parseShowLineLabel();
+        boolean anyLabel = showLabelLine || theme.parseShowWidth() ||
+                           theme.parseShowLevel() ||
+                           theme.parseShowMiddleHeight();
         ((EnhancedLineAndShapeRenderer)r).setShowLineLabel(anyLabel, idx);
     }
 
@@ -105,7 +107,7 @@
         if (!(r instanceof EnhancedLineAndShapeRenderer)) {
             return;
         }
-        boolean showLabelLine = ThemeUtil.parseLabelShowBackground(theme);
+        boolean showLabelLine = theme.parseLabelShowBackground();
         ((EnhancedLineAndShapeRenderer)r).setShowLineLabelBG(idx, showLabelLine);
     }
 
@@ -116,7 +118,7 @@
             return;
         }
         ((EnhancedLineAndShapeRenderer)r).setLineLabelFont(
-                ThemeUtil.parseTextFont(theme), idx);
+                theme.parseTextFont(), idx);
     }
 
     /** Tell the renderer which color to use for
@@ -126,7 +128,7 @@
             return;
         }
         ((EnhancedLineAndShapeRenderer)r).setLineLabelTextColor(
-                idx, ThemeUtil.parseTextColor(theme));
+                idx, theme.parseTextColor());
     }
 
     /** Tell the renderer which color to use for bg of
@@ -136,12 +138,12 @@
             return;
         }
         ((EnhancedLineAndShapeRenderer)r).setLineLabelBGColor(idx,
-            ThemeUtil.parseTextBackground(theme));
+            theme.parseTextBackground());
     }
 
     /** Set stroke of series. */
     protected void applyLineSize(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parseLineWidth(theme);
+        int size = theme.parseLineWidth();
         r.setSeriesStroke(
             idx,
             new BasicStroke(size));
@@ -150,8 +152,8 @@
 
     /** Set stroke strength of series. */
     protected void applyLineType(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parseLineWidth(theme);
-        float[] dashes = ThemeUtil.parseLineStyle(theme);
+        int size = theme.parseLineWidth();
+        float[] dashes = theme.parseLineStyle();
 
         // Do not apply the dashed style.
         if (dashes.length <= 1) {
@@ -170,7 +172,7 @@
 
 
     protected void applyPointSize(XYLineAndShapeRenderer r, int idx) {
-        int size = ThemeUtil.parsePointWidth(theme);
+        int size = theme.parsePointWidth();
         int dim  = 2 * size;
 
         r.setSeriesShape(idx, new Ellipse2D.Double(-size, -size, dim, dim));
@@ -178,7 +180,7 @@
 
 
     protected void applyPointColor(XYLineAndShapeRenderer r, int idx) {
-        Color c = ThemeUtil.parsePointColor(theme);
+        Color c = theme.parsePointColor();
 
         if (c != null) {
             r.setSeriesFillPaint(idx, c);
@@ -192,7 +194,7 @@
      * Sets form and visibility of points.
      */
     protected void applyShowPoints(XYLineAndShapeRenderer r, int idx) {
-        boolean show = ThemeUtil.parseShowPoints(theme);
+        boolean show = theme.parseShowPoints();
 
         r.setSeriesShapesVisible(idx, show);
         r.setDrawOutlines(true);
@@ -200,7 +202,7 @@
 
 
     protected void applyShowLine(XYLineAndShapeRenderer r, int idx) {
-        boolean show = ThemeUtil.parseShowLine(theme);
+        boolean show = theme.parseShowLine();
         r.setSeriesLinesVisible(idx, show);
     }
 
@@ -210,7 +212,7 @@
             return;
         }
 
-        boolean visible = ThemeUtil.parseShowMinimum(theme);
+        boolean visible = theme.parseShowMinimum();
 
         EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
         er.setIsMinimumShapeVisisble(idx, visible);
@@ -222,7 +224,7 @@
             return;
         }
 
-        boolean visible = ThemeUtil.parseShowMaximum(theme);
+        boolean visible = theme.parseShowMaximum();
 
         EnhancedLineAndShapeRenderer er = (EnhancedLineAndShapeRenderer) r;
         er.setIsMaximumShapeVisible(idx, visible);
--- a/artifacts/src/main/java/org/dive4elements/river/themes/TextStyle.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/themes/TextStyle.java	Fri Sep 27 17:36:50 2013 +0200
@@ -37,12 +37,16 @@
     }
 
     public void apply(XYTextAnnotation ta) {
-        ta.setPaint(textColor);
-        ta.setFont(font);
-        if (this.showBg) {
+        if (textColor != null) {
+            ta.setPaint(textColor);
+        }
+        if (font != null) {
+            ta.setFont(font);
+        }
+        if (showBg) {
             ta.setBackgroundPaint(bgColor);
         }
-        if (this.isVertical) {
+        if (isVertical) {
             ta.setRotationAngle(270f*Math.PI/180f);
         }
         else {
@@ -57,4 +61,4 @@
             renderer.setLabelBGColor(bgColor);
         }
     }
-}
\ No newline at end of file
+}
--- a/artifacts/src/main/java/org/dive4elements/river/themes/ThemeAccess.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,132 +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.themes;
-
-import org.dive4elements.river.utils.ThemeUtil;
-
-import java.awt.Color;
-import java.awt.Font;
-
-import org.w3c.dom.Document;
-
-
-public class ThemeAccess
-{
-    protected Document theme;
-
-    protected Integer lineWidth;
-
-    protected Color   lineColor;
-    protected Color   textColor;
-    protected Font    font;
-    protected String  textOrientation;
-    protected Color   textBackground;
-    protected Boolean showTextBackground;
-    protected Color   pointColor;
-
-
-    public ThemeAccess(Document theme) {
-        this.theme = theme;
-    }
-
-
-    public int parseLineWidth() {
-        if (lineWidth == null) {
-            lineWidth = ThemeUtil.parseLineWidth(theme);
-        }
-        return lineWidth;
-    }
-
-
-    public Color parseLineColorField() {
-        if (lineColor == null) {
-            lineColor = ThemeUtil.parseLineColorField(theme);
-            if (lineColor == null) {
-                lineColor = Color.BLACK;
-            }
-        }
-        return lineColor;
-    }
-
-
-    public Color parseTextColor() {
-        if (textColor == null) {
-            textColor = ThemeUtil.parseTextColor(theme);
-            if (textColor == null) {
-                textColor = Color.BLACK;
-            }
-        }
-        return textColor;
-    }
-
-
-    public Font parseTextFont() {
-        if (font == null) {
-            font = ThemeUtil.parseTextFont(theme);
-            if (font == null) {
-                font = new Font("Arial", Font.BOLD, 10);
-            }
-        }
-        return font;
-    }
-
-
-    public String parseTextOrientation() {
-        if (textOrientation == null) {
-            textOrientation = ThemeUtil.parseTextOrientation(theme);
-        }
-        return textOrientation;
-    }
-
-
-    public Color parseTextBackground() {
-        if (textBackground == null) {
-            textBackground = ThemeUtil.parseTextBackground(theme);
-            if (textBackground == null) {
-                textBackground = Color.WHITE;
-            }
-        }
-        return textBackground;
-    }
-
-    public boolean parseLabelShowBackground() {
-        if (showTextBackground == null) {
-            showTextBackground = ThemeUtil.parseLabelShowBackground(theme);
-        }
-        return showTextBackground;
-    }
-
-
-    public Color parsePointColor() {
-        if (pointColor == null) {
-            pointColor = ThemeUtil.parsePointColor(theme);
-
-            if (pointColor == null) {
-                return parseLineColorField();
-            }
-        }
-
-        return pointColor;
-    }
-
-
-    public LineStyle parseLineStyle() {
-        return new LineStyle(parseLineColorField(), Integer.valueOf(parseLineWidth()));
-    }
-
-    public TextStyle parseTextStyle() {
-        return new TextStyle(
-            parseTextColor(),
-            parseTextFont(),
-            parseTextBackground(),
-            parseLabelShowBackground(),
-            !parseTextOrientation().equals("horizontal"));
-    }
-}
-// 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/themes/ThemeDocument.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,745 @@
+/* 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.themes;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.dive4elements.artifacts.CallMeta;
+import org.dive4elements.river.artifacts.model.MapserverStyle;
+import org.dive4elements.river.artifacts.model.MapserverStyle.Clazz;
+import org.dive4elements.river.artifacts.model.MapserverStyle.Expression;
+import org.dive4elements.river.artifacts.model.MapserverStyle.Label;
+import org.dive4elements.river.artifacts.model.MapserverStyle.Style;
+import org.dive4elements.river.artifacts.resources.Resources;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NodeList;
+
+public class ThemeDocument
+{
+    private static Logger logger = Logger.getLogger(ThemeDocument.class);
+
+    private static final String MSG_ISOBATH_CLASS = "floodmap.isobath.class";
+
+    private static final String MSG_ISOBATH_LASTCLASS = "floodmap.isobath.lastclass";
+
+    public final static String FILL_COLOR = "fillcolor";
+
+    public final static String LINE_COLOR = "linecolor";
+
+    public final static String AREA_LINE_COLOR = "areabordercolor";
+
+    public static final String LINE_SIZE = "linesize";
+
+    public static final String LINE_STYLE = "linetype";
+
+    public static final String POINT_SIZE = "pointsize";
+
+    public static final String POINT_COLOR = "pointcolor";
+
+    public final static String SHOW_BORDER = "showborder";
+
+    public final static String AREA_SHOW_BORDER = "showborder";
+
+    public final static String SHOW_POINTS = "showpoints";
+
+    public final static String SHOW_LINE = "showlines";
+
+    public final static String SHOW_VERTICAL_LINE = "showverticalline";
+
+    public final static String SHOW_HORIZONTAL_LINE = "showhorizontalline";
+
+    public final static String SHOW_LINE_LABEL = "showlinelabel";
+
+    public final static String SHOW_POINT_LABEL = "showpointlabel";
+
+    public final static String SHOW_WIDTH = "showwidth";
+
+    public final static String SHOW_LEVEL = "showlevel";
+
+    public final static String TRANSPARENCY = "transparency";
+
+    public final static String AREA_TRANSPARENCY = "areatransparency";
+
+    public final static String SHOW_AREA = "showarea";
+
+    public final static String SHOW_AREA_LABEL = "showarealabel";
+
+    public final static String SHOW_MIDDLE_HEIGHT = "showmiddleheight";
+
+    public final static String LABEL_FONT_COLOR = "labelfontcolor";
+
+    public final static String LABEL_FONT_SIZE = "labelfontsize";
+
+    public final static String LABEL_FONT_FACE = "labelfontface";
+
+    public final static String LABEL_FONT_STYLE = "labelfontstyle";
+
+    public final static String TEXT_ORIENTATION = "textorientation";
+
+    public final static String LABEL_BGCOLOR = "labelbgcolor";
+
+    public final static String LABEL_SHOW_BACKGROUND = "labelshowbg";
+
+    public final static String BACKGROUND_COLOR = "backgroundcolor";
+
+    public final static String AREA_BACKGROUND_COLOR = "areabgcolor";
+
+    public final static String SYMBOL = "symbol";
+
+    public final static String SHOW_MINIMUM = "showminimum";
+
+    public final static String SHOW_MAXIMUM = "showmaximum";
+
+    public final static String WSPLGEN_STARTCOLOR = "startcolor";
+
+    public final static String WSPLGEN_ENDCOLOR = "endcolor";
+
+    public final static String WSPLGEN_NUMCLASSES = "numclasses";
+
+    public final static String BANDWIDTH = "bandwidth";
+
+    public final static String SHOWEXTRAMARK = "showextramark";
+
+
+    private Document document;
+
+    private Map<String, String> values;
+
+    public ThemeDocument() {
+    }
+
+    public ThemeDocument(Document document) {
+        this.document = document;
+        values = extractValues(document);
+    }
+
+    public Document getDocument() {
+        return document;
+    }
+
+    private String getValue(String key) {
+        return values.get(key);
+    }
+
+    private static Map<String, String> extractValues(Document document) {
+        Map<String, String> values = new HashMap<String, String>();
+        if (document == null) {
+            logger.error("Invalid null document given.");
+            return values;
+        }
+
+        NodeList fields = document.getElementsByTagName("field");
+        for (int i = 0, N = fields.getLength(); i < N; ++i) {
+            Element field = (Element)fields.item(i);
+            String name   = field.getAttribute("name");
+            String value  = field.getAttribute("default");
+            if (!name.isEmpty() && !value.isEmpty()) {
+                values.put(name, value);
+            }
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("Theme values: " + values);
+        }
+        return values;
+    }
+
+    /** Parse string to be boolean with default if empty or unrecognized. */
+    private static boolean parseBoolean(String value, boolean defaultsTo) {
+        if (value == null) {
+            return defaultsTo;
+        }
+        if (value.equals("false")) {
+            return false;
+        }
+        if (value.equals("true")) {
+            return true;
+        }
+        return defaultsTo;
+    }
+
+
+    /**
+     * Attempt converting \param value to an integer, in failing cases,
+     * return \param defaultsTo.
+     * @param value String to be converted to integer.
+     * @param defaultsTo Default to return if conversion failed.
+     * @return \param value as integer or defaultsto if conversion failed.
+     */
+    private static int parseInteger(String value, int defaultsTo) {
+        if (value == null) {
+            return defaultsTo;
+        }
+
+        try {
+            return Integer.parseInt(value);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        return defaultsTo;
+    }
+
+
+    /**
+     * Attempt converting \param value to a double, in failing cases,
+     * return \param defaultsTo.
+     * @param value String to be converted to double.
+     * @param defaultsTo Default to return if conversion failed.
+     * @return \param value as integer or defaultsto if conversion failed.
+     */
+    private static double parseDouble(String value, double defaultsTo) {
+        if (value == null) {
+            return defaultsTo;
+        }
+
+        try {
+            return Double.parseDouble(value);
+        }
+        catch (NumberFormatException nfe) {
+            // do nothing
+        }
+
+        return defaultsTo;
+    }
+
+    public boolean parseShowLineLabel() {
+        String show = getValue(SHOW_LINE_LABEL);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowWidth() {
+        String show = getValue(SHOW_WIDTH);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowLevel() {
+        String show = getValue(SHOW_LEVEL);
+        return parseBoolean(show, false);
+    }
+
+    public String parseTextOrientation() {
+        String o = getValue(TEXT_ORIENTATION);
+
+        return o != null && "true".equals(o)
+            ? "horizontal"
+            : "vertical";
+    }
+
+    public boolean parseShowMiddleHeight() {
+        String show = getValue(SHOW_MIDDLE_HEIGHT);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseLabelShowBackground() {
+        String show = getValue(LABEL_SHOW_BACKGROUND);
+        return parseBoolean(show, false);
+    }
+
+    public Font parseTextFont() {
+        String font = getValue(LABEL_FONT_FACE);
+        if (font == null) {
+            return null;
+        }
+
+        int size = parseTextSize();
+        int style = parseTextStyle();
+        Font f = new Font(font, style, size);
+        return f;
+    }
+
+    public Color parseTextColor() {
+        return parseRGB(getTextColorString());
+    }
+
+    private String getTextColorString() {
+        return getValue(LABEL_FONT_COLOR);
+    }
+
+    public Color parseTextBackground() {
+        String color = getLabelBackgroundColorString();
+        return color != null
+            ? parseRGB(color)
+            : Color.WHITE;
+    }
+
+    private String getLabelBackgroundColorString() {
+        return getValue(LABEL_BGCOLOR);
+    }
+
+    public int parseLineWidth() {
+        String size = getValue(LINE_SIZE);
+        if (size == null) {
+            return 0;
+        }
+
+        try {
+            return Integer.parseInt(size);
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Unable to set line size from string: '" + size + "'");
+        }
+        return 0;
+    }
+
+    public float [] parseLineStyle() {
+        String dash = getValue(LINE_STYLE);
+
+        float[] def = {10};
+        if (dash == null) {
+            return def;
+        }
+
+        String[] pattern = dash.split(",");
+        if(pattern.length == 1) {
+            return def;
+        }
+
+        try {
+            float[] dashes = new float[pattern.length];
+            for (int i = 0; i < pattern.length; i++) {
+                dashes[i] = Float.parseFloat(pattern[i]);
+            }
+            return dashes;
+        }
+        catch (NumberFormatException nfe) {
+            logger.warn("Unable to set dash from string: '" + dash + "'");
+            return def;
+        }
+    }
+
+    public int parsePointWidth() {
+        String width = getValue(POINT_SIZE);
+        return parseInteger(width, 3);
+    }
+
+    public Color parsePointColor() {
+        String color = getValue(POINT_COLOR);
+        return parseColor(color);
+    }
+
+    public boolean parseShowPoints() {
+        String show = getValue(SHOW_POINTS);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowLine() {
+        String show = getValue(SHOW_LINE);
+        return parseBoolean(show, true);
+    }
+
+    public int parseTextStyle() {
+        String style = getValue(LABEL_FONT_STYLE);
+        if (style == null) {
+            return Font.PLAIN;
+        }
+
+        if (style.equals("italic")) {
+            return Font.ITALIC;
+        }
+        if (style.equals("bold")) {
+            return Font.BOLD;
+        }
+        return Font.PLAIN;
+    }
+
+    public TextStyle parseComplexTextStyle() {
+        return new TextStyle(
+            parseTextColor(),
+            parseTextFont(),
+            parseTextBackground(),
+            parseLabelShowBackground(),
+            !parseTextOrientation().equals("horizontal"));
+    }
+
+    public LineStyle parseComplexLineStyle() {
+        return new LineStyle(
+            parseLineColorField(),
+            Integer.valueOf(parseLineWidth()));
+    }
+
+    public boolean parseShowVerticalLine() {
+        String show = getValue(SHOW_VERTICAL_LINE);
+        return parseBoolean(show, true);
+    }
+
+    public boolean parseShowHorizontalLine() {
+        String show = getValue(SHOW_HORIZONTAL_LINE);
+        return parseBoolean(show, true);
+    }
+
+    public double parseBandWidth() {
+        String bandWidth = getValue(BANDWIDTH);
+        return parseDouble(bandWidth, 0);
+    }
+
+    private static Color parseColor(String colorString) {
+        if (colorString == null) {
+            return null;
+        }
+        if (colorString.indexOf("#") == 0) {
+            return parseHexColor(colorString);
+        }
+        if (colorString.indexOf(",") >= 0) {
+            return parseRGB(colorString);
+        }
+
+        return null;
+    }
+
+
+    /**
+     * Parse a string like "#00CC22" and return the corresponding color.
+     *
+     * @param hex The hex color value.
+     *
+     * @return a Color or null, if <i>hex</i> is empty.
+     */
+    private static Color parseHexColor(String hex) {
+        return hex != null
+            ? Color.decode(hex)
+            : null;
+    }
+
+
+    public boolean parseShowArea() {
+        String show = getValue(SHOW_AREA);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowAreaLabel() {
+        String show = getValue(SHOW_AREA_LABEL);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowPointLabel() {
+        String show = getValue(SHOW_POINT_LABEL);
+        return parseBoolean(show, false);
+    }
+
+    public boolean parseShowExtraMark() {
+        String show = getValue(SHOWEXTRAMARK);
+        return parseBoolean(show, false);
+    }
+
+    public int parseTextSize() {
+        String size = getValue(LABEL_FONT_SIZE);
+        if (size == null) {
+            return 10;
+        }
+
+        try {
+            return Integer.parseInt(size);
+        }
+        catch (NumberFormatException nfe) {
+            // Do nothing
+        }
+        return 10;
+    }
+
+    /**
+     * Parse a string like "103, 100, 0" and return a corresping color.
+     * @param rgbtext Color as string representation, e.g. "255,0,20".
+     * @return Color, null in case of issues.
+     */
+    public static Color parseRGB(String rgbtext) {
+        if (rgbtext == null) {
+            return null;
+        }
+        String rgb[] = rgbtext.split(",");
+        try {
+            return new Color(
+                Integer.parseInt(rgb[0].trim()),
+                Integer.parseInt(rgb[1].trim()),
+                Integer.parseInt(rgb[2].trim()));
+        }
+        catch (NumberFormatException nfe) {
+            // Do nothing
+        }
+        return null;
+    }
+
+    private String getLineColorString() {
+        return getValue(LINE_COLOR);
+    }
+
+
+    /** Get show border as string. */
+    private String getShowBorderString() {
+        return getValue(SHOW_BORDER);
+    }
+
+
+    /** Get fill color as string. */
+    private String getFillColorString() {
+        return getValue(FILL_COLOR);
+    }
+
+    private String getSymbol() {
+        return getValue(SYMBOL);
+    }
+
+    private String getTransparencyString() {
+        return getValue(TRANSPARENCY);
+    }
+
+
+    private String getAreaTransparencyString() {
+        return getValue(AREA_TRANSPARENCY);
+    }
+
+
+    private String getShowMinimum() {
+        return getValue(SHOW_MINIMUM);
+    }
+
+
+    private String getShowMaximum() {
+        return getValue(SHOW_MAXIMUM);
+    }
+
+    /**
+     * Gets color from color field.
+     * @param theme    the theme document.
+     * @return color.
+     */
+    public Color parseFillColorField() {
+        return parseRGB(getFillColorString());
+    }
+
+    public boolean parseShowBorder() {
+        return parseBoolean(getShowBorderString(), false);
+    }
+
+    public int parseTransparency() {
+        return parseInteger(getTransparencyString(), 50);
+    }
+
+
+    /**
+     * Gets color from color field.
+     * @return color.
+     */
+    public Color parseLineColorField() {
+        String lineColorStr = getLineColorString();
+        if (logger.isDebugEnabled()) {
+            logger.debug("parseLineColorField: lineColorStr = " +
+                (lineColorStr == null
+                     ? "null"
+                     : lineColorStr));
+        }
+        return parseColor(lineColorStr);
+    }
+
+
+    public Color parseAreaLineColorField() {
+        String lineColorStr = getAreaLineColorString();
+        if (logger.isDebugEnabled()) {
+            logger.debug("parseLineColorField: lineColorStr = " +
+                (lineColorStr == null
+                    ? "null"
+                    : lineColorStr));
+        }
+        return parseColor(lineColorStr);
+    }
+
+
+    private String getAreaLineColorString() {
+        return getValue(AREA_LINE_COLOR);
+    }
+
+
+    public boolean parseShowMinimum() {
+        return parseBoolean(getShowMinimum(), false);
+    }
+
+
+    public boolean parseShowMaximum() {
+        return parseBoolean(getShowMaximum(), false);
+    }
+
+
+    /**
+     * Creates a MapserverStyle from the given XML theme.
+     * This method uses a start- and endcolor to interpolate a
+     * given number of color classes for the MapserverStyle.
+     * @param theme
+     * @return String representation of the MapserverStyle
+     */
+    public String createDynamicMapserverStyle(
+        float    from,
+        float    to,
+        float    step,
+        CallMeta meta
+    ) {
+        MapserverStyle ms = new MapserverStyle();
+
+        String strStartColor = getValue(WSPLGEN_STARTCOLOR);
+        Color startColor = strStartColor != null
+            ? parseColor(strStartColor)
+            : new Color(178, 201, 215);
+        String strEndColor = getValue(WSPLGEN_ENDCOLOR);
+        Color endColor = strEndColor != null
+            ? parseColor(strEndColor)
+            : new Color(2, 27, 42);
+
+        to = to != 0 ? to : 9999;
+        step = step != 0 ? step : to;
+
+        int numClasses = (int)((to - from) / step);
+
+        float rd = (endColor.getRed()   - startColor.getRed())   / (float)numClasses;
+        float gd = (endColor.getGreen() - startColor.getGreen()) / (float)numClasses;
+        float bd = (endColor.getBlue()  - startColor.getBlue())  / (float)numClasses;
+
+        if (numClasses > 1) {
+            // Desktop Flys always added a last "and larger class"
+            numClasses += 1;
+        }
+
+        for (int n = 0; n < numClasses; n++) {
+            StringBuilder newColor = new StringBuilder();
+            newColor.append(startColor.getRed()   + Math.round(n * rd));
+            newColor.append(' ');
+            newColor.append(startColor.getGreen() + Math.round(n * gd));
+            newColor.append(' ');
+            newColor.append(startColor.getBlue()  + Math.round(n * bd));
+
+            String expr = createWSPLGENClassExpression(from + n * step, step, n + 1, numClasses);
+            String name = createWSPLGENClassName(from + n * step, step, n + 1, numClasses, meta);
+
+            Clazz c = new Clazz(name);
+            Style s = new Style();
+            s.setColor(newColor.toString());
+            s.setSize(5);
+
+            c.addItem(new Expression("(" + expr + ")"));
+            c.addItem(s);
+
+            ms.addClazz(c);
+        }
+
+        return ms.toString();
+    }
+
+
+    protected static String createWSPLGENClassExpression(float val, float step, int idx, int maxIdx) {
+        if (idx < maxIdx) {
+            return "[DIFF] >= " + val + " AND  [DIFF] < " + (val + step);
+        }
+        else {
+            return "[DIFF] >= " + val;
+        }
+    }
+
+    /**
+     * Creates a class name for the mapfile style that visualizes a floodmap.
+     * The class names are used in the map's legend.
+     *
+     * @param val       Current isobath value.
+     * @param step      Difference between to class values.
+     * @param idx       Current class index that is being processed.
+     * @param maxIdx    Highest class index.
+     * @param meta      Caller meta object used to determine locale.
+     * @return
+     */
+    protected static String createWSPLGENClassName(
+        float    val,
+        float    step,
+        int      idx,
+        int      maxIdx,
+        CallMeta meta
+    ) {
+        assert meta != null : "CallMeta instance is null";
+
+        if (idx < maxIdx) {
+            return Resources.getMsg(meta, MSG_ISOBATH_CLASS,
+                    new Object[] {val, val + step});
+        }
+        return Resources.getMsg(meta, MSG_ISOBATH_LASTCLASS,
+                new Object[] {val});
+    }
+
+
+    public String createMapserverStyle() {
+        String symbol    = getSymbol();
+        String backcolor = getLabelBackgroundColorString();
+        String linecolor = getLineColorString();
+        if (linecolor == null) {
+            logger.warn("createMapserverStyle: linecolor String is empty");
+            linecolor = "0,128,255";
+        }
+
+        int linewidth = parseLineWidth();
+
+        MapserverStyle ms = new MapserverStyle();
+
+        Clazz c = new Clazz(" ");
+
+        Style s = new Style();
+        s.setOutlineColor(linecolor.replace(",", " "));
+
+        if (backcolor != null) {
+            s.setColor(backcolor.replace(",", " "));
+        }
+
+        s.setSize(linewidth);
+        s.setSymbol(symbol);
+        c.addItem(s);
+
+        String textcolor = getTextColorString();
+        int    textsize  = parseTextSize();
+
+        if (textcolor != null && textsize > 0) {
+            Label l = new Label();
+            l.setColor(textcolor.replace(",", " "));
+            l.setSize(textsize);
+            c.addItem(l);
+        }
+
+        ms.addClazz(c);
+
+        return ms.toString();
+    }
+
+
+    private String getAreaBackgroundColorString() {
+        return getValue(AREA_BACKGROUND_COLOR);
+    }
+
+
+    public Color parseAreaBackgroundColor() {
+        return parseColor(getAreaBackgroundColorString());
+    }
+
+
+    public int parseAreaTransparency() {
+        return parseAreaTransparency(50);
+    }
+
+    public int parseAreaTransparency(int alpha) {
+        return parseInteger(getAreaTransparencyString(), alpha);
+    }
+
+
+    public boolean parseAreaShowBorder() {
+        return parseBoolean(getAreaShowBorderString(), false);
+    }
+
+
+    private String getAreaShowBorderString() {
+        return getValue(AREA_SHOW_BORDER);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/themes/ThemeMapping.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/themes/ThemeMapping.java	Fri Sep 27 17:36:50 2013 +0200
@@ -133,7 +133,8 @@
         }
 
         // Test.
-        if (artifact.getDataAsString(parts[0]).equals(parts[1])) {
+        String artData = artifact.getDataAsString(parts[0]);
+        if (artData != null && artData.equals(parts[1])) {
             logger.debug("Matches master Attribute.");
             return true;
         }
--- a/artifacts/src/main/java/org/dive4elements/river/utils/ArtifactMapfileGenerator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/ArtifactMapfileGenerator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,12 +10,13 @@
 
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.river.artifacts.D4EArtifact;
-import org.dive4elements.river.artifacts.access.RiverAccess;
+import org.dive4elements.river.artifacts.access.DGMAccess;
 import org.dive4elements.river.artifacts.model.LayerInfo;
 import org.dive4elements.river.artifacts.model.map.WMSDBLayerFacet;
 import org.dive4elements.river.artifacts.model.map.WMSLayerFacet;
 import org.dive4elements.river.artifacts.model.map.WSPLGENLayerFacet;
 import org.dive4elements.river.artifacts.resources.Resources;
+import org.dive4elements.river.artifacts.states.FloodMapState;
 
 import java.io.File;
 import java.io.FileNotFoundException;
@@ -32,6 +33,12 @@
 
     private static Logger logger = Logger.getLogger(ArtifactMapfileGenerator.class);
 
+    public static final String FLOODMAP_UESK_KEY =
+        "floodmap.uesk";
+
+    public static final String FLOODMAP_UESK_DEF =
+        "Floodmap: {0}-km {1,number,####} - {2,number,####} - {3}";
+
     @Override
     protected String getVelocityLogfile() {
         return RiverUtils.getXPathString(RiverUtils.XPATH_FLOODMAP_VELOCITY_LOGFILE);
@@ -77,18 +84,33 @@
     {
         logger.debug("createUeskLayer");
 
+        String identifier = flys.identifier();
+
+        DGMAccess access = new DGMAccess(flys);
+
         LayerInfo layerinfo = new LayerInfo();
-        layerinfo.setName(MS_WSPLGEN_PREFIX + flys.identifier());
+        layerinfo.setName(MS_WSPLGEN_PREFIX + identifier);
         layerinfo.setType("POLYGON");
-        layerinfo.setDirectory(flys.identifier());
+        layerinfo.setDirectory(identifier);
         layerinfo.setData(WSPLGEN_RESULT_SHAPE);
-        layerinfo.setTitle(Resources.getMsg(Resources.getLocale(context.getMeta()),
-                                            "floodmap.uesk",
-                                            "Floodmap"));
+
+        String river = access.getRiver();
+
+        double from = access.hasFrom() ? access.getFrom() : 0d;
+        double to   = access.hasTo()   ? access.getTo()   : 0d;
+
+        String title = Resources.format(
+            context.getMeta(),
+            FLOODMAP_UESK_KEY,
+            FLOODMAP_UESK_DEF,
+            river,
+            from, to,
+            identifier);
+
+        layerinfo.setTitle(title);
+
         layerinfo.setStyle(style);
-        RiverAccess access = new RiverAccess(flys);
-        String river = access.getRiver();
-        layerinfo.setSrid(RiverUtils.getRiverDGMSrid(river));
+        layerinfo.setSrid(String.valueOf(access.getDGM().getSrid()));
 
         String name = MS_LAYER_PREFIX + wms.getName();
 
@@ -99,7 +121,7 @@
         }
 
         try {
-            File dir = new File(getShapefileBaseDir(), flys.identifier());
+            File dir = new File(getShapefileBaseDir(), identifier);
             writeLayer(layerinfo, new File(dir, name), template);
         }
         catch (FileNotFoundException fnfe) {
@@ -135,11 +157,11 @@
         ShapefileReader sfr = new ShapefileReader(sf, true, false, null);
         ShapefileHeader sfh = sfr.getHeader();
 
-        String group      = uuid + MS_USERSHAPE_PREFIX;
+        String group      = MS_USERSHAPE_PREFIX + uuid;
         String groupTitle = "I18N_USER_SHAPE_TITLE";
 
         LayerInfo info = new LayerInfo();
-        info.setName(MS_USERSHAPE_PREFIX + uuid);
+        info.setName(MS_LAYER_PREFIX + FloodMapState.WSPLGEN_USER_RGD + uuid);
         if (sfh.getShapeType().isLineType()) {
             info.setType("LINE");
         }
@@ -156,7 +178,11 @@
         info.setGroupTitle(groupTitle);
         info.setSrid(wms.getSrid());
 
-        String nameUser = MS_LAYER_PREFIX + wms.getName();
+        //String nameUser = MS_LAYER_PREFIX + wms.getName();
+        // TODO: This rewrites the user-rgd mapfile fragment generated by
+        // HWSBarrierState. Otherwise we would have to fragments with same
+        // layer name. Should be refactored...
+        String nameUser = MS_LAYER_PREFIX + "user-rgd";
 
         Template tpl = getTemplateByName(SHP_LAYER_TEMPLATE);
         if (tpl == null) {
@@ -225,13 +251,12 @@
     }
 
     @Override
-        protected String getMapfilePath() {
-            return RiverUtils.getXPathString(RiverUtils.XPATH_FLOODMAP_MAPFILE_PATH);
-        }
+    protected String getMapfilePath() {
+        return RiverUtils.getXPathString(RiverUtils.XPATH_FLOODMAP_MAPFILE_PATH);
+    }
 
     @Override
-        protected String getMapfileTemplate() {
-            return RiverUtils.getXPathString(RiverUtils.XPATH_FLOODMAP_MAPFILE_TEMPLATE);
-        }
-
+    protected String getMapfileTemplate() {
+        return RiverUtils.getXPathString(RiverUtils.XPATH_FLOODMAP_MAPFILE_TEMPLATE);
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/BatchLoader.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,116 @@
+/* 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.utils;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+import org.hibernate.SQLQuery;
+import org.hibernate.Session;
+
+/** To reduce the number of SQL queries send to the backend
+ *  (mainly by the fixings overviews) we execute them in batches of ids
+ *  and store the results in a small cache.
+ *  TODO: It currently relies on dynamic SQL.
+ *  Is there a way to use Hibernate with java.sql.Array
+ *  in cross database compatible manner?
+ */
+public abstract class BatchLoader<T> {
+
+    private static Logger log = Logger.getLogger(BatchLoader.class);
+
+    public static final int BATCH_SIZE = 100;
+
+    private Map<Integer, T> loaded;
+    private List<Integer>   rest;
+    private Session         session;
+    private String          sqlTemplate;
+
+    public BatchLoader(
+        List<Integer> columns,
+        Session       session,
+        String        sqlTemplate
+    ) {
+        rest             = new ArrayList<Integer>(columns.size());
+        loaded           = new HashMap<Integer, T>();
+        this.session     = session;
+        this.sqlTemplate = sqlTemplate;
+
+        // Insert in reverse order to minize searching.
+        for (int i = columns.size()-1; i >= 0; --i) {
+            rest.add(columns.get(i));
+        }
+    }
+
+    /** Searches for id and fill a batch to load containing the found id. */
+    private List<Integer> prepareBatch(int id) {
+        List<Integer> batch = new ArrayList<Integer>(BATCH_SIZE);
+
+        boolean found = false;
+
+        for (int i = rest.size()-1; batch.size() < BATCH_SIZE && i >= 0; --i) {
+            Integer cid = rest.get(i);
+            if (cid == id) {
+                found = true;
+                batch.add(cid);
+                rest.remove(i);
+            }
+            else if ((found && batch.size() < BATCH_SIZE)
+                 || (!found && batch.size() < BATCH_SIZE-1)) {
+                batch.add(cid);
+                rest.remove(i);
+            }
+        }
+
+        return batch;
+    }
+
+    /** Converts id to a list of comma separated ints. */
+    private static String idsAsString(List<Integer> ids) {
+        StringBuilder sb = new StringBuilder();
+        for (Iterator<Integer> i = ids.iterator(); i.hasNext();) {
+            sb.append(i.next());
+            if (i.hasNext()) {
+                sb.append(',');
+            }
+        }
+        return sb.toString();
+    }
+
+    /** Get data for id. */
+    public T get(int id) {
+        T already = loaded.get(id);
+        if (already != null) {
+            return already;
+        }
+
+        List<Integer> batch = prepareBatch(id);
+        if (batch.isEmpty()) {
+            return null;
+        }
+        String sql = sqlTemplate.replace("$IDS", idsAsString(batch));
+        if (log.isDebugEnabled()) {
+            log.debug(sql + " " + sql.length());
+        }
+        fill(session.createSQLQuery(sql));
+        return get(id);
+    }
+
+    /** Call this from fill() to store data in the cache. */
+    protected void cache(int key, T data) {
+        loaded.put(key, data);
+    }
+
+    /** Override this to fill the cache */
+    protected abstract void fill(SQLQuery query);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/CompareUtil.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,25 @@
+/* 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.utils;
+
+/** Utils to deal with Comparisons. */
+public class CompareUtil
+{
+    /** Singleton. */
+    private CompareUtil() {
+    }
+
+    /** Return true if a and b are either both null or equal(). */
+    public static <T> boolean areSame(T a, T b) {
+       if (a == null) return b == null;
+       if (b == null) return false;
+       return a.equals(b);
+    }
+}
+// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/DataUtil.java	Fri Sep 27 17:36:50 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/Formatter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/Formatter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -94,11 +94,15 @@
     // SQ Relation
     public static final int SQ_RELATION_KM_MIN_DIGITS = 2;
     public static final int SQ_RELATION_KM_MAX_DIGITS = 2;
-    public static final int SQ_RELATION_A_MAX_DIGITS  = 7;
-    public static final int SQ_RELATION_A_MIN_DIGITS  = 7;
+    public static final int SQ_RELATION_A_MAX_DIGITS  = 2;
+    public static final int SQ_RELATION_A_MIN_DIGITS  = 2;
     public static final int SQ_RELATION_B_MAX_DIGITS  = 3;
     public static final int SQ_RELATION_B_MIN_DIGITS  = 3;
 
+    // OTHER
+    public static final int CSV_DIAGRAM_DATA_MAX_DIGITS  = 3;
+    public static final int CSV_DIAGRAM_DATA_MIN_DIGITS  = 3;
+
     /**
      * Creates a localized NumberFormatter with given range of decimal digits.
      * @param m CallMeta to find the locale.
@@ -191,6 +195,18 @@
                 WATERLEVEL_KM_MAX_DIGITS);
     }
 
+    /**
+     * Returns the number formatter for data exported from diagram (not from
+     * calculation.
+     *
+     * @return the number formatter for csv data from diagra.
+     */
+    public static NumberFormat getCSVFormatter(CallContext context) {
+        return getFormatter(
+                context,
+                CSV_DIAGRAM_DATA_MIN_DIGITS,
+                CSV_DIAGRAM_DATA_MAX_DIGITS);
+    }
 
     public static NumberFormat getWaterlevelW(CallMeta meta) {
         return getFormatter(
@@ -442,7 +458,7 @@
     }
 
     public static NumberFormat getSQRelationA(CallContext context) {
-        return getFormatter(
+        return getScientificFormater(
                 context,
                 SQ_RELATION_A_MIN_DIGITS,
                 SQ_RELATION_A_MAX_DIGITS);
--- a/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/Pair.java	Fri Sep 27 17:36:50 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/java/org/dive4elements/river/utils/RiverUtils.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/java/org/dive4elements/river/utils/RiverUtils.java	Fri Sep 27 17:36:50 2013 +0200
@@ -10,6 +10,7 @@
 
 import org.dive4elements.artifactdatabase.state.State;
 import org.dive4elements.artifacts.Artifact;
+import org.dive4elements.artifacts.ArtifactNamespaceContext;
 import org.dive4elements.artifacts.CallContext;
 import org.dive4elements.artifacts.common.utils.Config;
 import org.dive4elements.artifacts.common.utils.XMLUtils;
@@ -80,9 +81,6 @@
     public static final String XPATH_FLOODMAP_RIVER_PROJECTION =
         "/artifact-database/floodmap/river[@name=$name]/srid/@value";
 
-    public static final String XPATH_FLOODMAP_DGM_PROJECTION =
-        "/artifact-database/floodmap/river[@name=$name]/dgm-srid/@value";
-
     public static final String XPATH_FLOODMAP_SHAPEFILE_DIR =
         "/artifact-database/floodmap/shapefile-path/@value";
 
@@ -104,6 +102,11 @@
     public static final String XPATH_FLOODMAP_MAPSERVER_TEMPLATE_PATH =
         "/artifact-database/floodmap/mapserver/templates/@path";
 
+    public static final String CURRENT_KM = "currentKm";
+
+    public static final String XPATH_CHART_CURRENTKM =
+        "/art:action/art:attributes/art:currentKm/@art:km";
+
 
     private RiverUtils() {
     }
@@ -427,24 +430,6 @@
             variables);
     }
 
-    public static String getRiverDGMSrid(String rivername) {
-        Map<String, String> variables = new HashMap<String, String>(1);
-        variables.put("name", rivername);
-
-        Document cfg = Config.getConfig();
-
-        String dgm = (String) XMLUtils.xpath(
-            cfg,
-            XPATH_FLOODMAP_DGM_PROJECTION,
-            XPathConstants.STRING,
-            null,
-            variables);
-        if (logger.isDebugEnabled()) {
-            logger.debug("Use EPSG:" + dgm + " for DGM");
-        }
-        return dgm;
-    }
-
     /**
      * Return the (first) Gauge corresponding to the given location(s) of
      * the artifact.
@@ -491,10 +476,14 @@
 
     public static Gauge getReferenceGauge(D4EArtifact flys) {
         Long officialNumber = flys.getDataAsLong("reference_gauge");
+        String river = getRivername(flys);
 
-        return officialNumber != null
-            ? Gauge.getGaugeByOfficialNumber(officialNumber)
-            : null;
+        if (officialNumber != null && river != null) {
+            return Gauge.getGaugeByOfficialNumber(officialNumber, river);
+        } else if (officialNumber != null) {
+            return Gauge.getGaugeByOfficialNumber(officialNumber);
+        }
+        return null;
     }
 
 
@@ -925,5 +914,40 @@
 
         return river.determineGauges(dist[0], dist[1]);
     }
+
+    /** Round a Q in the AT format style **/
+    public static double roundQ(double q) {
+        if (q < 10d) q = Math.rint((q*1000d)) / 1000d;
+        else if (q < 100d) q = Math.rint((q*100d)) / 100d;
+        else if (q < 1000d) q = Math.rint((q*10d)) / 10d;
+        else if (q >= 1000d) q = Math.rint(q);
+        return q;
+    }
+
+    /** Parses the request and checks if it contains a currentKM setting.
+     * If this is the case the currentKM is added to the context.*/
+    public static void setKMFromRequestInContext(Document request,
+            CallContext context) {
+        Double dKm;
+        String km = XMLUtils.xpathString(
+            request,
+            XPATH_CHART_CURRENTKM,
+            ArtifactNamespaceContext.INSTANCE);
+
+        if (km == null) {
+            return;
+        }
+
+        try {
+            dKm = Double.valueOf(km);
+        } catch (NumberFormatException nfe) {
+            return;
+        }
+
+        if (logger.isDebugEnabled()) {
+            logger.debug("currentKm = " + dKm);
+        }
+        context.putContextValue(CURRENT_KM, dKm);
+    }
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/artifacts/src/main/java/org/dive4elements/river/utils/ThemeUtil.java	Fri Sep 13 18:29:01 2013 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,827 +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.utils;
-
-import org.dive4elements.artifacts.CallMeta;
-import org.dive4elements.artifacts.common.utils.XMLUtils;
-import org.dive4elements.river.artifacts.model.MapserverStyle;
-import org.dive4elements.river.artifacts.model.MapserverStyle.Clazz;
-import org.dive4elements.river.artifacts.model.MapserverStyle.Expression;
-import org.dive4elements.river.artifacts.model.MapserverStyle.Label;
-import org.dive4elements.river.artifacts.model.MapserverStyle.Style;
-import org.dive4elements.river.artifacts.resources.Resources;
-
-import java.awt.Color;
-import java.awt.Font;
-
-import org.apache.log4j.Logger;
-import org.w3c.dom.Document;
-
-
-/**
- * Utility to deal with themes and their representations.
- */
-public class ThemeUtil {
-
-    /** Private logger. */
-    private static Logger logger =
-            Logger.getLogger(ThemeUtil.class);
-
-    private static final String MSG_ISOBATH_CLASS = "floodmap.isobath.class";
-
-    private static final String MSG_ISOBATH_LASTCLASS = "floodmap.isobath.lastclass";
-
-    public final static String XPATH_FILL_COLOR =
-            "/theme/field[@name='fillcolor']/@default";
-
-    public final static String XPATH_LINE_COLOR =
-            "/theme/field[@name='linecolor']/@default";
-
-    public final static String XPATH_AREA_LINE_COLOR =
-            "/theme/field[@name='areabordercolor']/@default";
-
-    public static final String XPATH_LINE_SIZE =
-            "/theme/field[@name='linesize']/@default";
-
-    public static final String XPATH_LINE_STYLE =
-            "/theme/field[@name='linetype']/@default";
-
-    public static final String XPATH_POINT_SIZE =
-            "/theme/field[@name='pointsize']/@default";
-
-    public static final String XPATH_POINT_COLOR =
-            "/theme/field[@name='pointcolor']/@default";
-
-    public final static String XPATH_SHOW_BORDER =
-            "/theme/field[@name='showborder']/@default";
-
-    public final static String XPATH_AREA_SHOW_BORDER =
-            "/theme/field[@name='showborder']/@default";
-
-    public final static String XPATH_SHOW_POINTS =
-            "/theme/field[@name='showpoints']/@default";
-
-    public final static String XPATH_SHOW_LINE =
-            "/theme/field[@name='showlines']/@default";
-
-    public final static String XPATH_SHOW_VERTICAL_LINE =
-            "/theme/field[@name='showverticalline']/@default";
-
-    public final static String XPATH_SHOW_HORIZONTAL_LINE =
-            "/theme/field[@name='showhorizontalline']/@default";
-
-    public final static String XPATH_SHOW_LINE_LABEL =
-            "/theme/field[@name='showlinelabel']/@default";
-
-    public final static String XPATH_SHOW_POINT_LABEL =
-            "/theme/field[@name='showpointlabel']/@default";
-
-    public final static String XPATH_SHOW_WIDTH =
-            "/theme/field[@name='showwidth']/@default";
-
-    public final static String XPATH_SHOW_LEVEL =
-            "/theme/field[@name='showlevel']/@default";
-
-    public final static String XPATH_TRANSPARENCY =
-            "/theme/field[@name='transparency']/@default";
-
-    public final static String XPATH_AREA_TRANSPARENCY =
-            "/theme/field[@name='areatransparency']/@default";
-
-    public final static String XPATH_SHOW_AREA =
-            "/theme/field[@name='showarea']/@default";
-
-    public final static String XPATH_SHOW_MIDDLE_HEIGHT =
-            "/theme/field[@name='showmiddleheight']/@default";
-
-    public final static String XPATH_LABEL_FONT_COLOR =
-            "/theme/field[@name='labelfontcolor']/@default";
-
-    public final static String XPATH_LABEL_FONT_SIZE =
-            "/theme/field[@name='labelfontsize']/@default";
-
-    public final static String XPATH_LABEL_FONT_FACE =
-            "/theme/field[@name='labelfontface']/@default";
-
-    public final static String XPATH_LABEL_FONT_STYLE =
-            "/theme/field[@name='labelfontstyle']/@default";
-
-    public final static String XPATH_TEXT_ORIENTATION =
-            "/theme/field[@name='textorientation']/@default";
-
-    public final static String XPATH_LABEL_BGCOLOR =
-            "/theme/field[@name='labelbgcolor']/@default";
-
-    public final static String XPATH_LABEL_SHOW_BACKGROUND =
-            "/theme/field[@name='labelshowbg']/@default";
-
-    public final static String XPATH_BACKGROUND_COLOR =
-            "/theme/field[@name='backgroundcolor']/@default";
-
-    public final static String XPATH_AREA_BACKGROUND_COLOR =
-            "/theme/field[@name='areabgcolor']/@default";
-
-    public final static String XPATH_SYMBOL =
-            "/theme/field[@name='symbol']/@default";
-
-    public final static String XPATH_SHOW_MINIMUM =
-            "/theme/field[@name='showminimum']/@default";
-
-    public final static String XPATH_SHOW_MAXIMUM =
-            "/theme/field[@name='showmaximum']/@default";
-
-    public final static String XPATH_WSPLGEN_FIELDS =
-            "/theme[@name='WSPLGEN']/field";
-
-    public final static String XPATH_WSPLGEN_STARTCOLOR =
-            "/theme/field[@name='startcolor']/@default";
-
-    public final static String XPATH_WSPLGEN_ENDCOLOR =
-            "/theme/field[@name='endcolor']/@default";
-
-    public final static String XPATH_WSPLGEN_NUMCLASSES =
-            "/theme/field[@name='numclasses']/@default";
-
-    /** XPATH to bandwidth field. */
-    public final static String XPATH_BANDWIDTH =
-            "/theme/field[@name='bandwidth']/@default";
-
-    /** XPATH to find showextramark field. */
-    public final static String XPATH_SHOWEXTRAMARK =
-            "/theme/field[@name='showextramark']/@default";
-
-    /** Parse string to be boolean with default if empty or unrecognized. */
-    public static boolean parseBoolean(String value, boolean defaultsTo) {
-        if (value == null || value.length() == 0) {
-            return defaultsTo;
-        }
-        if (value.equals("false")) {
-            return false;
-        }
-        else if (value.equals("true")) {
-            return true;
-        }
-        else {
-            return defaultsTo;
-        }
-    }
-
-
-    /**
-     * Attempt converting \param value to an integer, in failing cases,
-     * return \param defaultsTo.
-     * @param value String to be converted to integer.
-     * @param defaultsTo Default to return if conversion failed.
-     * @return \param value as integer or defaultsto if conversion failed.
-     */
-    public static int parseInteger(String value, int defaultsTo) {
-        if (value == null || value.length() == 0) {
-            return defaultsTo;
-        }
-
-        try {
-            return Integer.parseInt(value);
-        }
-        catch (NumberFormatException nfe) {
-            // do nothing
-        }
-
-        return defaultsTo;
-    }
-
-
-    /**
-     * Attempt converting \param value to a double, in failing cases,
-     * return \param defaultsTo.
-     * @param value String to be converted to double.
-     * @param defaultsTo Default to return if conversion failed.
-     * @return \param value as integer or defaultsto if conversion failed.
-     */
-    public static double parseDouble(String value, double defaultsTo) {
-        if (value == null || value.length() == 0) {
-            return defaultsTo;
-        }
-
-        try {
-            return Double.parseDouble(value);
-        }
-        catch (NumberFormatException nfe) {
-            // do nothing
-        }
-
-        return defaultsTo;
-    }
-
-
-    /**
-     * Parses line width, defaulting to 0.
-     * @param theme the theme
-     */
-    public static int parseLineWidth(Document theme) {
-        String size = XMLUtils.xpathString(theme, XPATH_LINE_SIZE, null);
-        if (size == null || size.length() == 0) {
-            return 0;
-        }
-
-        try {
-            return Integer.parseInt(size);
-        }
-        catch (NumberFormatException nfe) {
-            logger.warn("Unable to set line size from string: '" + size + "'");
-        }
-        return 0;
-    }
-
-
-    /**
-     * Parse band width, defaulting to 0.
-     * @param theme the theme.
-     */
-    public static double parseBandWidth(Document theme) {
-        String bandWidth = XMLUtils.xpathString(theme, XPATH_BANDWIDTH, null);
-
-        return parseDouble(bandWidth, 0);
-    }
-
-
-    public static int parsePointWidth(Document theme) {
-        String width = XMLUtils.xpathString(theme, XPATH_POINT_SIZE, null);
-
-        return parseInteger(width, 3);
-    }
-
-
-    public static Color parsePointColor(Document theme) {
-        String color = XMLUtils.xpathString(theme, XPATH_POINT_COLOR, null);
-        logger.debug("parsePointColor(): color = " + color);
-        return parseColor(color);
-    }
-
-
-    /**
-     * Parses the line style, defaulting to '10'.
-     * @param theme The theme.
-     */
-    public static float[] parseLineStyle(Document theme) {
-        String dash = XMLUtils.xpathString(theme, XPATH_LINE_STYLE, null);
-
-        float[] def = {10};
-        if (dash == null || dash.length() == 0) {
-            return def;
-        }
-
-        String[] pattern = dash.split(",");
-        if(pattern.length == 1) {
-            return def;
-        }
-
-        try {
-            float[] dashes = new float[pattern.length];
-            for (int i = 0; i < pattern.length; i++) {
-                dashes[i] = Float.parseFloat(pattern[i]);
-            }
-            return dashes;
-        }
-        catch(NumberFormatException nfe) {
-            logger.warn("Unable to set dash from string: '" + dash + "'");
-            return def;
-        }
-    }
-
-
-    /**
-     * Parses text size, defaulting to 10.
-     * @param theme The theme.
-     */
-    public static int parseTextSize(Document theme, String path) {
-        String size = XMLUtils.xpathString(theme, path, null);
-        if (size == null || size.length() == 0) {
-            return 10;
-        }
-
-        try {
-            return Integer.parseInt(size);
-        }
-        catch (NumberFormatException nfe) {
-        }
-        return 10;
-    }
-
-
-    public static int parseTextSize(Document theme) {
-        return parseTextSize(theme, XPATH_LABEL_FONT_SIZE);
-    }
-
-
-    /**
-     * Parses the attribute 'showextramark', defaults to false.
-     * @param theme The theme.
-     */
-    public static boolean parseShowExtraMark(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOWEXTRAMARK, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses the attribute 'showpoints', defaults to false.
-     * @param theme The theme.
-     */
-    public static boolean parseShowPoints(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_POINTS, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses the attribute 'showmiddleheight', defaults to false.
-     * @param theme The theme.
-     */
-    public static boolean parseShowMiddleHeight(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_MIDDLE_HEIGHT, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses the attribute 'showarea', defaults to false.
-     * @param theme The theme.
-     */
-    public static boolean parseShowArea(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_AREA, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses the attribute 'showverticalline', defaults to true.
-     * @param theme The theme.
-     */
-    public static boolean parseShowVerticalLine(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_VERTICAL_LINE, null);
-        return parseBoolean(show, true);
-    }
-
-    /**
-     * Parses the attribute 'showhorizontalline', defaults to true.
-     * @param theme The theme.
-     */
-    public static boolean parseShowHorizontalLine(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_HORIZONTAL_LINE, null);
-        return parseBoolean(show, true);
-    }
-
-    /**
-     * Parses the attribute 'showlines', defaults to true.
-     * @param theme The theme.
-     */
-    public static boolean parseShowLine(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_LINE, null);
-        return parseBoolean(show, true);
-    }
-
-    /**
-     * Parses the attribute 'showlinelabel', defaults to true.
-     * @param theme The theme.
-     */
-    public static boolean parseShowLineLabel(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_LINE_LABEL, null);
-        return parseBoolean(show, false);
-    }
-
-    public static boolean parseShowPointLabel(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_POINT_LABEL, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses text color.
-     * @param theme The theme.
-     */
-    public static Color parseTextColor(Document theme) {
-        return parseRGB(getTextColorString(theme));
-    }
-
-
-    /**
-     * Parses the font.
-     * @param theme The theme.
-     */
-    public static Font parseTextFont(Document theme) {
-        String font = XMLUtils.xpathString(theme, XPATH_LABEL_FONT_FACE, null);
-        if (font == null || font.length() == 0) {
-            return null;
-        }
-
-        int size = parseTextSize(theme);
-        int style = parseTextStyle(theme);
-        Font f = new Font (font, style, size);
-        return f;
-    }
-
-
-    /**
-     * Parses the text style, defaults to 'Font.PLAIN'.
-     * @param theme The theme.
-     */
-    public static int parseTextStyle(Document theme, String path) {
-        String style = XMLUtils.xpathString(theme, path, null);
-        if (style == null || style.length() == 0) {
-            return Font.PLAIN;
-        }
-
-        if (style.equals("italic")) {
-            return Font.ITALIC;
-        }
-        else if (style.equals("bold")) {
-            return Font.BOLD;
-        }
-        else {
-            return Font.PLAIN;
-        }
-    }
-
-
-    public static int parseTextStyle(Document theme) {
-        return parseTextStyle(theme, XPATH_LABEL_FONT_STYLE);
-    }
-
-
-    public static boolean parseShowWidth(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_WIDTH, null);
-        return parseBoolean(show, false);
-    }
-
-
-    public static boolean parseShowLevel(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_SHOW_LEVEL, null);
-        return parseBoolean(show, false);
-    }
-
-    /**
-     * Parses the textorientation, defaults to 'vertical'.
-     * @param theme The theme.
-     */
-    public static String parseTextOrientation(Document theme) {
-        String o = XMLUtils.xpathString(theme, XPATH_TEXT_ORIENTATION, null);
-        if ("true".equals(o)) {
-            return "horizontal";
-        }
-        else {
-            return "vertical";
-        }
-    }
-
-
-    /**
-     * Parses the text background color, defaults to white.
-     * @param theme The theme.
-     */
-    public static Color parseTextBackground(Document theme) {
-        String color = getLabelBackgroundColorString(theme);
-        if (color == null || color.length() == 0) {
-            return Color.WHITE;
-        }
-        return parseRGB(color);
-    }
-
-
-    /**
-     * Parses the attribute whether to show background or not, defaults to
-     * false.
-     * @param theme The theme.
-     */
-    public static boolean parseLabelShowBackground(Document theme) {
-        String show = XMLUtils.xpathString(theme, XPATH_LABEL_SHOW_BACKGROUND, null);
-        return parseBoolean(show, false);
-    }
-
-
-    public static Color parseColor(String colorString) {
-        if (colorString == null || colorString.length() == 0) {
-            return null;
-        }
-        else if (colorString.indexOf("#") == 0) {
-            return parseHexColor(colorString);
-        }
-        else if (colorString.indexOf(",") >= 0) {
-            return parseRGB(colorString);
-        }
-
-        return null;
-    }
-
-
-    /**
-     * Parse a string like "#00CC22" and return the corresponding color.
-     *
-     * @param hex The hex color value.
-     *
-     * @return a Color or null, if <i>hex</i> is empty.
-     */
-    public static Color parseHexColor(String hex) {
-        if (hex == null) {
-            return null;
-        }
-
-        return Color.decode(hex);
-    }
-
-    /**
-     * Parse a string like "103, 100, 0" and return a corresping color.
-     * @param rgbtext Color as string representation, e.g. "255,0,20".
-     * @return Color, null in case of issues.
-     */
-    public static Color parseRGB(String rgbtext) {
-        if (rgbtext == null) {
-            return null;
-        }
-        String rgb[] = rgbtext.split(",");
-        Color c = null;
-        try {
-            c = new Color(
-                    Integer.parseInt(rgb[0].trim()),
-                    Integer.parseInt(rgb[1].trim()),
-                    Integer.parseInt(rgb[2].trim()));
-        }
-        catch (NumberFormatException nfe) {
-            c = null;
-        }
-        return c;
-    }
-
-
-    public static String getLineColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_LINE_COLOR, null);
-    }
-
-
-    /** Get show border as string. */
-    public static String getShowBorderString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_SHOW_BORDER, null);
-    }
-
-
-    /** Get fill color as string. */
-    public static String getFillColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_FILL_COLOR, null);
-    }
-
-
-    public static String getLabelBackgroundColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_LABEL_BGCOLOR, null);
-    }
-
-
-    public static String getBackgroundColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_BACKGROUND_COLOR, null);
-    }
-
-
-    public static String getTextColorString(Document theme) {
-        String textColor = XMLUtils.xpathString(theme, XPATH_LABEL_FONT_COLOR, null);
-        return textColor;
-    }
-
-
-    public static String getSymbol(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_SYMBOL, null);
-    }
-
-
-    public static String getTransparencyString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_TRANSPARENCY, null);
-    }
-
-
-    public static String getAreaTransparencyString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_AREA_TRANSPARENCY, null);
-    }
-
-
-    public static String getShowMinimum(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_SHOW_MINIMUM, null);
-    }
-
-
-    public static String getShowMaximum(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_SHOW_MAXIMUM, null);
-    }
-
-
-    /**
-     * Gets color from color field.
-     * @param theme    the theme document.
-     * @return color.
-     */
-    public static Color parseFillColorField(Document theme) {
-        return parseRGB(getFillColorString(theme));
-    }
-
-
-    public static boolean parseShowBorder(Document theme) {
-        return parseBoolean(getShowBorderString(theme), false);
-    }
-
-
-    public static int parseTransparency(Document theme) {
-        return parseInteger(getTransparencyString(theme), 50);
-    }
-
-
-    /**
-     * Gets color from color field.
-     * @param theme    the theme document.
-     * @return color.
-     */
-    public static Color parseLineColorField(Document theme) {
-        String lineColorStr = getLineColorString(theme);
-        logger.debug("parseLineColorField: lineColorStr = " +
-                (lineColorStr == null ? "null" : lineColorStr));
-        return parseColor(lineColorStr);
-    }
-
-
-    public static Color parseAreaLineColorField(Document theme) {
-        String lineColorStr = getAreaLineColorString(theme);
-        logger.debug("parseLineColorField: lineColorStr = " +
-                (lineColorStr == null ? "null" : lineColorStr));
-        return parseColor(lineColorStr);
-    }
-
-
-    private static String getAreaLineColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_AREA_LINE_COLOR, null);
-    }
-
-
-    public static boolean parseShowMinimum(Document theme) {
-        return parseBoolean(getShowMinimum(theme), false);
-    }
-
-
-    public static boolean parseShowMaximum(Document theme) {
-        return parseBoolean(getShowMaximum(theme), false);
-    }
-
-
-    /**
-     * Creates a MapserverStyle from the given XML theme.
-     * This method uses a start- and endcolor to interpolate a
-     * given number of color classes for the MapserverStyle.
-     * @param theme
-     * @return String representation of the MapserverStyle
-     */
-    public static String createDynamicMapserverStyle(Document theme,
-            float from, float to, float step, CallMeta meta)
-    {
-        MapserverStyle ms = new MapserverStyle();
-
-        String strStartColor = XMLUtils.xpathString(theme, XPATH_WSPLGEN_STARTCOLOR, null);
-        Color startColor = strStartColor != null ? parseColor(strStartColor) : new Color(178, 201, 215);
-        String strEndColor = XMLUtils.xpathString(theme, XPATH_WSPLGEN_ENDCOLOR, null);
-        Color endColor = strEndColor != null? parseColor(strEndColor) : new Color(2, 27, 42);
-
-        to = to != 0 ? to : 9999;
-        step = step != 0 ? step : to;
-
-        int numClasses = (int)((to - from) / step);
-
-        float rd = (endColor.getRed()   - startColor.getRed())   / (float)numClasses;
-        float gd = (endColor.getGreen() - startColor.getGreen()) / (float)numClasses;
-        float bd = (endColor.getBlue()  - startColor.getBlue())  / (float)numClasses;
-
-        if (numClasses > 1) {
-            // Desktop Flys always added a last "and larger class"
-            numClasses += 1;
-        }
-
-        for (int n = 0; n < numClasses; n++) {
-            StringBuilder newColor = new StringBuilder();
-            newColor.append(startColor.getRed()   + Math.round(n * rd));
-            newColor.append(' ');
-            newColor.append(startColor.getGreen() + Math.round(n * gd));
-            newColor.append(' ');
-            newColor.append(startColor.getBlue()  + Math.round(n * bd));
-
-            String expr = createWSPLGENClassExpression(from + n * step, step, n + 1, numClasses);
-            String name = createWSPLGENClassName(from + n * step, step, n + 1, numClasses, meta);
-
-            Clazz c = new Clazz(name);
-            Style s = new Style();
-            s.setColor(newColor.toString());
-            s.setSize(5);
-
-            c.addItem(new Expression("(" + expr + ")"));
-            c.addItem(s);
-
-            ms.addClazz(c);
-        }
-
-        return ms.toString();
-    }
-
-
-    protected static String createWSPLGENClassExpression(float val, float step, int idx, int maxIdx) {
-        if (idx < maxIdx) {
-            return "[DIFF] >= " + val + " AND  [DIFF] < " + (val + step);
-        }
-        else {
-            return "[DIFF] >= " + val;
-        }
-    }
-
-    /**
-     * Creates a class name for the mapfile style that visualizes a floodmap.
-     * The class names are used in the map's legend.
-     *
-     * @param val       Current isobath value.
-     * @param step      Difference between to class values.
-     * @param idx       Current class index that is being processed.
-     * @param maxIdx    Highest class index.
-     * @param meta      Caller meta object used to determine locale.
-     * @return
-     */
-    protected static String createWSPLGENClassName(float val, float step, int idx, int maxIdx, CallMeta meta) {
-        assert meta != null : "CallMeta instance is null";
-
-        if (idx < maxIdx) {
-            return Resources.getMsg(meta, MSG_ISOBATH_CLASS,
-                    new Object[] {val, val + step});
-        }
-        else {
-            return Resources.getMsg(meta, MSG_ISOBATH_LASTCLASS,
-                    new Object[] {val});
-        }
-    }
-
-
-    public static String createMapserverStyle(Document theme) {
-        String symbol    = getSymbol(theme);
-        String backcolor = getLabelBackgroundColorString(theme);
-        String linecolor = getLineColorString(theme);
-        if (linecolor == null || "".equals(linecolor)) {
-            logger.warn("createMapserverStyle: linecolor String is empty");
-            linecolor = "0,128,255";
-        }
-
-        int linewidth = parseLineWidth(theme);
-
-        MapserverStyle ms = new MapserverStyle();
-
-        Clazz c = new Clazz(" ");
-
-        Style s = new Style();
-        s.setOutlineColor(linecolor.replace(",", " "));
-
-        if (backcolor != null && backcolor.length() > 0) {
-            s.setColor(backcolor.replace(",", " "));
-        }
-
-        s.setSize(linewidth);
-        s.setSymbol(symbol);
-        c.addItem(s);
-
-        String textcolor = getTextColorString(theme);
-        int    textsize  = parseTextSize(theme);
-
-        if (textcolor != null && textcolor.length() > 0 && textsize > 0) {
-            Label l = new Label();
-            l.setColor(textcolor.replace(",", " "));
-            l.setSize(textsize);
-            c.addItem(l);
-        }
-
-        ms.addClazz(c);
-
-        return ms.toString();
-    }
-
-
-    public static String getAreaBackgroundColorString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_AREA_BACKGROUND_COLOR, null);
-    }
-
-
-    public static Color parseAreaBackgroundColor(Document theme) {
-        return parseColor(getAreaBackgroundColorString(theme));
-    }
-
-
-    public static int parseAreaTransparency(Document theme) {
-        return parseInteger(getAreaTransparencyString(theme), 50);
-    }
-
-
-    public static boolean parseAreaShowBorder(Document theme) {
-        return parseBoolean(getAreaShowBorderString(theme), false);
-    }
-
-
-    private static String getAreaShowBorderString(Document theme) {
-        return XMLUtils.xpathString(theme, XPATH_AREA_SHOW_BORDER, null);
-    }
-}
-// 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/utils/UniqueDateFormatter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,59 @@
+/* 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.utils;
+
+import java.text.DateFormat;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.log4j.Logger;
+
+public class UniqueDateFormatter {
+
+    private static Logger log = Logger.getLogger(UniqueDateFormatter.class);
+
+    private DateFormat df;
+    private DateFormat lf;
+    private Map<String, int[]> collisions;
+
+    public UniqueDateFormatter(
+        DateFormat df,
+        DateFormat lf,
+        Collection<Date> dates
+    ) {
+        this.df = df;
+        this.lf = lf;
+        collisions = build(dates);
+    }
+
+    private Map<String, int []> build(Collection<Date> dates) {
+        Map<String, int []> collisions = new HashMap<String, int[]>();
+        for (Date d: dates) {
+            String s = df.format(d);
+            int [] count = collisions.get(s);
+            if (count == null) {
+                collisions.put(s, count = new int[1]);
+            }
+            if (++count[0] > 1) {
+                log.debug("date collsion found: " + d);
+            }
+        }
+        return collisions;
+    }
+
+    public String format(Date date) {
+        String s = df.format(date);
+        int [] count = collisions.get(s);
+        return count == null || count[0] < 2
+            ? s
+            : lf.format(date);
+    }
+}
--- a/artifacts/src/main/resources/datacage-sql/org-h2-driver.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/resources/datacage-sql/org-h2-driver.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -17,7 +17,7 @@
 insert.out = INSERT INTO outs (id, artifact_id, name, description, out_type) VALUES (?, ?, ?, ?, ?)
 facet.id.nextval = SELECT NEXTVAL('FACETS_ID_SEQ')
 insert.facet = INSERT INTO facets (id, out_id, name, num, state, description) VALUES (?, ?, ?, ?, ?, ?)
-update.artifact.state = UPDATE artifacts SET state = ? WHERE gid = ?
+update.artifact.state = UPDATE artifacts SET state = ? WHERE id = ?
 
 update.collection.name = UPDATE collections SET name = ? WHERE gid = ?
 delete.artifact.from.collection = DELETE FROM collection_items WHERE collection_id = ? AND artifact_id = ?
--- a/artifacts/src/main/resources/messages.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/resources/messages.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -141,7 +141,7 @@
 floodplain.inactive = Inactiv
 
 outlier.method.grubbs=Grubbs
-outlier.method.std-dev=Standard deviation
+outlier.method.std-dev=Residual standard error
 
 river = River
 calculation_mode = Calculation Mode
@@ -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]
@@ -171,7 +172,7 @@
 chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
 chart.computed.discharge.curve.yaxis.label = W [{0}]
 chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1}
-chart.computed.discharge.curve.gauge = Discharge curve at gauge {0} (km {1})
+chart.computed.discharge.curve.gauge = current DC {0} {1} - {2}
 chart.duration.curve.title = Duration Curve
 chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
 chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days]
@@ -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]
 
@@ -287,7 +288,7 @@
 bedquality.toplayer = 0.0m - 0.3m
 bedquality.sublayer = 0.1m - 0.5m
 facet.bedheight.diff.year = Bedheight Difference {0}
-facet.bedheight.diff.morph = sounding Width {0}
+facet.bedheight.diff.morph = sounding Width
 facet.bedheight.diff.height1 = Original Height Minuend {0}
 facet.bedheight.diff.height2 = Original Height Subtrahend {0}
 facet.bedheight.diff.absolute = Bedheight Difference/Year {0}
@@ -310,7 +311,7 @@
 chart.beddifference.height.yaxis.label = Difference [cm/year]
 chart.beddifference.epoch.title = Bedheight Difference
 chart.beddifference.xaxis.label = {0}-km
-chart.beddifference.yaxis.label.diff = Difference [m]
+chart.beddifference.yaxis.label.diff = Difference [cm]
 chart.beddifference.yaxis.label.height = Absolute Height [{0}]
 chart.beddifference.year.title = Bedheight Difference
 chart.beddifference.yaxis.label.morph = Width [m]
@@ -391,6 +392,7 @@
 export.sedimentload_ls.csv.header.sand = sand
 export.sedimentload_ls.csv.header.suspsand = susp. sand
 export.sedimentload_ls.csv.header.suspsediment = susp. sediment
+export.sedimentload_ls.csv.header.suspsandbb = susp. sand (BB)
 export.sedimentload_ls.csv.header.total = total
 export.sqrelation.csv.header.parameter = Parameter
 export.sqrelation.csv.header.station = Station
@@ -403,9 +405,10 @@
 export.sqrelation.csv.header.coeff.r = r^2
 export.sqrelation.csv.header.n.total = n total
 export.sqrelation.csv.header.n.outliers = n outliers
-export.sqrelation.csv.header.c.duan = C (DUAN)
-export.sqrelation.csv.header.c.ferguson = C (FERGUSON)
-export.sqrelation.csv.header.variance = Standard variance
+export.sqrelation.csv.header.c.duan = C (Duan)
+export.sqrelation.csv.header.c.ferguson = C (Ferguson)
+export.sqrelation.csv.header.sd = Standard error
+export.sqrelation.csv.header.qmax = Q max,measured
 export.sqrelation.pdf.file = /jasper/sqrelation_en.jasper
 export.sqrelation.pdf.mode = Load Discharge Relation
 export.minfo.bedquality.km = km
@@ -424,7 +427,7 @@
 
 floodmap.wmsbackground = Background Map
 floodmap.riveraxis = River Axis
-floodmap.uesk = Floodmap: {0}-km {1,number,####} - {2,number,####}
+floodmap.uesk = Floodmap: {0}-km {1,number,####} - {2,number,####} - {3}
 floodmap.barriers = Digitized Objects
 floodmap.kms = Kilometrage
 floodmap.qps = Crosssection Tracks
--- a/artifacts/src/main/resources/messages_de.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/resources/messages_de.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -142,7 +142,7 @@
 floodplain.inactive = Inaktiv
 
 outlier.method.grubbs=Grubbs
-outlier.method.std-dev=Standardabweichung
+outlier.method.std-dev=Standardfehler der Residuen
 
 river = Fluss
 calculation_mode = Berechnungsart
@@ -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]
@@ -171,7 +172,7 @@
 chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
 chart.computed.discharge.curve.yaxis.label = W [{0}]
 chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1}
-chart.computed.discharge.curve.gauge = Abflusskurve an Pegel {0} (km {1})
+chart.computed.discharge.curve.gauge = aktuelle AT {0} {1} - {2}
 chart.duration.curve.title = Dauerlinie
 chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
 chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage]
@@ -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]
 
@@ -287,7 +288,7 @@
 bedquality.toplayer = 0,0m - 0,3m
 bedquality.sublayer = 0,1m - 0,5m
 facet.bedheight.diff.year = Sohlh\u00f6hendifferenz {0}
-facet.bedheight.diff.morph = gepeilte Breite {0}
+facet.bedheight.diff.morph = gepeilte Breite
 facet.bedheight.diff.height1 = H\u00f6he Minuend {0}
 facet.bedheight.diff.height2 = H\u00f6he Subtrahend {0}
 facet.bedheight.diff.absolute = Sohlh\u00f6hendifferenz/Jahr {0}
@@ -310,7 +311,7 @@
 chart.beddifference.height.yaxis.label = Differenz [cm/Jahr]
 chart.beddifference.epoch.title = Sohlh\u00f6hendifferenz
 chart.beddifference.xaxis.label = {0}-km
-chart.beddifference.yaxis.label.diff = Differenz [m]
+chart.beddifference.yaxis.label.diff = Differenz [cm]
 chart.beddifference.yaxis.label.height = Absolute H\u00f6he [m]
 chart.beddifference.year.title = Sohlh\u00f6hendifferenz
 chart.beddifference.yaxis.label.morph = Breite [m]
@@ -386,12 +387,13 @@
 export.bedheight_middle.csv.header.locations = Streckendaten
 export.sedimentload_ls.csv.header.km = km
 export.sedimentload_ls.csv.header.year = Jahr
-export.sedimentload_ls.csv.header.coarse = Grob
-export.sedimentload_ls.csv.header.finemiddle = Fein
+export.sedimentload_ls.csv.header.coarse = Kies(g)
+export.sedimentload_ls.csv.header.finemiddle = Kies(f+m)
 export.sedimentload_ls.csv.header.sand = Sand
-export.sedimentload_ls.csv.header.suspsand = susp. Sand
-export.sedimentload_ls.csv.header.suspsediment = susp. Sediment
-export.sedimentload_ls.csv.header.total = Total
+export.sedimentload_ls.csv.header.suspsand = susp.Sand
+export.sedimentload_ls.csv.header.suspsandbb = susp.Sand(BB)
+export.sedimentload_ls.csv.header.suspsediment = Schwebst.
+export.sedimentload_ls.csv.header.total = Gesamt
 export.sqrelation.csv.header.parameter = Parameter
 export.sqrelation.csv.header.station = Station
 export.sqrelation.csv.header.km = Fluss-Km
@@ -401,11 +403,12 @@
 export.sqrelation.csv.header.coeff.b = b
 export.sqrelation.csv.header.coeff.q = Q
 export.sqrelation.csv.header.coeff.r = r^2
-export.sqrelation.csv.header.n.total = n gesamt
+export.sqrelation.csv.header.n.total = n Gesamt
 export.sqrelation.csv.header.n.outliers = n Ausrei\u00dfer
-export.sqrelation.csv.header.c.duan = C (DUAN)
-export.sqrelation.csv.header.c.ferguson = C (FERGUSON)
-export.sqrelation.csv.header.variance = Standardabweichung
+export.sqrelation.csv.header.c.duan = C (Duan)
+export.sqrelation.csv.header.c.ferguson = C (Ferguson)
+export.sqrelation.csv.header.sd = Standardfehler
+export.sqrelation.csv.header.qmax = Q max,gemessen
 export.sqrelation.pdf.file = /jasper/sqrelation.jasper
 export.sqrelation.pdf.mode = Transport-Abfluss Beziehung
 export.minfo.bedquality.km = km
@@ -424,7 +427,7 @@
 
 floodmap.wmsbackground = Hintergrundkarte
 floodmap.riveraxis = Flussachse
-floodmap.uesk = \u00dcSG: {0}-km {1,number,####} - {2,number,####}
+floodmap.uesk = \u00dcSG: {0}-km {1,number,####} - {2,number,####} - {3}
 floodmap.barriers = Digitalisierte Objekte
 floodmap.kms = Kilometrierung
 floodmap.qps = Querprofilspuren
--- a/artifacts/src/main/resources/messages_de_DE.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/resources/messages_de_DE.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -141,7 +141,7 @@
 floodplain.inactive = Inaktiv
 
 outlier.method.grubbs=Grubbs
-outlier.method.std-dev=Standardabweichung
+outlier.method.std-dev=Standardfehler der Residuen
 
 river = Fluss
 calculation_mode = Berechnungsart
@@ -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]
@@ -170,7 +171,7 @@
 chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
 chart.computed.discharge.curve.yaxis.label = W [{0}]
 chart.computed.discharge.curve.curve.label = Abflusskurve {0} km {1}
-chart.computed.discharge.curve.gauge = Abflusskurve an Pegel {0} (km {1})
+chart.computed.discharge.curve.gauge = aktuelle AT {0} {1} - {2}
 chart.duration.curve.title = Dauerlinie
 chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
 chart.duration.curve.xaxis.label = Unterschreitungsdauer [Tage]
@@ -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]
 
@@ -286,7 +287,7 @@
 bedquality.sublayer = 0,1m - 0,5m
 facet.bedheight.diff.year = Sohlh\u00f6hendifferenz {0}
 facet.bedheight.diff.year.raw = Sohlh\u00f6hendifferenz {0} (Rohdaten)
-facet.bedheight.diff.morph = gepeilte Breite {0}
+facet.bedheight.diff.morph = gepeilte Breite
 facet.bedheight.diff.height1 = H\u00f6he Minuend {0}
 facet.bedheight.diff.height2 = H\u00f6he Subtrahend {0}
 facet.bedheight.diff.absolute = Sohlh\u00f6hendifferenz/Jahr {0}
@@ -308,7 +309,7 @@
 chart.beddifference.height.yaxis.label = Differenz [cm/Jahr]
 chart.beddifference.epoch.title = Sohlh\u00f6hendifferenz
 chart.beddifference.xaxis.label = {0}-km
-chart.beddifference.yaxis.label.diff = Differenz [m]
+chart.beddifference.yaxis.label.diff = Differenz [cm]
 chart.beddifference.yaxis.label.height = Absolute H\u00f6he [{0}]
 chart.beddifference.year.title = Sohlh\u00f6hendifferenz
 chart.beddifference.yaxis.label.morph = Breite [m]
@@ -383,12 +384,13 @@
 export.bedheight_middle.csv.header.locations = Streckendaten
 export.sedimentload_ls.csv.header.km = km
 export.sedimentload_ls.csv.header.year = Jahr
-export.sedimentload_ls.csv.header.coarse = Grob
-export.sedimentload_ls.csv.header.finemiddle = Fein
+export.sedimentload_ls.csv.header.coarse = Kies(g)
+export.sedimentload_ls.csv.header.finemiddle = Kies(f+m)
 export.sedimentload_ls.csv.header.sand = Sand
-export.sedimentload_ls.csv.header.suspsand = susp. Sand
-export.sedimentload_ls.csv.header.suspsediment = susp. Sediment
-export.sedimentload_ls.csv.header.total = Total
+export.sedimentload_ls.csv.header.suspsand = susp.Sand
+export.sedimentload_ls.csv.header.suspsandbb = susp.Sand(BB)
+export.sedimentload_ls.csv.header.suspsediment = Schwebst.
+export.sedimentload_ls.csv.header.total = Gesamt
 export.sqrelation.csv.header.parameter = Parameter
 export.sqrelation.csv.header.station = Station
 export.sqrelation.csv.header.km = Fluss-Km
@@ -398,11 +400,12 @@
 export.sqrelation.csv.header.coeff.b = b
 export.sqrelation.csv.header.coeff.q = Q
 export.sqrelation.csv.header.coeff.r = r^2
-export.sqrelation.csv.header.n.total = n gesamt
+export.sqrelation.csv.header.n.total = n Gesamt
 export.sqrelation.csv.header.n.outliers = n Ausrei\u00dfer
-export.sqrelation.csv.header.c.duan = C (DUAN)
-export.sqrelation.csv.header.c.ferguson = C (FERGUSON)
-export.sqrelation.csv.header.variance = Standardabweichung
+export.sqrelation.csv.header.c.duan = C (Duan)
+export.sqrelation.csv.header.c.ferguson = C (Ferguson)
+export.sqrelation.csv.header.sd = Standardfehler
+export.sqrelation.csv.header.qmax = Q max,gemessen
 export.sqrelation.pdf.file = /jasper/sqrelation.jasper
 export.sqrelation.pdf.mode = Transport-Abfluss Beziehung
 export.minfo.bedquality.km = km
@@ -421,7 +424,7 @@
 
 floodmap.wmsbackground = Hintergrundkarte
 floodmap.riveraxis = Flussachse
-floodmap.uesk = \u00dcSG: {0}-km {1,number,####} - {2,number,####}
+floodmap.uesk = \u00dcSG: {0}-km {1,number,####} - {2,number,####} - {3}
 floodmap.barriers = Digitalisierte Objekte
 floodmap.kms = Kilometrierung
 floodmap.qps = Querprofilspuren
--- a/artifacts/src/main/resources/messages_en.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/artifacts/src/main/resources/messages_en.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -141,7 +141,7 @@
 floodplain.inactive = Inactiv
 
 outlier.method.grubbs=Grubbs
-outlier.method.std-dev=Standard deviation
+outlier.method.std-dev=Residual standard error
 
 river = River
 calculation_mode = Calculation Mode
@@ -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]
@@ -171,6 +172,7 @@
 chart.computed.discharge.curve.subtitle = {0}-km: {1,number,#.###}
 chart.computed.discharge.curve.yaxis.label = W [{0}]
 chart.computed.discharge.curve.curve.label = Discharge Curve {0} km {1}
+chart.computed.discharge.curve.gauge = current DC {0} {1} - {2}
 chart.duration.curve.title = Duration Curve
 chart.duration.curve.subtitle = {0}-km: {1,number,#.###}
 chart.duration.curve.xaxis.label = Duration of Non-Exceedence [Days]
@@ -209,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]
 
@@ -290,7 +292,7 @@
 bedquality.toplayer = 0.0m - 0.3m
 bedquality.sublayer = 0.1m - 0.5m
 facet.bedheight.diff.year = Bedheight Difference {0}
-facet.bedheight.diff.morph = sounding Width {0}
+facet.bedheight.diff.morph = sounding Width
 facet.bedheight.diff.height1 = Original Height Minuend {0}
 facet.bedheight.diff.height2 = Original Height Subtrahend {0}
 facet.bedheight.diff.absolute = Bedheight Difference/Year {0}
@@ -313,7 +315,7 @@
 chart.beddifference.height.yaxis.label = Difference [cm/year]
 chart.beddifference.epoch.title = Bedheight Difference
 chart.beddifference.xaxis.label = {0}-km
-chart.beddifference.yaxis.label.diff = Difference [m]
+chart.beddifference.yaxis.label.diff = Difference [cm]
 chart.beddifference.yaxis.label.height = Absolute Height [m]
 chart.beddifference.year.title = Bedheight Difference
 chart.beddifference.yaxis.label.morph = Width [m]
@@ -391,6 +393,7 @@
 export.sedimentload_ls.csv.header.coarse = coarse
 export.sedimentload_ls.csv.header.finemiddle = finemiddle
 export.sedimentload_ls.csv.header.sand = sand
+export.sedimentload_ls.csv.header.suspsandbb = susp. sand (BB)
 export.sedimentload_ls.csv.header.suspsand = susp. sand
 export.sedimentload_ls.csv.header.suspsediment = susp. sediment
 export.sedimentload_ls.csv.header.total = total
@@ -405,9 +408,10 @@
 export.sqrelation.csv.header.coeff.r = r^2
 export.sqrelation.csv.header.n.total = n total
 export.sqrelation.csv.header.n.outliers = n outliers
-export.sqrelation.csv.header.c.duan = C (DUAN)
-export.sqrelation.csv.header.c.ferguson = C (FERGUSON)
-export.sqrelation.csv.header.variance = Standard variance
+export.sqrelation.csv.header.c.duan = C (Duan)
+export.sqrelation.csv.header.c.ferguson = C (Ferguson)
+export.sqrelation.csv.header.sd = Standard error
+export.sqrelation.csv.header.qmax = Q max,measured
 export.sqrelation.pdf.file = /jasper/sqrelation_en.jasper
 export.sqrelation.pdf.mode = Load Discharge Relation
 export.minfo.bedquality.km = km
@@ -426,7 +430,7 @@
 
 floodmap.wmsbackground = Background Map
 floodmap.riveraxis = River Axis
-floodmap.uesk = Floodmap: {0}-km {1,number,####} - {2,number,####}
+floodmap.uesk = Floodmap: {0}-km {1,number,####} - {2,number,####} - {3}
 floodmap.barriers = Digitized Objects
 floodmap.kms = Kilometrage
 floodmap.qps = Crosssection Tracks
--- a/backend/src/main/java/org/dive4elements/river/importer/ImportRiver.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/importer/ImportRiver.java	Fri Sep 27 17:36:50 2013 +0200
@@ -941,6 +941,7 @@
         WstParser wstParser = new WstParser();
         wstParser.parse(wstFile);
         wst = wstParser.getWst();
+        wst.setKmUp(wst.guessWaterLevelIncreasing());
     }
 
     public void parseGauges() throws IOException {
@@ -1222,9 +1223,15 @@
     }
 
     public void storeWst() {
-        if (!Config.INSTANCE.skipWst()) {
+        if (wst != null && !Config.INSTANCE.skipWst()) {
             River river = getPeer();
             wst.storeDependencies(river);
+
+            // The flow direction of the main wst and the corresponding
+            // waterlevels determine if the river is 'km_up'.
+            Session session = ImporterSession.getInstance().getDatabaseSession();
+            river.setKmUp(wst.getKmUp());
+            session.save(river);
         }
     }
 
--- a/backend/src/main/java/org/dive4elements/river/importer/ImportWst.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/importer/ImportWst.java	Fri Sep 27 17:36:50 2013 +0200
@@ -46,6 +46,8 @@
 
     protected ImportWstColumnFactory columnFactory;
 
+    protected boolean kmUp;
+
     /** Wst as in db. */
     protected Wst peer;
 
@@ -80,6 +82,13 @@
         this.kind = kind;
     }
 
+    public boolean getKmUp() {
+        return kmUp;
+    }
+
+    public void setKmUp(boolean kmUp) {
+        this.kmUp = kmUp;
+    }
 
     public void setDescription(String description) {
         this.description = description;
@@ -120,7 +129,7 @@
     public void storeDependencies(River river) {
 
         log.info("store '" + description + "'");
-        Wst wst = getPeer(river);
+        getPeer(river);
 
         for (ImportWstColumn column: columns) {
             column.storeDependencies(river);
@@ -130,6 +139,19 @@
         session.flush();
     }
 
+    public boolean guessWaterLevelIncreasing() {
+        int up = 0;
+        for (ImportWstColumn column: columns) {
+            if (column.guessWaterLevelIncreasing()) ++up;
+        }
+        return up > columns.size() - up;
+    }
+
+    public void fixRangesOrder() {
+        for (ImportWstColumn column: columns) {
+            column.fixRangesOrder();
+        }
+    }
 
     /** Get corresponding mapped wst (from database). */
     public Wst getPeer(River river) {
--- a/backend/src/main/java/org/dive4elements/river/importer/ImportWstColumn.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/importer/ImportWstColumn.java	Fri Sep 27 17:36:50 2013 +0200
@@ -18,6 +18,7 @@
 
 import java.util.List;
 import java.util.ArrayList;
+import java.util.Random;
 
 import java.math.BigDecimal;
 
@@ -129,7 +130,7 @@
 
     public void storeDependencies(River river) {
         log.info("store column '" + name + "'");
-        WstColumn column = getPeer(river);
+        getPeer(river);
 
         for (ImportWstColumnQRange columnQRange: columnQRanges) {
             columnQRange.getPeer(river);
@@ -148,6 +149,36 @@
         this.timeInterval = timeInterval;
     }
 
+    public boolean guessWaterLevelIncreasing() {
+
+        int N = columnValues.size();
+
+        if (N < 2) {
+            return true;
+        }
+
+        Random r = new Random();
+        int up = 0;
+
+        int S = N < 50 ? N : (int)(0.1f * N)+1;
+        for (int s = 0; s < S; ++s) {
+            int i1, i2;
+            do {
+                i1 = r.nextInt(N-1);
+                i2 = r.nextInt(N-1);
+            } while (i1 == i2);
+            ImportWstColumnValue b = columnValues.get(i1);
+            ImportWstColumnValue a = columnValues.get(i2);
+            if (b.getPosition().compareTo(a.getPosition()) < 0) {
+                ImportWstColumnValue t = a; a = b; b = t;
+            }
+
+            if (a.getW().compareTo(b.getW()) < 0) ++up;
+        }
+
+        return up > S - up;
+    }
+
     /** Get corresponding mapped wst-column (from database). */
     public WstColumn getPeer(River river) {
         if (peer == null) {
@@ -169,7 +200,7 @@
 
             List<WstColumn> columns = query.list();
             if (columns.isEmpty()) {
-		log.debug("source: " + source);
+                log.debug("source: " + source);
                 peer = new WstColumn(
                     w, name, description, source, position, ti);
                 session.save(peer);
--- a/backend/src/main/java/org/dive4elements/river/model/CrossSection.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/CrossSection.java	Fri Sep 27 17:36:50 2013 +0200
@@ -69,7 +69,7 @@
         "JOIN cross_sections cs ON cs_ranges.cross_section_id = cs.id " +
         "LEFT OUTER JOIN time_intervals ON cs.time_interval_id = time_intervals.id " +
         "WHERE :km BETWEEN minkm AND maxkm " +
-        "ORDER BY stop_time desc, start_time asc, :km - minkm";
+        "ORDER BY stop_time desc, start_time desc, :km - minkm";
     // Order by time interval missing.
 
     private Integer                id;
@@ -241,11 +241,19 @@
 
         List<Integer> results = sqlQuery.list();
 
-        for (Integer result: results) {
+        if (results.size() >= 1) {
+            Integer result = results.get(0);
             if (result == getId()) {
                 return true;
             }
         }
+        else {
+            logger.warn("No CS found that could be master.");
+        }
+
+        // TODO If there is none, might need a fallback.
+        // Formerly this was the most current CS (issue1157).
+
         return false;
     }
 }
--- a/backend/src/main/java/org/dive4elements/river/model/Gauge.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/Gauge.java	Fri Sep 27 17:36:50 2013 +0200
@@ -223,7 +223,6 @@
         this.mainValues = mainValues;
     }
 
-
     public static Gauge getGaugeByOfficialNumber(long number) {
         Session session = SessionHolder.HOLDER.get();
 
@@ -237,6 +236,21 @@
         return results.isEmpty() ? null : results.get(0);
     }
 
+    public static Gauge getGaugeByOfficialNumber(long number, String river_name) {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from Gauge as gau " +
+            "where gau.officialNumber=:number and gau.river.name=:river_name");
+
+        query.setParameter("number", number);
+        query.setParameter("river_name", river_name);
+
+        List<Gauge> results = query.list();
+
+        return results.isEmpty() ? null : results.get(0);
+    }
+
 
     public DischargeTable fetchMasterDischargeTable() {
         for (DischargeTable dt: dischargeTables) {
--- a/backend/src/main/java/org/dive4elements/river/model/MeasurementStation.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/model/MeasurementStation.java	Fri Sep 27 17:36:50 2013 +0200
@@ -8,6 +8,8 @@
 
 package org.dive4elements.river.model;
 
+import java.util.List;
+
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.GeneratedValue;
@@ -18,6 +20,10 @@
 import javax.persistence.SequenceGenerator;
 import javax.persistence.Table;
 
+import org.hibernate.Session;
+import org.hibernate.Query;
+
+import org.dive4elements.river.backend.SessionHolder;
 
 @Entity
 @Table(name = "measurement_station")
@@ -176,4 +182,17 @@
     public void setDescription(String description) {
         this.description = description;
     }
+
+    public static List<MeasurementStation> getStationsAtKM(String river, Double river_km)
+    {
+        Session session = SessionHolder.HOLDER.get();
+
+        Query query = session.createQuery(
+            "from MeasurementStation as ms " +
+            "where ms.river.name = :river_name and ms.station = :river_km");
+        query.setParameter("river_name", river);
+        query.setParameter("river_km", river_km);
+
+        return query.list();
+    }
 }
--- a/backend/src/main/java/org/dive4elements/river/utils/EpsilonComparator.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/backend/src/main/java/org/dive4elements/river/utils/EpsilonComparator.java	Fri Sep 27 17:36:50 2013 +0200
@@ -9,9 +9,10 @@
 package org.dive4elements.river.utils;
 
 import java.util.Comparator;
+import java.io.Serializable;
 
 /** Comparator with some tolerance (epsilon). */
-public class EpsilonComparator implements Comparator<Double>
+public class EpsilonComparator implements Comparator<Double>, Serializable
 {
     public static final double EPSILON = 1e-4;
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/contrib/make_flys_release/issue_overview.sh	Fri Sep 27 17:36:50 2013 +0200
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# Use this on an exported query from roundup
+# Known issue: Tiles with more then three , in them fail.
+# Yes (ah) I do not know how to correctly handle the csv quoting...
+
+echo "-------------"
+echo "Unter anderem wurden folgende issues bearbeitet und können getestet werden:"
+
+sed 's/"\(.*\),\(.*\)"/"\1§\2"/g' "$1" | \
+sed 's/"\(.*\),\(.*\)"/"\1§\2"/g' | \
+sed 's/"\(.*\),\(.*\)"/\1§\2/g' | \
+gawk -F, '$5 > 5 {
+print "- flys/issue"$2 " ("$1")"
+print "[https://roundup-intern.intevation.de/flys/issue"$2"]"
+print ""
+}' | sed 's/§/,/g'
+
+
+echo "-------------"
+echo "Desweiteren gab es fortschritte in folgenden Issues:"
+echo ""
+sed 's/"\(.*\),\(.*\)"/"\1§\2"/g' "$1" | \
+sed 's/"\(.*\),\(.*\)"/"\1§\2"/g' | \
+sed 's/"\(.*\),\(.*\)"/\1§\2/g' | \
+gawk -F, '$5 <= 5 && $5 > 0 {
+print "- flys/issue"$2 " ("$1")"
+print "[https://roundup-intern.intevation.de/flys/issue"$2"]"
+print ""
+}' | sed 's/§/,/g'
+
--- a/contrib/make_flys_release/make_release.sh	Fri Sep 13 18:29:01 2013 +0200
+++ b/contrib/make_flys_release/make_release.sh	Fri Sep 27 17:36:50 2013 +0200
@@ -86,7 +86,7 @@
 BACKENDUSER=${BACKENDUSER:-flys_dami}
 BACKENDPASS=${BACKENDPASS:-flys_dami}
 INITSQLS=${INITSQLS:-}
-DGM_PATH=${DGM_PATH:-/opt/river/gewaesser}
+DGM_PATH=${DGM_PATH:-/vol1/projects/Geospatial/flys-3.0/testdaten/dami_dgms/}
 LOG_DIR=/var/log/flys
 
 OPTS=`getopt -o ?w:,t,o \
--- a/etl/src/main/java/org/dive4elements/river/etl/aft/River.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/etl/src/main/java/org/dive4elements/river/etl/aft/River.java	Fri Sep 27 17:36:50 2013 +0200
@@ -15,9 +15,11 @@
 import java.sql.SQLException;
 
 import java.util.ArrayList;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TreeMap;
 
 import org.apache.log4j.Logger;
 
@@ -80,6 +82,7 @@
         ConnectedStatements aftStatements  = context.getAftStatements();
 
         String riverName = getName();
+        String lowerRiverName = riverName.toLowerCase();
 
         Map<Long, DIPSGauge> aftDIPSGauges = new HashMap<Long, DIPSGauge>();
 
@@ -113,7 +116,7 @@
                     continue;
                 }
                 String gaugeRiver = dipsGauge.getRiverName();
-                if (!gaugeRiver.equalsIgnoreCase(riverName)) {
+                if (!lowerRiverName.contains(gaugeRiver.toLowerCase())) {
                     log.warn(
                         "DIPS: MESSSTELLE '" + name +
                         "' is assigned to river '" + gaugeRiver +
@@ -136,11 +139,25 @@
             .clearParameters()
             .setInt("river_id", id1).executeQuery();
 
+        TreeMap<Double, String> station2gaugeName = new TreeMap<Double, String>(
+            new Comparator<Double>() {
+                @Override
+                public int compare(Double a, Double b) {
+                    double diff = a - b;
+                    if (diff < -0.0001) return -1;
+                    if (diff >  0.0001) return +1;
+                    return 0;
+                }
+            });
+
         try {
             while (gaugesRs.next()) {
                 int gaugeId = gaugesRs.getInt("id");
                 String name = gaugesRs.getString("name");
                 long   number = gaugesRs.getLong("official_number");
+                double station = gaugesRs.getDouble("station");
+                station2gaugeName.put(station, name);
+
                 if (gaugesRs.wasNull()) {
                     log.warn("FLYS: Gauge '" + name +
                         "' has no official number. Ignored.");
@@ -163,7 +180,8 @@
             gaugesRs.close();
         }
 
-        boolean modified = createGauges(context, aftDIPSGauges);
+        boolean modified = createGauges(
+            context, aftDIPSGauges, station2gaugeName);
 
         modified |= updateGauges(context, updateGauges);
 
@@ -364,7 +382,8 @@
 
     protected boolean createGauges(
         SyncContext          context,
-        Map<Long, DIPSGauge> gauges
+        Map<Long, DIPSGauge> gauges,
+        Map<Double, String>  station2gaugeName
     )
     throws SQLException
     {
@@ -385,6 +404,14 @@
             log.info("Gauge '" + gauge.getAftName() +
                 "' not in FLYS but in AFT/DIPS. -> Create");
 
+            String flysGaugeName = station2gaugeName.get(gauge.getStation());
+            if (flysGaugeName != null) {
+                log.warn("FLYS: AFT gauge " + gauge.getName() +
+                    " has same station as FLYS gauge " + flysGaugeName +
+                    " -> ignored.");
+                continue;
+            }
+
             if (!gauge.hasDatums()) {
                 log.warn("DIPS: Gauge '" +
                     gauge.getAftName() + "' has no datum. Ignored.");
--- a/etl/src/main/java/org/dive4elements/river/etl/aft/Rivers.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/etl/src/main/java/org/dive4elements/river/etl/aft/Rivers.java	Fri Sep 27 17:36:50 2013 +0200
@@ -14,9 +14,7 @@
 import java.sql.SQLException;
 
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 
 import org.apache.log4j.Logger;
 
@@ -27,6 +25,24 @@
     public Rivers() {
     }
 
+    private static List<River> findFLYSRivers(
+        List<River> flysRivers,
+        String      needle
+    ) {
+        List<River> rivers = new ArrayList<River>();
+
+        needle = needle.toLowerCase();
+
+        for (River river: flysRivers) {
+            String name = river.getName().toLowerCase();
+            if (name.contains(needle)) {
+                rivers.add(river);
+            }
+        }
+
+        return rivers;
+    }
+
     public boolean sync(SyncContext context) throws SQLException {
 
         log.info("sync: rivers");
@@ -34,7 +50,7 @@
         ConnectedStatements flysStatements = context.getFlysStatements();
         ConnectedStatements aftStatements  = context.getAftStatements();
 
-        Map<String, River> flysRivers = new HashMap<String, River>();
+        List<River> flysRivers = new ArrayList<River>();
 
         ResultSet flysRs = flysStatements
             .getStatement("select.rivers").executeQuery();
@@ -45,7 +61,7 @@
                 String name = flysRs.getString("name");
                 double from = flysRs.getDouble("min_km");
                 double to   = flysRs.getDouble("max_km");
-                flysRivers.put(name.toLowerCase(), new River(id, name, from, to));
+                flysRivers.add(new River(id, name, from, to));
             }
         }
         finally {
@@ -60,9 +76,8 @@
         try {
             while (aftRs.next()) {
                 String name = aftRs.getString("NAME");
-                River river = flysRivers.get(name.toLowerCase());
-                if (river != null) {
-                    int id2 = aftRs.getInt("GEWAESSER_NR");
+                int    id2  = aftRs.getInt("GEWAESSER_NR");
+                for (River river: findFLYSRivers(flysRivers, name)) {
                     river.setId2(id2);
                     commonRivers.add(river);
                 }
@@ -72,8 +87,15 @@
             aftRs.close();
         }
 
+        boolean modified = false;
 
-        boolean modified = false;
+        if (log.isDebugEnabled()) {
+            log.debug("Rivers found in FLYS and AFT:");
+            for (River river: commonRivers) {
+                log.debug("  " + river.getName());
+            }
+            log.debug("---");
+        }
 
         for (River river: commonRivers) {
             modified |= river.sync(context);
--- a/etl/src/main/java/org/dive4elements/river/etl/aft/SyncContext.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/etl/src/main/java/org/dive4elements/river/etl/aft/SyncContext.java	Fri Sep 27 17:36:50 2013 +0200
@@ -119,6 +119,10 @@
             double station = gauge.getStation();
             if (station >= from && station <= to) {
                 result.put(entry.getKey(), gauge);
+            } else {
+                log.warn("DIPS: Skipping Gauge: " + gauge.getName() +
+                        " because it is at Station: " + station +
+                        " and the river is limited to: " + from + " - " + to);
             }
         }
 
--- a/etl/src/main/resources/sql/flys-common.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/etl/src/main/resources/sql/flys-common.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -7,7 +7,7 @@
     WHERE w.kind = 0 \
     GROUP BY r.id, r.name
 select.gauges = \
-    SELECT id, name, official_number \
+    SELECT id, name, official_number, station \
     FROM gauges \
     WHERE river_id = :river_id
 next.gauge.id = \
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.java	Fri Sep 27 17:36:50 2013 +0200
@@ -934,6 +934,18 @@
 
     String delta_w();
 
+    String delta_w_cm();
+
+    String delta_w_cma();
+
+    String wlevel();
+
+    String yields();
+
+    String years();
+
+    String epochs();
+
     // Capabilities Information Panel
 
     String addwmsInputTitle();
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -369,7 +369,7 @@
 error_gc_doc_not_valid = Capabilities document is not valid.
 error_malformed_url = The URL you have entered is not valid.
 error_no_dgm_selected = No DEM selected.
-error_invalid_dgm_selected = You have selected an invalid DEM.
+error_invalid_dgm_selected = An error occured while selecting the DEM.
 error_bad_dgm_range = You have selected a DEM with an invalid range.
 error_bad_dgm_river = You have selected a DEM for a wrong river.
 error_dialog_not_valid = One or more values are not valid.
@@ -496,7 +496,13 @@
 measurements = Measurements
 floodmarks = Flood Marks
 pegel_had_measurement_points = HAD Discharge-Measurement points
-delta_w = Delta W
+delta_w = Waterleveldifferences
+delta_w_cm = cm
+delta_w_cma = cm/a
+wlevel = Waterlevels
+yields = sedimentyields
+years = years
+epochs = epochs
 # No translation for the pegelonline wms service layer names.
 gauge_points = Pegelpunkte (WSV)
 gauge_level = Aktueller Wasserstand (WSV)
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_de.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -369,7 +369,7 @@
 error_gc_doc_not_valid = Das Capabilities Dokument ist nicht valide.
 error_malformed_url = Die eingegebene URL ist ung\u00fcltig.
 error_no_dgm_selected = Sie haben kein DGM gew\u00e4hlt.
-error_invalid_dgm_selected = Sie haben ein falsches DGM gew\u00e4hlt.
+error_invalid_dgm_selected = Bei der Auswahl des DGMs ist ein Fehler aufgetreten.
 error_bad_dgm_range = Das gew\u00e4hlte DGM passt nicht zur gew\u00e4hlten Berechnungsstrecke.
 error_bad_dgm_river = Das gew\u00e4hlte DGM passt nicht zum gew\u00e4hlten Fluss.
 error_dialog_not_valid = Eine oder mehrere Daten sind nicht korrekt.
@@ -501,7 +501,13 @@
 measurements = Messungen
 floodmarks = HW-Marken
 pegel_had_measurement_points = HAD Abflussmessstellen (WMS)
-delta_w = W Differenzen
+delta_w = Wasserspiegeldifferenzen
+delta_w_cm = cm
+delta_w_cma = cm/a
+wlevel = Wasserspiegellagen
+yields = Frachten
+years = Einzeljahre
+epochs = Epochen
 gauge_points = Pegelmessstelle (WMS)
 gauge_level = Wasserstand (WMS)
 gauge_names = Pegelname (WMS)
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_en.properties	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/FLYSConstants_en.properties	Fri Sep 27 17:36:50 2013 +0200
@@ -367,7 +367,7 @@
 error_gc_doc_not_valid = Capabilities document is not valid.
 error_malformed_url = The URL you have entered is not valid.
 error_no_dgm_selected = No DEM selected.
-error_invalid_dgm_selected = You have selected an invalid DEM.
+error_invalid_dgm_selected = An error occured while selecting the DEM.
 error_bad_dgm_range = You have selected a DEM with an invalid range.
 error_bad_dgm_river = You have selected a DEM for a wrong river.
 error_dialog_not_valid = One or more values are not valid.
@@ -480,7 +480,13 @@
 bedheights = Bedheights
 morph_width = morphologic Width
 datacage = Datacage
-delta_w = Delta W
+delta_w = Waterleveldifferences
+delta_w_cm = cm
+delta_w_cma = cm/a
+wlevel = Waterlevels
+yields = sedimentyields
+years = years
+epochs = epochs
 
 startcolor = Colorrange start color
 endcolor = Colorrange end color
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DischargeInfoService.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DischargeInfoService.java	Fri Sep 27 17:36:50 2013 +0200
@@ -28,7 +28,8 @@
      */
     DischargeInfoObject[] getDischargeInfo(
         String locale,
-        long gauge)
+        long gauge,
+        String river)
     throws ServerException;
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DischargeInfoServiceAsync.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/services/DischargeInfoServiceAsync.java	Fri Sep 27 17:36:50 2013 +0200
@@ -21,6 +21,7 @@
     void getDischargeInfo(
         String locale,
         long gauge,
+        String river,
         AsyncCallback<DischargeInfoObject[]> cb);
 }
 // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanel.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DistancePanel.java	Fri Sep 27 17:36:50 2013 +0200
@@ -57,6 +57,7 @@
 import java.util.List;
 
 
+/** Panel to allow input of distance for calculation range. */
 public class DistancePanel extends AbstractUIProvider implements BlurHandler, FilterHandler
 {
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ExportPanel.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ExportPanel.java	Fri Sep 27 17:36:50 2013 +0200
@@ -120,6 +120,7 @@
             imgUrl += MSG.downloadWST();
         }
         else  if (facet.equals("csv")) {
+            url += "&encoding=windows-1252";
             imgUrl += MSG.downloadCSV();
         }
         else {
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/GaugeTimeRangePanel.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/GaugeTimeRangePanel.java	Fri Sep 27 17:36:50 2013 +0200
@@ -105,7 +105,8 @@
 
         Config config = Config.getInstance();
         String url = config.getServerUrl();
-        yearTable.setDataSource(new DischargeInfoDataSource(url, gauge));
+        String river = artifact.getArtifactDescription().getRiver();
+        yearTable.setDataSource(new DischargeInfoDataSource(url, gauge, river));
 
         helperContainer.addMember(createHelperPanel());
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/LocationDistancePanel.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/LocationDistancePanel.java	Fri Sep 27 17:36:50 2013 +0200
@@ -963,17 +963,19 @@
         double step = getStep();
 
         if (from < min || from > max) {
-            String tmp = MESSAGES.error_validate_lower_range();
+            String tmp = MESSAGES.error_validate_range();
             tmp = tmp.replace("$1", nf.format(from));
             tmp = tmp.replace("$2", nf.format(min));
+            tmp = tmp.replace("$3", nf.format(max));
             errors.add(tmp);
             from = min;
         }
 
         if (to < min || to > max) {
-            String tmp = MESSAGES.error_validate_upper_range();
+            String tmp = MESSAGES.error_validate_range();
             tmp = tmp.replace("$1", nf.format(to));
-            tmp = tmp.replace("$2", nf.format(max));
+            tmp = tmp.replace("$2", nf.format(min));
+            tmp = tmp.replace("$3", nf.format(max));
             errors.add(tmp);
             to = max;
         }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/RiverInfoPanel.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/RiverInfoPanel.java	Fri Sep 27 17:36:50 2013 +0200
@@ -102,8 +102,6 @@
         DynamicForm infoLink = WikiLinks.linkDynamicForm(this.flys, wikiBaseUrl + url,
                                         MSG.gauge_river_info_link());
         infoLink.setTop(5);
-        LinkItem item = (LinkItem)infoLink.getField("saml");
-        item.setTextBoxStyle("font-size: large;");
         add(infoLink);
     }
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WikiLinks.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/WikiLinks.java	Fri Sep 27 17:36:50 2013 +0200
@@ -50,31 +50,32 @@
 
         if (currentUser != null) {
             String saml = currentUser.getSamlXMLBase64();
-            final DynamicForm form = new DynamicForm();
-            form.setMethod(FormMethod.POST);
-            form.setTarget("_blank");
-            form.setAction(quotedUrl);
-            form.setCanSubmit(true);
-            LinkItem item = new LinkItem("saml");
-            item.setShowTitle(false);
-            item.setLinkTitle(quotedText);
-            item.setValue(SafeHtmlUtils.htmlEscape(saml));
-            item.addClickHandler(new ClickHandler() {
-                @Override
-                public void onClick(ClickEvent event) {
-                    form.submitForm();
-                }
-            });
-            form.setFields(item);
-            return form;
+            if (saml != null) {
+                final DynamicForm form = new DynamicForm();
+                form.setMethod(FormMethod.POST);
+                form.setTarget("_blank");
+                form.setAction(quotedUrl);
+                form.setCanSubmit(true);
+                LinkItem item = new LinkItem("saml");
+                item.setTextBoxStyle("font-size: large;");
+                item.setShowTitle(false);
+                item.setLinkTitle(quotedText);
+                item.setValue(SafeHtmlUtils.htmlEscape(saml));
+                item.addClickHandler(new ClickHandler() {
+                    @Override
+                    public void onClick(ClickEvent event) {
+                        form.submitForm();
+                    }
+                });
+                form.setFields(item);
+                return form;
+            }
         }
-        else {
-            DynamicForm form = new DynamicForm();
-            LinkItem item = new LinkItem(quotedText);
-            item.setShowTitle(false);
-            item.setTarget(quotedUrl);
-            return form;
-        }
+        DynamicForm form = new DynamicForm();
+        LinkItem item = new LinkItem(quotedText);
+        item.setShowTitle(false);
+        item.setTarget(quotedUrl);
+        return form;
     }
 
     public static DynamicForm dynamicForm(FLYS flys, String url) {
@@ -83,26 +84,26 @@
 
         if (currentUser != null) {
             String saml = currentUser.getSamlXMLBase64();
-            saml = SafeHtmlUtils.htmlEscape(saml);
-            GWT.log("saml=" + saml);
-            DynamicForm form = new DynamicForm();
-            form.setID("wikiDynamicForm");
-            form.setMethod(FormMethod.POST);
-            form.setTarget("_blank");
-            form.setAction(quotedUrl);
-            form.setCanSubmit(true);
-            HiddenItem item = new HiddenItem("saml");
-            item.setDefaultValue(saml);
-            item.setValue(saml);
-            form.setFields(item);
-            //form.setValue("saml", saml);
-            return form;
+            if (saml != null) {
+                saml = SafeHtmlUtils.htmlEscape(saml);
+                GWT.log("saml=" + saml);
+                DynamicForm form = new DynamicForm();
+                form.setID("wikiDynamicForm");
+                form.setMethod(FormMethod.POST);
+                form.setTarget("_blank");
+                form.setAction(quotedUrl);
+                form.setCanSubmit(true);
+                HiddenItem item = new HiddenItem("saml");
+                item.setDefaultValue(saml);
+                item.setValue(saml);
+                form.setFields(item);
+                //form.setValue("saml", saml);
+                return form;
+            }
         }
-        else {
-            DynamicForm form = new DynamicForm();
-            form.setTarget("_blank");
-            form.setAction(quotedUrl);
-            return form;
-        }
+        DynamicForm form = new DynamicForm();
+        form.setTarget("_blank");
+        form.setAction(quotedUrl);
+        return form;
     }
 }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartOutputTab.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartOutputTab.java	Fri Sep 27 17:36:50 2013 +0200
@@ -652,6 +652,18 @@
     }
 
 
+    /** Get link to export image in given dimension, format and encoding. */
+    public String getExportUrl(
+        int width, int height, String format, String encoding) {
+        String url = getImgUrl(width, height);
+        url += "&format=" + format;
+        url += "&export=true";
+        url += "&encoding=" + encoding;
+
+        return url;
+    }
+
+
     /** Get link to export image in given dimension and format. */
     public String getExportUrl(int width, int height, String format) {
         String url = getImgUrl(width, height);
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartToolbar.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartToolbar.java	Fri Sep 27 17:36:50 2013 +0200
@@ -156,7 +156,7 @@
 
         downloadCSV = new ImgLink(
             baseUrl + MSG.downloadCSV(),
-            chartTab.getExportUrl(-1, -1, "csv"),
+            chartTab.getExportUrl(-1, -1, "csv", "windows-1252"),
             20,
             20);
         downloadCSV.setTooltip(MSG.downloadCSVTooltip());
--- a/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/range/DischargeInfoDataSource.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/client/ui/range/DischargeInfoDataSource.java	Fri Sep 27 17:36:50 2013 +0200
@@ -20,7 +20,7 @@
     public static final String XPATH_DISCHARGE_DEFAULT = "/discharges/discharge";
 
 
-    public DischargeInfoDataSource(String url, long gauge) {
+    public DischargeInfoDataSource(String url, long gauge, String river) {
         setDataFormat(DSDataFormat.XML);
         setRecordXPath(XPATH_DISCHARGE_DEFAULT);
 
@@ -37,15 +37,16 @@
             "end", FieldType.TEXT, "end");
 
         setFields(desc, bfgid, start, end);
-        setDataURL(getServiceURL(url, gauge));
+        setDataURL(getServiceURL(url, gauge, river));
     }
 
 
-    protected String getServiceURL(String server, long gauge) {
+    protected String getServiceURL(String server, long gauge, String river) {
         String url = GWT.getModuleBaseURL();
         url += "dischargeinfoxml";
         url += "?server=" + server;
         url += "&gauge=" + String.valueOf(gauge);
+        url += "&river=" + river;
 
         return url;
     }
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/CSVExportServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/CSVExportServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -77,7 +77,7 @@
         try {
             InputStream in = client.collectionOut(requestDoc, uuid, "export");
             Reader reader       = new InputStreamReader (in, "UTF-8");
-            CSVReader csvReader = new CSVReader (reader);
+            CSVReader csvReader = new CSVReader (reader, ';');
 
             List<String[]> lines = new ArrayList<String[]>();
             String[]       line  = null;
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/ChartOutputServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/ChartOutputServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -29,8 +29,10 @@
 
 /**
  * This service is used to request a chart from the artifact server. The
- * response is directed directly to the output stream, so the image that is
- * retrieved is displayed in the UI afterwards.
+ * response is directed directly to the output stream. This can be used
+ * to stream an image that is displayed in the UI afterwards.
+ *
+ * Note that a chart output can also be a csv file.
  *
  * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
  */
@@ -41,6 +43,7 @@
         Logger.getLogger(ChartOutputServiceImpl.class);
 
 
+    /** Handle a get, collectionOut. */
     public void doGet(HttpServletRequest req, HttpServletResponse resp) {
         logger.info("ChartOutputServiceImpl.doGet");
 
@@ -49,9 +52,10 @@
 
             String url = getServletContext().getInitParameter("server-url");
 
-            String uuid   = req.getParameter("uuid");
-            String type   = req.getParameter("type");
-            String locale = req.getParameter("locale");
+            String uuid     = req.getParameter("uuid");
+            String type     = req.getParameter("type");
+            String locale   = req.getParameter("locale");
+            String encoding = req.getParameter("encoding");
 
             prepareHeader(req, resp);
 
@@ -60,6 +64,16 @@
                 ChartServiceHelper.getChartAttributes(prepareChartAttributes(req)));
 
             HttpClient client = new HttpClientImpl(url, locale);
+
+            if (encoding != null) {
+                try {
+                client.setOutEncoding(
+                    java.nio.charset.Charset.forName(encoding));
+                }
+                catch(java.nio.charset.UnsupportedCharsetException e) {
+                    logger.warn("Unsupported encoding: " + encoding);
+                }
+            }
             client.collectionOut(request, uuid, "chart", out);
 
             out.close();
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/DischargeInfoServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/DischargeInfoServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -48,9 +48,11 @@
     public static final String XPATH_DISTANCES = "art:discharges/art:discharge";
 
 
+    @Override
     public DischargeInfoObject[] getDischargeInfo(
         String locale,
-        long gauge)
+        long gauge,
+        String river)
     throws ServerException
     {
         logger.info("DichargeInfoServiceImpl.getDischargeInfo");
@@ -67,6 +69,10 @@
         Element gaugeEl = ec.create("gauge");
         gaugeEl.setTextContent(String.valueOf(gauge));
 
+        Element riverEl = ec.create("river");
+        riverEl.setTextContent(river);
+
+        gaugeEl.appendChild(riverEl);
         doc.appendChild(gaugeEl);
 
         HttpClient client = new HttpClientImpl(url, locale);
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/DischargeInfoXML.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/DischargeInfoXML.java	Fri Sep 27 17:36:50 2013 +0200
@@ -50,6 +50,8 @@
 
         String gauge = req.getParameter("gauge");
 
+        String river = req.getParameter("river");
+
         Document doc = XMLUtils.newDocument();
 
         XMLUtils.ElementCreator ec = new XMLUtils.ElementCreator(
@@ -60,6 +62,12 @@
         Element gaugeEl = ec.create("gauge");
         gaugeEl.setTextContent(gauge);
 
+        if (river != null && !river.isEmpty()) {
+            Element riverEl = ec.create("river");
+            riverEl.setTextContent(river);
+            gaugeEl.appendChild(riverEl);
+        }
+
         doc.appendChild(gaugeEl);
 
         HttpClient client = new HttpClientImpl(url);
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/ExportServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/ExportServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -55,6 +55,7 @@
             String locale = req.getParameter("locale");
             String km     = req.getParameter("km");
             String fn     = name + "." + type;
+            String enc    = req.getParameter("encoding");
 
             resp.setHeader("Content-Disposition", "attachment;filename=" + fn);
 
@@ -74,6 +75,18 @@
             Document request = ClientProtocolUtils.newOutCollectionDocument(
                 uuid, mode, type, attr);
             HttpClient client = new HttpClientImpl(url, locale);
+
+            // Set out encoding if specified.
+            if (enc != null) {
+                try {
+                client.setOutEncoding(
+                    java.nio.charset.Charset.forName(enc));
+                }
+                catch(java.nio.charset.UnsupportedCharsetException e) {
+                    logger.warn("Unsupported encoding: " + enc);
+                }
+            }
+
             client.collectionOut(request, uuid, mode, out);
 
             out.close();
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/FixingsOverviewServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/FixingsOverviewServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -33,8 +33,6 @@
 import java.util.ArrayList;
 import java.util.List;
 
-import javax.xml.xpath.XPathConstants;
-
 import org.apache.log4j.Logger;
 
 import org.w3c.dom.Document;
@@ -58,9 +56,6 @@
     protected static final String XPATH_RFROM = "/fixings/river/@from";
     protected static final String XPATH_RTO = "/fixings/river/@to";
 
-    protected static final String XPATH_EVENT = "/fixings/events/event";
-
-
     @Override
     public FixingsOverviewInfo generateOverview(
         String  locale,
@@ -193,18 +188,16 @@
     protected List<FixEvent> getFixEvents(Document doc) {
         List<FixEvent> list = new ArrayList<FixEvent>();
 
-        NodeList events = (NodeList) XMLUtils.xpath(
-            doc,
-            XPATH_EVENT,
-            XPathConstants.NODESET,
-            null);
+        NodeList events = doc.getElementsByTagName("event");
 
-        if (events == null || events.getLength() == 0) {
+        int E = events.getLength();
+
+        if (E == 0) {
             log.warn("No events in Overview!");
             return list;
         }
 
-        for (int i = 0, E = events.getLength(); i < E; i++) {
+        for (int i = 0; i < E; i++) {
             Element n = (Element)events.item(i);
             List<Sector> sectors = getSectors(n);
             String cid  = n.getAttribute("cid");
@@ -218,13 +211,15 @@
     protected List<Sector> getSectors(Element event) {
         NodeList sectors = event.getElementsByTagName("sector");
 
-        if (sectors.getLength() == 0) {
+        int S = sectors.getLength();
+
+        if (S == 0) {
             log.warn("No Sectors in Event!");
             return null;
         }
 
-        List<Sector> list = new ArrayList<Sector>();
-        for (int i = 0, S = sectors.getLength(); i < S; i++) {
+        List<Sector> list = new ArrayList<Sector>(S);
+        for (int i = 0; i < S; i++) {
             Element n = (Element)sectors.item(i);
             int    cls  = -1;
             double from = -1;
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/MapPrintServiceImpl.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/MapPrintServiceImpl.java	Fri Sep 27 17:36:50 2013 +0200
@@ -427,6 +427,7 @@
         //        Currently this is not a problem because /flys/map-print
         //        is whitelisted in GGInAFilter.
         GetMethod get = new GetMethod(url);
+        get.addRequestHeader("X_NO_GGINA_AUTH", "TRUE");
         int result = client.executeMethod(get);
         InputStream in = get.getResponseBodyAsStream();
 
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/RemoteServiceServlet.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/RemoteServiceServlet.java	Fri Sep 27 17:36:50 2013 +0200
@@ -16,7 +16,7 @@
 extends      com.google.gwt.user.server.rpc.RemoteServiceServlet
 {
     /**
-     * Return the current logged in user from the HTTP Session
+     * Return the current logged in user from the HTTP Session.
      */
     public User getUser() {
         HttpSession session = this.getThreadLocalRequest().getSession();
--- a/gwt-client/src/main/java/org/dive4elements/river/client/server/filter/GGInAFilter.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/server/filter/GGInAFilter.java	Fri Sep 27 17:36:50 2013 +0200
@@ -100,9 +100,12 @@
 
         // Allow access to localhost
         if (isLocalAddress(req)) {
-            logger.debug("Request to localhost");
-            chain.doFilter(req, resp);
-            return;
+            String noAuth = sreq.getHeader("X_NO_GGINA_AUTH");
+            if (noAuth != null && noAuth.equals("TRUE")) {
+                logger.debug("Request to localhost");
+                chain.doFilter(req, resp);
+                return;
+            }
         }
 
         // Allow access to login pages
--- a/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/FixingsOverviewInfo.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/FixingsOverviewInfo.java	Fri Sep 27 17:36:50 2013 +0200
@@ -61,9 +61,9 @@
     }
 
     public FixEvent getEventByCId(String cid) {
-        for (int i = 0; i < events.size(); i++) {
-            if (events.get(i).getCId().equals(cid)) {
-                return events.get(i);
+        for (FixEvent event: events) {
+            if (event.getCId().equals(cid)) {
+                return event;
             }
         }
         return null;
--- a/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/ToLoad.java	Fri Sep 13 18:29:01 2013 +0200
+++ b/gwt-client/src/main/java/org/dive4elements/river/client/shared/model/ToLoad.java	Fri Sep 27 17:36:50 2013 +0200
@@ -21,26 +21,34 @@
 {
 
     /** Two strings. */
-    public class StringPair {
+    public static class StringTriple {
         public String first;
         public String second;
-        public StringPair(String first, String second) {
+        public String third;
+        public StringTriple(String first, String second, String third) {
             this.first = first;
             this.second = second;
+            this.third = third;
         }
+        @Override
         public int hashCode() {
-            return first.hashCode() + second.hashCode();
+            return first.hashCode() + second.hashCode() + third.hashCode();
         }
-        public boolean equals(StringPair other) {
-            return (this.second.equals(other.second)) && (this.first.equals(other.first));
+        @Override
+        public boolean equals(Object o) {
+            if (!(o instanceof StringTriple)) {
+                return false;
+            }
+            StringTriple other = (StringTriple) o;
+            return second.equals(other.second) && first.equals(other.first) && third.equals(other.third);
         }
     }
     public static final String SYNTHETIC_KEY = "key-";
 
-    protected Map<String, Map<StringPair, ArtifactFilter>> artifacts;
+    protected Map<String, Map<StringTriple, ArtifactFilter>> artifacts;
 
     public ToLoad() {
-        artifacts = new HashMap<String, Map<StringPair, ArtifactFilter>>();
+        artifacts = new HashMap<String, Map<StringTriple, ArtifactFilter>>();
     }
 
     public static final String uniqueKey(Map<?, ?> map) {
@@ -81,17 +89,17 @@
             artifactName = uniqueKey(artifacts);
         }
 
-        Map<StringPair, ArtifactFilter> artifact = artifacts.get(artifactName);
+        Map<StringTriple, ArtifactFilter> artifact = artifacts.get(artifactName);
 
         if (artifact == null) {
-            artifact = new HashMap<StringPair, ArtifactFilter>();
+            artifact = new HashMap<StringTriple, ArtifactFilter>();
             artifacts.put(artifactName, artifact);
         }
 
         ArtifactFilter filter = artifact.get(factory);
         if (filter == null) {
             filter = new ArtifactFilter(factory);
-            artifact.put(new StringPair(factory, displayName), filter);
+            artifact.put(new StringTriple(factory, displayName, targetOut), filter);
         }
 
         filter.add(out, name, ids);
@@ -104,7 +112,7 @@
     public List<Recommendation> toRecommendations() {
         List<Recommendation> recommendations = new ArrayList<Recommendation>();
 
-        for (Map.Entry<String, Map<StringPair, ArtifactFilter>> all:
+        for (Map.Entry<String, Map<StringTriple, ArtifactFilter>> all:
             artifacts.entrySet()
         ) {
             String masterArtifact = all.getKey();
@@ -113,11 +121,12 @@
                 masterArtifact = null;
             }
 
-            for (Map.Entry<StringPair, ArtifactFilter> entry:
+            for (Map.Entry<StringTriple, ArtifactFilter> entry:
                 all.getValue().entrySet()
             ) {
-                StringPair pair = entry.getKey();
-                String factory = pair.first;
+                StringTriple triple = entry.getKey();
+                String factory = triple.first;
+                String targetOut = triple.third;
                 ArtifactFilter artifactFilter = entry.getValue();
 
                 String                ids;
@@ -133,8 +142,8 @@
                 }
 
                 Recommendation recommendation = new Recommendation(
-                    factory, ids, masterArtifact, filter);
-                recommendation.setDisplayName(pair.second);
+                    factory, ids, masterArtifact, filter, targetOut);
+                recommendation.setDisplayName(triple.second);
 
                 recommendations.add(recommendation);
             }

http://dive4elements.wald.intevation.org